Java設計模式之原型模式

在創建一個對象的時候,通常會使用到類中的構造方法,也就是說,對象是從類中創建的。但是在某些特殊情況下,是不允許或不希望直接調用構造方法,即不通過類來創建對象,怎麼辦呢?我們就可以通過已經創建好的對象“克隆”出新的對象,即不通過類來創建實例,而通過實例來創建實例,這就是原型模式。
原型模式使用的場景如下:

  • 類初始化消耗資源較多,構造方法比較複雜;
  • 需要在循環體中大量創建對象。

所有的類都繼承了Object類,而Object類中默認提供了clone方法,通過實現Cloneable接口並重寫clone方法即可實現對象克隆。在使用原型模式時,根據其成員變量是否也需要克隆,原型模式又分爲:淺拷貝和深拷貝。
代碼示例如下:

淺克隆

public class Student implements Cloneable, Serializable {

    private int age;
    private Date birthday;

    public Student(int age, Date birthday) {
        this.age = age;
        this.birthday = birthday;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Student student = (Student) super.clone();
        return student;
    }
}
public class Test {

    public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
        Student s1 = new Student(1, new Date());
        Student s2 = (Student) s1.clone();
        System.out.println(s1 == s2);
        System.out.println(s1.getBirthday() == s2.getBirthday());
    }
}
false
true

可以看出,s1和s2的引用地址不同,所以s2是克隆出來的新對象,但爲什麼說這種方式是淺克隆呢?因爲如果Student的成員變量中含有引用對象類型的話,它的成員變量的地址引用還是指向原來的對象,所以是s1和s2的birthday屬性的引用地址是相同的。如果修改了s1的birthday屬性,就會同時修改s1的birthday屬性。所以,如果需要克隆一個全新的對象,我們就需要使用深克隆,把對應的引用對象也相應克隆一份。

深克隆

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Student student = (Student) super.clone();
        student.birthday = (Date) birthday.clone();
        return student;
    }

我們只需要在clone方法中把對應的birthday屬性克隆一份,就可以實現深克隆。

序列化和反序列化實現深克隆

除了把對應的引用類型的成員變量進行克隆這一方法外,我們還可以使用序列化和反序列化的方法實現深克隆,代碼如下:

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Student student = (Student) super.clone();
        Student cloneStudent = null;
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(student);
            byte[] bytes = baos.toByteArray();
            ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
            ObjectInputStream ois = new ObjectInputStream(bais);
            cloneStudent = (Student) ois.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return cloneStudent;
    }

使用序列化和反序列化可以很方便的實現深克隆,而且不用關心引用對象嵌套的問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章