java设计模式学习笔记--原型模式(浅克隆和深克隆)

1. 什么是原型模式

原型模式属于对象的创建模式。。原型模式允许你通过复制现有的实例来创建新的实例。
这个模式的重点在于,客户端的代码在不知道要实例化何种特定类的情况下,可以制造出新的实例。在java中,一般使用clone()的方法,或者序列化。

2. 原型模式的实现

在java中,由于Object类中有一个clone()方法,所以要使用原型模式非常简单,只要实现Cloneable的接口就可以了。
Java语言提供的Cloneable接口只起一个作用,就是在运行时期通知Java虚拟机可以安全地在这个类上使用clone()方法。通过调用这个clone()方法可以得到一个对象的复制。由于Object类本身并不实现Cloneable接口,因此如果所考虑的类没有实现Cloneable接口时,调用clone()方法会抛出CloneNotSupportedException异常。
具体代码如下:

public class Student implements Cloneable ,Serializable{

    private String name;
    private transient int age;
    private Book book;

    //浅克隆
    public Student clone(){
        Student stu = null;
        try {
            stu = (Student) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return stu;
    }

    //深克隆
    public Student deepClone() throws Exception {
        Student stu = null;
        ByteArrayOutputStream bo = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bo);
        oos.writeObject(this);
        ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
        ObjectInputStream oi = new ObjectInputStream(bi);
        stu = (Student) oi.readObject();
        return stu;
    }


    public Student(String name, int age, Book book) {
        this.name = name;
        this.age = age;
        this.book = book;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

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

    public Book getBook() {
        return book;
    }

    public void setBook(Book book) {
        this.book = book;
    }
}
public class Book implements Serializable{

    private String name;

    public Book(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
public class Client {
    public static void main(String[] args){
        Book book = new Book("Chinese");
        Student s = new Student("Tom",12,book);
        Student s1 = s.clone();

        System.out.println(s==s1);
        System.out.println(s.getClass()==s1.getClass());
        System.out.println(s.getBook()==s1.getBook());

        System.out.println("------------------------");

        try {
            Student s2 = s.deepClone();

            System.out.println(s==s2);
            System.out.println(s.getClass()==s2.getClass());
            System.out.println(s.getBook()==s2.getBook());

            System.out.println("-------------------");
            System.out.println("s.age:"+s.getAge()+" s2.age:"+s2.getAge());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

运行结果如下:
java设计模式学习笔记--原型模式(浅克隆和深克隆)_第1张图片

在例子中可以看到,使用Clone()方法,我们可以很容易的复制一个新的实例。
但是,这样的复制,使用的是浅克隆,那么什么是浅克隆哪?

3. 浅克隆与深克隆

浅克隆:
只负责克隆按值传递的数据(比如基本数据类型、String类型),而不复制它所引用的对象,换言之,所有的对其他对象的引用都仍然指向原来的对象。
也就是说如果被克隆的对象中,有对其他对象的引用,那么就只复制那个对象的引用,而不是重新复制一个新的对象。
在上面的例子中,Student对象中具有对Book的引用,那么在使用浅克隆时(使用Clone()方法),复制的s1的book和原来的book是同一个对象,所以s.getBook() ==s1.getBook() 返回 true。

深克隆:
除了浅度克隆要克隆的值外,还负责克隆引用类型的数据。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深度克隆把要复制的对象所引用的对象都复制了一遍,而这种对被引用到的对象的复制叫做间接复制。
深克隆的方法有,反序列化,重写Clone()方法等。在上述例子中,就使用了序列化和反序列化的手段。由此,s.getBook() ==s1.getBook() 返回 false。

利用序列化实现深度克隆
把对象写到流里的过程是序列化(Serialization)过程;而把对象从流中读出来的过程则叫反序列化(Deserialization)过程。应当指出的是,写到流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。
在序列化一个对象时,应该先实现Serializable接口,然后把对象(实际是对象的拷贝)写到流里面,然后再从流里面反序列化出来,这样就重新创建了一个对象。
使用这个方法有一个前提,那就是要克隆的对象所引用的所有对象都应该是可序列化的,那么如果遇到不可序列化的对象时应该怎么办哪?
这时就要用到transient关键字了(注意:transient只能修饰变量),被transient修饰的变量在序列化时不会被序列化。在上述的例子中,Student的age变量使用了transient关键字修饰,所以我们可以看到s.getAge()的值为12,而s2.getAge()的值为0(int类型默认值为0)。

4.原型模式的优缺点

优点:
1、向客户隐藏制造新实例的复杂性。
2、提供让客户能够产生未知类型对象的选项。
3、在某些环境下,复制对象比创建对象更加有效。

缺点:
1、对象的复制有时相当的复杂。特别是当一个类引用不支持序列化的间接对象,或者引用含有循环结构的时候。

用途:
1、在一个复杂的类层次中,当系统必须从其中的许多类型创建新对象时,可以考虑原型

你可能感兴趣的:(java设计模式)