基本概念
用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效,根本无须知道对象创建的细节。
应用场景举例
1. 创建一个对象成本较高时可考虑选用原型模式,如一个对象比比较复杂,创建过程耗时较长,或者占用CPU较高等;
2. 当程序中需要使用一个对象的大部分信息,而只对部分信息做出修改时,可使用原型模式。
实现
在Java中,原型对象实现Cloneable接口,实现实现clone方法即可实现克隆,但要注意浅克隆与深克隆的区别。
浅克隆
如果原型对象的成员变量是值类型(String、int、double等),调用clone方法之后将值类型的成员变量复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,即克隆对象和原型对象成员变量的引用地址相同。
public class Company implements Cloneable {
private String id;
private String name;
private Department department;
public Company(String id, String name, Department department) {
this.id = id;
this.name = name;
this.department = department;
}
public String getInfo() {
return "{" + id + ", " + name + "}";
}
public Department getDepartment() {
return department;
}
@Override
protected Company clone() throws CloneNotSupportedException {
// 克隆对象
return (Company) super.clone();
}
}
public class Department {
private String id;
private String name;
public String getInfo() {
return "{" + id + ", " + name + "}";
}
public Department(String id, String name) {
this.id = id;
this.name = name;
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
// 原型对象
Company company1 = new Company("A", "公司A", new Department("DA", "部门A"));
// 打印原型对象的内存地址和值属性
System.out.println(company1 + ", " + company1.getInfo());
// 打印原型对象引用成员变量的内存地址和属性
System.out.println(company1.getDepartment() + ", " + company1.getDepartment().getInfo());
// 克隆对象
Company company2 = company1.clone();
// 打印克隆对象的内存地址和值属性
System.out.println(company2 + ", " + company2.getInfo());
// 打印克隆对象引用成员变量的内存地址和属性
System.out.println(company2.getDepartment() + ", " + company2.getDepartment().getInfo());
}
}
上述例子中,company1为原型对象,company2为克隆对象,原型对象和克隆对象中的id和name(值类型)一样,说明值类型被复制了一份给company2,但company1和company2的内存地址不同,所以company1和company2不是同一个对象。company1中的引用对象department和company2中的引用对象department的内存地址一样,说明在克隆时,引用类型只复制引用对象的地址。打印结果如下:
如果想要原型对象中的引用对象也被作为原型对象完全克隆一份,就要使用深克隆。
深克隆
方法1:引用对象也实现Cloneable接,在原型对象clone方法中调用引用对象的clone方法,即可实现深克隆。
public class Company implements Cloneable {
private String id;
private String name;
private Department department;
public Company(String id, String name, Department department) {
this.id = id;
this.name = name;
this.department = department;
}
public String getInfo() {
return "{" + id + ", " + name + "}";
}
public Department getDepartment() {
return department;
}
@Override
protected Company clone() throws CloneNotSupportedException {
Company company = (Company) super.clone();
// 克隆引用对象
company.department = company.department.clone();
return company;
}
}
public class Department implements Cloneable {
private String id;
private String name;
public String getInfo() {
return "{" + id + ", " + name + "}";
}
public Department(String id, String name) {
this.id = id;
this.name = name;
}
@Override
protected Department clone() throws CloneNotSupportedException {
return (Department) super.clone();
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
// 原型对象
Company company1 = new Company("A", "公司A", new Department("DA", "部门A"));
// 打印原型对象的内存地址和值属性
System.out.println(company1 + ", " + company1.getInfo());
// 打印原型对象引用成员变量的内存地址和属性
System.out.println(company1.getDepartment() + ", " + company1.getDepartment().getInfo());
// 克隆对象
Company company2 = company1.clone();
// 打印克隆对象的内存地址和值属性
System.out.println(company2 + ", " + company2.getInfo());
// 打印克隆对象引用成员变量的内存地址和属性
System.out.println(company2.getDepartment() + ", " + company2.getDepartment().getInfo());
}
}
Company的引用类型成员变量Department也实现了Cloneable接口,在Company的clone方法中调用了Cloneable的clone方法进行克隆,因此在克隆Company时Department也会被作为原型对象复制一份。打印结果如下:
方法2:原型对象和引用类型的成员变量都实现实现Serializable接口,在克隆方法中,将对象写入字节流,然后再读取出来即可。
public class Company implements Serializable {
private String id;
private String name;
private Department department;
public Company(String id, String name, Department department) {
this.id = id;
this.name = name;
this.department = department;
}
public String getInfo() {
return "{" + id + ", " + name + "}";
}
public Department getDepartment() {
return department;
}
// 深克隆方法
protected Company deepClone() throws CloneNotSupportedException, IOException, ClassNotFoundException {
//将对象写入流中,相当于复制一份,原对象还存在于内存中(jvm机制)
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(this);
//将对象从流中取出
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
return (Company) objectInputStream.readObject();
}
}
public class Department implements Serializable {
private String id;
private String name;
public String getInfo() {
return "{" + id + ", " + name + "}";
}
public Department(String id, String name) {
this.id = id;
this.name = name;
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
// 原型对象
Company company1 = new Company("A", "公司A", new Department("DA", "部门A"));
// 打印原型对象的内存地址和值属性
System.out.println(company1 + ", " + company1.getInfo());
// 打印原型对象引用成员变量的内存地址和属性
System.out.println(company1.getDepartment() + ", " + company1.getDepartment().getInfo());
// 克隆对象
Company company2 = company1.deepClone();
// 打印克隆对象的内存地址和值属性
System.out.println(company2 + ", " + company2.getInfo());
// 打印克隆对象引用成员变量的内存地址和属性
System.out.println(company2.getDepartment() + ", " + company2.getDepartment().getInfo());
}
}
打印结果如下: