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;
    }

使用序列化和反序列化可以很方便的实现深克隆,而且不用关心引用对象嵌套的问题。

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