定义:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或者相似的新对象。
优点:
- Java自带的原型模式基于内存二进制流的复制,复制比new一个对象更加高效。
- 可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。
缺点:
- 需要给每一个类配置一个
clone
方法。 -
clone
方法位于类的内部,对类改造,修改类的代码,违背开闭原则。 - 深克隆时,当对象之间存在多重嵌套时,每一层都必须支持深克隆,实现会比较麻烦。
原型模式的实现
原型模式的实现分为浅克隆和深克隆。
浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
浅克隆
package com.yang.prototype;
import lombok.Data;
/**
* Order 订单类
* 属性 serialNumber 基础类型 编码
* 属性 Product 引用类型 产品
*/
@Data
public class Order implements Cloneable {
int serialNumber = 1;
Product product = new Product();
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
package com.yang.prototype;
import lombok.Data;
/**
* Product 产品类
* 属性 productName 产品名称
*/
@Data
public class Product {
String productName = "iphone11";
}
测试
package com.yang.prototype;
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Order order = new Order();
System.out.println(order);
Order orderClone = (Order) order.clone();
System.out.println(orderClone);
System.out.println(order == orderClone);
System.out.println(order.getProduct() == orderClone.getProduct());
}
}
Order(serialNumber=1, product=Product(productName=iphone11))
Order(serialNumber=1, product=Product(productName=iphone11))
false
true
有一个订单类Order
,有两个属性,一个基本类型serialNumber
,另一个引用类型Product
,要克隆订单类(必须满足):
- 实现
Cloneable
接口 - 重新
clone()
方法 - 调用 订单类
clone()
方法,实现克隆
结果证明:克隆对象是一个新的对象,具有自己的地址,但是引用类型参数,克隆的是它的引用地址值,下面这个例子将更具体证明这一点
package com.yang.prototype;
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Order order = new Order();
System.out.println(order);
Order orderClone = (Order) order.clone();
orderClone.setSerialNumber(2);
orderClone.getProduct().setProductName("iphone12");
System.out.println(order);
System.out.println(orderClone);
}
}
Order(serialNumber=1, product=Product(productName=iphone11))
Order(serialNumber=1, product=Product(productName=iphone12))
Order(serialNumber=2, product=Product(productName=iphone12))
我们对克隆对象操作,修改了基础类型setSerialNumber
的值,原型order
不会被影响,当我们修改引用类型product
的值,原型order
也随之改变了。
因为克隆对象和原型指向的是同一个对象,对象值改变,自然两个都会变化。
深克隆
浅克隆往往不能满足我们的实际需要,我们想要的是两个对象的完全隔离,这样就需要做一个深克隆。
其实也很简单,对于基本类型不用管,对于引用类型,我们不希望仅仅是克隆它的地址引用,那么在克隆的时候,我们主动去克隆一份不就可以了。
基于浅克隆的例子加以修改:
- 为引用类型
Product
添加克隆条件(实现Cloneable
接口,重新clone
方法)
package com.yang.prototype;
import lombok.Data;
/**
* Product 产品类
* 属性 productName 产品名称
*/
@Data
public class Product implements Cloneable {
String productName = "iphone11";
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
修改Order
克隆方法
package com.yang.prototype;
import lombok.Data;
/**
* Order 订单类
* 属性 serialNumber 基础类型 编码
* 属性 Product 引用类型 商品
*/
@Data
public class Order implements Cloneable {
int serialNumber = 1;
Product product = new Product();
@Override
protected Object clone() throws CloneNotSupportedException {
Order order = (Order) super.clone();
order.setProduct((Product) order.getProduct().clone());
return order;
}
}
测试
package com.yang.prototype;
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Order order = new Order();
System.out.println(order);
Order orderClone = (Order) order.clone();
orderClone.setSerialNumber(2);
orderClone.getProduct().setProductName("iphone12");
System.out.println(order);
System.out.println(orderClone);
}
}
Order(serialNumber=1, product=Product(productName=iphone11))
Order(serialNumber=1, product=Product(productName=iphone11))
Order(serialNumber=2, product=Product(productName=iphone12))
达到深克隆效果
总结
在使用原型模式的时候需要注意是深克隆还是浅克隆,避免造成安全隐患。