引言
在开发过程中,有时会遇到为一个类创建多个实例的情况,这些实例内部成员往往完全相同或有细微的差异,而且实例的创建开销比较大或者需要输入较多参数,如果能通过复制一个已创建的对象实例来重复创建多个相同的对象,这就可以大大减少创建对象的开销,这个时候就需要原型模式。
原型模式: 用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象。
使用场景:
对象的创建非常复杂,可以使用原型模式快捷的创建对象。
性能和安全要求比较高。
浅拷贝: 对于基本数据类型,会直接复制值给拷贝对象;对于引用数据类型,只拷贝对象地址,指向原来对象的地址。
// 基本类型浅拷贝:
int a = 100;
int b = a;
System.out.println(a); // 100
System.out.println(b); // 100
System.out.println(a==b); // true
// 引用数据类型浅拷贝:
// 定义学生类
class Student{
private String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
}
//测试
Student student1 = new Student("stu",18);
System.out.println(student1); // JavaPackage_1.Student@68b0af6
Student student2 = student1;
System.out.println(student2); // JavaPackage_1.Student@68b0af6
System.out.println(student1==student2); // true,student1和student2指向的是同一地址
student1.setName("newstu");
System.out.println(student2.getName()); // newstu,通过修改student1可以修改student2
深拷贝: 无论基本类型还是引用类型,全部拷贝为一个新的对象,属性中引用的其他对象也会被拷贝,不指向原有对象的地址。
// 定义Food类
class Food{
String food;
public String getFood() {
return food;
}
public void setFood(String food) {
this.food = food;
}
public Food(String food) {
this.food = food;
}
}
// 定义Student类
class Student implements Cloneable{ // Cloneable接口
@Override //重写clone()方法
// 提升访问权限为public
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
private Food food;
private String name;
int age;
public Student() {
}
public Student(String name, int age, Food food) {
this.name = name;
this.age = age;
this.food = food;
}
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 void setFood(Food food){
this.food = food;
}
public Food getFood(){
return food;
}
}
//测试
Student student1 = new Student("stu", 29,food);
Student student2 = (Student) student1.clone(); // 通过student1克隆student2,不通过new对象的形式创建
System.out.println(student1); // JavaPackage_1.Student@682a0b20
System.out.println(student2); // JavaPackage_1.Student@3d075dc0
System.out.println(student1==student2); // false,student1和student2指向的是不同地址
System.out.println(student1.getFood()); // JavaPackage_1.Food@58b835a
System.out.println(student2.getFood()); // JavaPackage_1.Food@58b835a
System.out.println(student1.getFood()==student2.getFood()); // true,student1.food和student2.food指向同一地址
food.setFood("orange");
System.out.println(student1.getFood().getFood()); // orange
System.out.println(student2.getFood().getFood()); // orange
这样,使用了clone()方法实现了浅拷贝,由测试结果知道,对象student1和student2中的food仍是指向的同一地址。下面,对上述代码进行改进,实现原型模式的深拷贝。
下面,通过修改clone()方法实现深拷贝:
@Override
public Object clone() throws CloneNotSupportedException { // 提升访问权限
Student student = (Student) super.clone();
//针对成员变量进行拷贝
student.name = new String(name);
student.food = new Food(food.getFood());
return student;
}
//测试
Food food = new Food("apple");
System.out.println(food); // JavaPackage_1.Food@6d311334
//Java深拷贝,对内层的对象也进行拷贝。
Student student1 = new Student("stu", 29,food);
// 通过student1克隆student2,不通过new对象的形式创建
Student student2 = (Student) student1.clone();
System.out.println(student1); // JavaPackage_1.Student@3d075dc0
System.out.println(student2); // JavaPackage_1.Student@214c265e
System.out.println(student1==student2); // false,student1和student2指向的是不同地址
System.out.println(student1.getFood()); // JavaPackage_1.Food@6d311334
System.out.println(student2.getFood()); // JavaPackage_1.Food@448139f0
System.out.println(student1.getFood()==student2.getFood()); // false,student1.food和student2.food指向不同地址
food.setFood("orange");
System.out.println(student1.getFood().getFood()); // orange
System.out.println(student2.getFood().getFood()); // apple
原型模式没有复杂的继承体系,只需要使需要具有拷贝功能的类实现Cloneable接口并重写clone()方法即可。但它的应用却及其广泛,它将对一个对象中各个字段(不管是私有的还是共有的)的复制操作封装在了clone()方法中,这样,使用该类的用户就不需要对对象中的各个字段的细节进行了解,直接调用clone()方法就可以实现对象的拷贝,而且,通过clone()方法还可以为不同的字段设置被复制的权限,从而允许仅对可以被复制的字段进行复制。
参考: