原型模式
原型模式(Prototype Pattern)是指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。重点是对象的拷贝
适用场景
1、类初始化消耗资源较多。
2、new 产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)
3、构造函数比较复杂。
4、循环体中生产大量对象时。
简单克隆(浅克隆)
public interface Prototype{
Prototype clone();
}
创建克隆对象
public class ConcretePrototype implements Prototype {
private int age;
private String name;
private List hobbies;
//get、set方法省略
@Override
public ConcretePrototype clone() {
ConcretePrototype concretePrototype = new ConcretePrototype();
concretePrototype.setAge(this.age);
concretePrototype.setName(this.name);
concretePrototype.setHobbies(this.hobbies);
return concretePrototype;
}
}
public class Client {
private Prototype prototype;
public Client(Prototype prototype){
this.prototype = prototype;
}
public Prototype startClone(Prototype concretePrototype){
return (Prototype)concretePrototype.clone();
}
}
测试
public class PrototypeTest {
public static void main(String[] args) {
// 创建一个具体的需要克隆的对象
ConcretePrototype concretePrototype = new ConcretePrototype();
// 填充属性,方便测试
concretePrototype.setAge(30);
concretePrototype.setName("prototype");
List hobbies = new ArrayList<String>();
concretePrototype.setHobbies(hobbies);
System.out.println(concretePrototype);
// 创建 Client 对象,准备开始克隆
Client client = new Client(concretePrototype);
ConcretePrototype concretePrototypeClone = (ConcretePrototype)
client.startClone(concretePrototype);
System.out.println(concretePrototypeClone);
System.out.println("克隆对象中的引用类型地址值:" + concretePrototypeClone.getHobbies());
System.out.println("原对象中的引用类型地址值:" + concretePrototype.getHobbies());
System.out.println("对象地址比较:"+(concretePrototypeClone.getHobbies() ==
concretePrototype.getHobbies()));
}
}
结论:
浅克隆只是完整复制了值类型数据,没有赋值引用对象。换言之,所有的引用对象仍然指向原来的对象,复制的不是值,而是引用的地址。
深克隆
创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
深浅克隆都会在堆中新分配一块区域,区别在于对象属性引用的对象是否需要进行克隆(递归性的)。
代码演示:
public class Professor implements Cloneable {
private String name;
private Integer age;
public Professor(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Object clone() {
Object o = null;
try {
o = super.clone();
} catch (CloneNotSupportedException e) {
System.out.println(e.toString());
}
return o;
}
}
public class Student implements Cloneable {
private String name;
private Integer age;
private Professor professor;
public Student(String name, Integer age, Professor professor) {
this.name = name;
this.age = age;
this.professor = professor;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Professor getProfessor() {
return professor;
}
public void setProfessor(Professor professor) {
this.professor = professor;
}
public Object clone() {
Student o = null;
try {
o = (Student) super.clone();
} catch (CloneNotSupportedException e) {
System.out.println(e.toString());
}
o.professor = (Professor) professor.clone();
return o;
}
}
测试:
public class DeepCloneT {
public static void main(String[] args) {
Professor p = new Professor("wangwu", 50);
Student s1 = new Student("xiaoming", 20, p);
Student s2 = (Student) s1.clone();
s2.getProfessor().setName("xiaowangwu");
s2.getProfessor().setAge(40);
System.out.println("name=" + s1.getProfessor().getName()
+ "," + "age=" + s1.getProfessor().getAge());
System.out.println("name=" + s2.getProfessor().getName()
+ "," + "age=" + s2.getProfessor().getAge());
}
}
输出结果:
name=wangwu,age=50
name=xiaowangwu,age=40
问题:克隆破坏单例模式?
如果我们克隆的目标的对象是单例对象,那意味着,深克隆就会破坏单例。实际上防止克隆破坏单例解决思路非常简单,禁止深克隆便可。
要么你我们的单例类不实现Cloneable 接口;要么我们重写 clone()方法,在 clone 方法中返回单例对象即可。
具体实现
@Override
protected Object clone() throws CloneNotSupportedException {
return INSTANCE;
}