目录
1. 定义
通过原型对象实例高效地克隆出新对象实例,新对象实例拥有全部或部分和原型对象实例相同的属性。
原型模式有以下角色:
Prototype:接口,定义了克隆方法。
ConcretePrototype:实现Prototype接口的原型对象,拥有克隆能力。
克隆有以下分类:
浅克隆:克隆得到的新对象实例的基本数据类型的属性和原型对象实例的基础数据类型的属性值完全一样,且互不干扰;但是,引用类型的属性和原型对象实例的引用类型的属性具有相同的指向。
深克隆:克隆得到的新对象实例的基本数据类型的属性、引用类型的属性,和原型对象实例的基础数据类型的属性值、引用类型的属性值完全一样,且互不干扰。
2. 使用场景
基于已有的原型对象实例,创建拥有相同属性值的新对象。
3. 实现方式
3.1 通用的原型模式
interface IClone {
IClone clone();
}
class Achievement {
private int chinese;
private int math;
public Achievement(int chinese, int math) {
this.chinese = chinese;
this.math = math;
}
public int getChinese() {
return chinese;
}
public void setChinese(int chinese) {
this.chinese = chinese;
}
public int getMath() {
return math;
}
public void setMath(int math) {
this.math = math;
}
@Override
public String toString() {
return "Achievement{" +
"chinese=" + chinese +
", math=" + math +
'}';
}
}
class Student implements IClone {
private String name;
private int age;
private Achievement achievement;
public Student(String name, int age, Achievement achievement) {
this.name = name;
this.age = age;
this.achievement = achievement;
}
@Override
public IClone clone() {
// 直接使用原型对象实例的achievement赋值给克隆得到的新对象实例,是浅克隆
return new Student(this.name, this.age, this.achievement);
// 通过原型对象实例的achievement的属性值新建Achievement对象,再将Achievement对象赋值给克隆得到的新对象实例的achievement属性,就是深克隆
// return new Student(this.name, this.age, new Achievement(this.achievement.getChinese(), this.achievement.getMath()));
}
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 Achievement getAchievement() {
return achievement;
}
public void setAchievement(Achievement achievement) {
this.achievement = achievement;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", achievement=" + achievement +
'}';
}
}
public class Test{
public static void main(String[] args) {
Student student1 = new Student("AAA", 20, new Achievement(90, 90));
Student student2 = (Student) student1.clone();
System.out.println(student1); // Student{name='AAA', age=20, achievement=Achievement{chinese=90, math=90}}
System.out.println(student2); // Student{name='AAA', age=20, achievement=Achievement{chinese=90, math=90}}
System.out.println(student1.getAchievement() == student2.getAchievement()); // true
student2.setName("BBB");
student2.setAge(22);
// 浅克隆,修改克隆得到的新对象实例的引用类型属性achievement,会同步修改原型实例对象的引用类型属性achievement
student2.getAchievement().setChinese(100);
student2.getAchievement().setMath(100);
System.out.println(student1); // Student{name='AAA', age=20, achievement=Achievement{chinese=100, math=100}}
System.out.println(student2); // Student{name='BBB', age=22, achievement=Achievement{chinese=100, math=100}}
}
}
3.2 基于Java实现原型模式
3.2.1 基于Cloneable接口实现原型模式
JDK提供了克隆标记接口Cloneable(无抽象方法),某类实现Cloneable接口且重写Object类的clone方法,此类便具有克隆能力。
3.2.1.1 浅克隆
class Achievement {
private int chinese;
private int math;
public Achievement(int chinese, int math) {
this.chinese = chinese;
this.math = math;
}
public int getChinese() {
return chinese;
}
public void setChinese(int chinese) {
this.chinese = chinese;
}
public int getMath() {
return math;
}
public void setMath(int math) {
this.math = math;
}
@Override
public String toString() {
return "Achievement{" +
"chinese=" + chinese +
", math=" + math +
'}';
}
}
class Student implements Cloneable {
private String name;
private int age;
private Achievement achievement;
public Student(String name, int age, Achievement achievement) {
this.name = name;
this.age = age;
this.achievement = achievement;
}
@Override
public Object clone() {
Object obj = null;
try {
// 此处是浅克隆,因为未处理引用类型Achievement
obj = super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
return obj;
}
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 Achievement getAchievement() {
return achievement;
}
public void setAchievement(Achievement achievement) {
this.achievement = achievement;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", achievement=" + achievement +
'}';
}
}
public class Test{
public static void main(String[] args) {
Student student1 = new Student("AAA", 20, new Achievement(90, 90));
Student student2 = (Student) student1.clone();
System.out.println(student1); // Student{name='AAA', age=20, achievement=Achievement{chinese=90, math=90}}
System.out.println(student2); // Student{name='AAA', age=20, achievement=Achievement{chinese=90, math=90}}
System.out.println(student1.getAchievement() == student2.getAchievement()); // true
student2.setName("BBB");
student2.setAge(22);
// 浅克隆,修改克隆得到的新对象实例的引用类型属性achievement,会同步修改原型实例对象的引用类型属性achievement
student2.getAchievement().setChinese(100);
student2.getAchievement().setMath(100);
System.out.println(student1); // Student{name='AAA', age=20, achievement=Achievement{chinese=100, math=100}}
System.out.println(student2); // Student{name='BBB', age=22, achievement=Achievement{chinese=100, math=100}}
}
}
3.2.1.2 深克隆
修改上述代码,便可达到Student类的深克隆,修改点如下:
1. Achievement类实现Cloneable接口且重写Object类的clone方法。
2. 在Student类的clone方法中,使用Achievement的clone方法克隆achievement。
class Achievement implements Cloneable {
private int chinese;
private int math;
public Achievement(int chinese, int math) {
this.chinese = chinese;
this.math = math;
}
@Override
protected Object clone() {
Object obj = null;
try {
obj = super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
return obj;
}
public int getChinese() {
return chinese;
}
public void setChinese(int chinese) {
this.chinese = chinese;
}
public int getMath() {
return math;
}
public void setMath(int math) {
this.math = math;
}
@Override
public String toString() {
return "Achievement{" +
"chinese=" + chinese +
", math=" + math +
'}';
}
}
class Student implements Cloneable {
private String name;
private int age;
private Achievement achievement;
public Student(String name, int age, Achievement achievement) {
this.name = name;
this.age = age;
this.achievement = achievement;
}
@Override
public Object clone() {
Student newStudent = null;
try {
// 此处是深克隆
newStudent = (Student) super.clone();
newStudent.setAchievement((Achievement) this.achievement.clone());
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
return newStudent;
}
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 Achievement getAchievement() {
return achievement;
}
public void setAchievement(Achievement achievement) {
this.achievement = achievement;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", achievement=" + achievement +
'}';
}
}
public class Test{
public static void main(String[] args) {
Student student1 = new Student("AAA", 20, new Achievement(90, 90));
Student student2 = (Student) student1.clone();
System.out.println(student1); // Student{name='AAA', age=20, achievement=Achievement{chinese=90, math=90}}
System.out.println(student2); // Student{name='AAA', age=20, achievement=Achievement{chinese=90, math=90}}
System.out.println(student1.getAchievement() == student2.getAchievement()); // false
student2.setName("BBB");
student2.setAge(22);
student2.getAchievement().setChinese(100);
student2.getAchievement().setMath(100);
// 深克隆,克隆得到的新对象实例和原型对象实例互不干扰
System.out.println(student1); // Student{name='AAA', age=20, achievement=Achievement{chinese=90, math=90}}
System.out.println(student2); // Student{name='BBB', age=22, achievement=Achievement{chinese=100, math=100}}
}
}
3.2.2 基于Serializable接口实现原型模式
基于序列化实现的克隆是深克隆,代码如下所示:
class Achievement implements Serializable {
private int chinese;
private int math;
public Achievement(int chinese, int math) {
this.chinese = chinese;
this.math = math;
}
public int getChinese() {
return chinese;
}
public void setChinese(int chinese) {
this.chinese = chinese;
}
public int getMath() {
return math;
}
public void setMath(int math) {
this.math = math;
}
@Override
public String toString() {
return "Achievement{" +
"chinese=" + chinese +
", math=" + math +
'}';
}
}
class Student implements Serializable {
private String name;
private int age;
private Achievement achievement;
public Student(String name, int age, Achievement achievement) {
this.name = name;
this.age = age;
this.achievement = achievement;
}
// 反序列化得到的是新对象,因此序列化/反序列化方式的克隆是深克隆
public Student deepClone() {
try {
// 序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
// 反序列化
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (Student) ois.readObject();
} catch (Exception e) {
return null;
}
}
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 Achievement getAchievement() {
return achievement;
}
public void setAchievement(Achievement achievement) {
this.achievement = achievement;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", achievement=" + achievement +
'}';
}
}
public class Test{
public static void main(String[] args) {
Student student1 = new Student("AAA", 20, new Achievement(90, 90));
Student student2 = student1.deepClone();
System.out.println(student1);
System.out.println(student2);
System.out.println(student1.getAchievement() == student2.getAchievement()); // false
student2.setName("BBB");
student2.setAge(22);
student2.getAchievement().setChinese(100);
student2.getAchievement().setMath(100);
// 深克隆,克隆得到的新对象实例和原型对象实例互不干扰
System.out.println(student1); // Student{name='AAA', age=20, achievement=Achievement{chinese=90, math=90}}
System.out.println(student2); // Student{name='BBB', age=22, achievement=Achievement{chinese=100, math=100}}
}
}