一、应用场景
假设我们要创建一个非常复杂的对象,光是组装对象就写了一上午,之后还得多次创建属性基本相同的实例,怎么办?
原型模式告诉我们,直接从内存二进制流进行对象拷贝就好啦,并且jdk有现成的实现,还是在Object类中实现的。
二、模式结构
因为Object类帮我们实现好了,所以我们只需要重写一下clone方法,扩大访问权限即可。
三、源码分析
public class Prototype implements Cloneable {
private String name;
private List<String> list = new ArrayList<>();
public Prototype(String name, List<String> list) {
super();
System.out.println("构造函数调用");
this.name = name;
this.list = list;
}
@Override
public Prototype clone() throws CloneNotSupportedException {
return (Prototype) super.clone();
}
public String getName() {
return name;
}
public List<String> getList() {
return list;
}
}
public class Client {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("ABC");
Prototype prototype = new Prototype("小明", list);
Prototype prototypeClone = null;
try {
prototypeClone = prototype.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
System.out.println("prototype: name =" + prototype.getName());
System.out.println("prototypeClone: name =" + prototypeClone.getName());
System.out.println("prototype: list =" + prototype.getList());
System.out.println("prototypeClone: list =" + prototypeClone.getList());
System.out.println("prototype: hash =" + prototype.hashCode());
System.out.println("prototypeClone: hash =" + prototypeClone.hashCode());
}
}
执行结果:
可以看出:
1. clone可以完全拷贝对象属性
2. 产生的新对象引用地址与原对象不同
3. clone过程不会调用构造方法。
四、深、浅拷贝
Object中的clone方法是浅拷贝实现:对于成员变量都只进行值拷贝。对于引用类型来说,拷贝的就是引用地址,因而拷贝出来的对象仍然指向相同的地址。与调用方法时传递引用对象,在方法内产生一个与该引用类型有相同引用地址的形参变量原理一样。
而深拷贝对于引用变量进行单独拷贝,如此拷贝生产的引用变量所引用的地址不再相同,但是如果该引用变量内部还存在引用变量,那么拷贝出来之后,内部的这些引用变量将会指向相同地址,原理同上。
public class Student { private int age; private String name; public Student(int age, String name) { super(); this.age = age; this.name = name; } @Override public String toString() { return "Student [age=" + age + ", name=" + name + "]"; } }
public class Prototype implements Cloneable { private String name; private ArrayList<Student> list = new ArrayList<>(); public Prototype(String name, ArrayList<Student> list) { super(); System.out.println("构造函数调用"); this.name = name; this.list = list; } @Override public Prototype clone() throws CloneNotSupportedException { Prototype prototype = (Prototype) super.clone(); prototype.list = (ArrayList<Student>) this.getList().clone(); return prototype; } public String getName() { return name; } public ArrayList<Student> getList() { return list; } }
public class Client { public static void main(String[] args) { ArrayList<Student> list = new ArrayList<>(); list.add(new Student(0, "小李")); Prototype prototype = new Prototype("prototype", list); Prototype prototypeClone = null; try { prototypeClone = prototype.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } // 对于String引用对象,拷贝的是引用地址,所以clone前后该属性仍指向同一地址 System.out.println("prototype: name =" + prototype.getName()); System.out.println("prototypeClone: name =" + prototypeClone.getName()); System.out.println("prototype.name == prototypeClone.name :" + (prototype.getName() == prototypeClone.getName())); System.out.println(); // 对于list进行深拷贝,即单独clone,因而clone前后不再指向相同地址 System.out.println("prototype: list =" + prototype.getList()); System.out.println("prototypeClone: list =" + prototypeClone.getList()); System.out.println("prototype.list == prototypeClone.list :" + (prototype.getList() == prototypeClone.getList())); System.out.println(); //对于list中的Student引用对象,仍做的是浅拷贝,所以clone前后指向同一地址 System.out.println("prototype: Student =" + prototype.getList().get(0)); System.out.println("prototypeClone: Student =" + prototypeClone.getList().get(0)); System.out.println("prototype.Student == prototypeClone.Student :" + (prototype.getList().get(0) == prototypeClone.getList().get(0))); } }
运行结果:
由此可以看出:
1. 要达到完全深拷贝,需要对其中的引用变量递归深拷贝才行。
2. 被final修饰的成员变量无法做深拷贝。