原型模式简述
先简单的说一下原型模式,原型模式属于创建型模式,和单例模式属于同一个大类,都是用来生成的对象的,与 java 中的 new 不同,原型模式会把一个对象的属性值拷贝一份复制给另外一个对象。而 new 需要传入需要的参数,不如原型模式方便。
示例代码
//需要实现 Cloneable 接口
class Test implements Cloneable{
public int testId;
public String testName;
//需要重写 clone()方法
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return super.clone();
}
@Override
public String toString() {
return "Test [testId=" + testId + ", testName=" + testName + "]";
}
public Test(int testId, String testName) {
this.testId = testId;
this.testName = testName;
}
}
class Main{
public static void main(String[] args) throws Exception {
Test test1 = new Test(123456, "zhangsan");
Test test2 = (Test) test1.clone();
System.out.println(test2.toString());
}
}
//输出:Test [testId=123456, testName=zhangsan]
可以看到,输出的 test2 对象确实拿到了 test1 对象的所有属性
浅拷贝与深拷贝
涉及到原型模式,我们常会提起浅拷贝与深拷贝的问题,什么是浅拷贝呢,拿刚才的代码举例子。我们拷贝了一份第一个对象的属性值给第二个对象。这就引申出了一个问题,假如第一个对象里面的参数包含一个对象呢,比如:
class Test implements Cloneable{
public int testId;
public String testName;
public Teacher teacher;
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return super.clone();
}
public Test(int testId, String testName, Teacher teacher) {
this.testId = testId;
this.testName = testName;
this.teacher = teacher;
}
@Override
public String toString() {
return "Test [teacher=" + teacher + ", testId=" + testId + ", testName=" + testName + "]";
}
}
class Teacher implements Cloneable{
private int teacherId;
private String teacherName;
public Teacher(int teacherId, String teacherName) {
this.teacherId = teacherId;
this.teacherName = teacherName;
}
@Override
public String toString() {
return "Teacher [teacherId=" + teacherId + ", teacherName=" + teacherName + "]";
}
}
class Main{
public static void main(String[] args) throws Exception {
Teacher teacher = new Teacher(000, "lisi");
Test test1 = new Test(123456, "zhangsan", teacher);
Test test2 = (Test) test1.clone();
System.out.println(test2.toString());
}
}
//输出为:Test [teacher=Teacher [teacherId=0, teacherName=lisi],
//testId=123456, testName=zhangsan]
此时我们做一个尝试,我们修改 test1 对象中的 int 型参数 testId 从123456改为777777,我们再次打印两个对象,得到输出:
Test1:
Test [teacher=Teacher [teacherId=0, teacherName=lisi],
testId=123456, testName=zhangsan]
Test2:
Test [teacher=Teacher [teacherId=0, teacherName=lisi],
testId=777777, testName=zhangsan]
可以发现确实是实现了我们之前所说的拷贝:两个对象单独拥有相同的属性。
我们换一种方式,再来实验一下:
这里我们修改 test2 中的 Teacher 类型的对象 teacher 中的 teacherId 参数,看看会发生什么,得到输出:
Test1:
Test [teacher=Teacher [teacherId=111, teacherName=lisi],
testId=123456, testName=zhangsan]
Test2:
Test [teacher=Teacher [teacherId=111, teacherName=lisi],
testId=123456, testName=zhangsan]
咦?这个时候我们就发现了问题,为什么我修改的是 test2 中的 teacherId 的值,结果影响到了 test1 中的 teacherId 的值,这显然和我想要达到的效果不一样啊。这就是浅拷贝(仅仅是把对象中的参数值拷贝,假如有对象中还有对象的话,那么也会把对象的引用作为值拷贝一份)
那么我们怎么实现我们想要的效果,即拷贝对象间的属性修改互不影响呢?
这里提到一个概念:深拷贝:重写 clone()方法,在对象拷贝的时候,对对象中的对象再进行一次拷贝,重写部分代码如下:
@Override
protected Object clone() throws CloneNotSupportedException {
Object obj = super.clone();
Test test = (Test)obj;
this.teacher = (Teacher)test.teacher.clone();
return obj;
}
再次进行测试,此时结果如下:
Test1:
Test [teacher=Teacher [teacherId=0, teacherName=lisi],
testId=123456, testName=zhangsan]
Test2:
Test [teacher=Teacher [teacherId=111, teacherName=lisi],
testId=123456, testName=zhangsan]
可以观察到达到既定效果。
这就是我们常说的深拷贝与浅拷贝的特定实现,但是需要注意的是,在对象当中拥有多个对象的时候,实现深拷贝需要做的任务量是很大的。