原型模式的定义与特点
原型(Prototype)模式的定义如下:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效,根本无须知道对象创建的细节。例如,Windows 操作系统的安装通常较耗时,如果复制就快了很多。在生活中复制的例子非常多,这里不一一列举了。
原型模式的结构与实现
由于 Java 提供了对象的 clone() 方法,所以用 Java 实现原型模式很简单。这里要重点解释的是原型模式的浅拷贝深拷贝的问题。
看了很多博客都没有说清这个问题。
我们先看看引用复制是啥样的。
public class Student implements Cloneable{
private int StuId;
private String StuName;
private int StuAge;
public Student(int stuId, String stuName, int stuAge) {
super();
StuId = stuId;
StuName = stuName;
StuAge = stuAge;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}//深拷贝的重新代码省去,很简单!这里的super.clone调用的是Object的方法,他默认只拷贝基本类型数据。要用深拷贝,再拷贝一份引用类型就OK,如果引用类型过多,建议用序列化的形式实现!
}
public class CloneTest {
public static void main(String[] args) throws CloneNotSupportedException {
Student stu1 = new Student(0,"zhangsan",20);
Student stu2 = stu1;
System.out.println("stu1 = " + stu1);
System.out.println("stu2 = " + stu2);
}
}
运行结果:
stu1 = Student@3c635421
stu2 = Student@3c635421
上图中,stu1和stu2都是指向同一个类!!他们的地址相同。
问题来了,浅拷贝出来的地址相同嘛?
public class CloneTest {
public static void main(String[] args) throws CloneNotSupportedException {
Student stu3 = new Student(1,"lisi",20);
Student stu4 = (Student) stu3.clone();
System.out.println("stu3 = " + stu3);
System.out.println("stu4 = " + stu4);
System.out.println(stu3.StuName==stu4.StuName);
}
}
运行结果:
stu3 = Student@7bc2f501
stu4 = Student@3c635421
true
可以看到,结果不同!这里可以看到Student类中第二个为String引用类型的,复制出的stu3和stu4的String类型的地址相同!
浅拷贝:java有两种数据类型,一种是8种数据基本类型(int ,char等),一种是引用类型(类等)。浅拷贝就是单纯的拷贝了基本的8种类型,而对于引用类型,并没有拷贝出单独的一份,仍然用的是原来引用的地址。
OK,以上解释了深拷贝和引用复制的区别!区别只有一点,引用复制的两个类地址都一样,住一间房子!浅拷贝的住两个房子,但是引用类型的地址都是一样的。来个图:
如果是引用复制,那么栈内存中左边都是在一个房子住!
深拷贝:深拷贝就是在堆内存中,将引用对象复制一份,所以引用类型深拷贝的地址是不相等的,深拷贝就是深度的拷贝,他不止拷贝基本类型,也拷贝引用类型。
要注意的是String!!!
我们再次重复刚刚的代码(要用序列化或者重写clone才能达到深拷贝的效果)这里默认已经重写了clone方法!
String毫无疑问它是引用类型。
public class CloneTest {
public static void main(String[] args) throws CloneNotSupportedException {
Student stu3 = new Student(1,"lisi",20,new Address("安徽","合肥"));//我们这里加上一个Address
Student stu4 = (Student) stu3.clone();
System.out.println("stu3 = " + stu3);
System.out.println("stu4 = " + stu4);
System.out.println(stu3.StuName==stu4.StuName);
System.out.println(stu3.getAddress()==stu4.getAddress());
}
}
运行结果:
stu3 = Student@7bc2f501
stu4 = Student@3c635421
true//这里的结果竟然还是!!!true
false//哦豁,这里的结果错了??为什么叻
答案是String并不是简单的引用类型,它是一个final修饰的引用类型!不可更改,所以还是true!