原型模式(Prototype Pattern)是指原型实例指定创建对象的种类,并且通过拷贝这些 原型创建新的对象。
原型模式主要适用于以下场景:
- 类初始化消耗资源较多。
- new 产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)
- 构造函数比较复杂。
- 循环体中生产大量对象时。
数据内容完全一样,但实例不同
- 能够直接拷贝其实际内容的数据类型/只支持9种,八大基本数据类型+String
- 深拷贝,其它类型也能拷贝
简单克隆
/**
* 原型接口
*/
public interface ProtoType {
ProtoType clone();
}
/**
* 具体需要克隆的对象
*/
public class ConcretePrototype implements ProtoType{
private int age;
private String name;
private List hobbies;
@Override
public ProtoType clone() {
ConcretePrototype concretePrototype = new ConcretePrototype();
concretePrototype.setAge(this.age);
concretePrototype.setHobbies(this.hobbies);
concretePrototype.setName(this.name);
return concretePrototype;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List getHobbies() {
return hobbies;
}
public void setHobbies(List hobbies) {
this.hobbies = hobbies;
}
}
public class Client {
private ProtoType protoType;
public Client(ProtoType protoType){
this.protoType= protoType;
}
public ProtoType startClone(){
return protoType.clone();
}
}
测试
public class Test {
public static void main(String[] args){
ConcretePrototype concretePrototype = new ConcretePrototype();
concretePrototype.setAge(11);
concretePrototype.setName("pro");
ArrayList<Object> arrayList = new ArrayList<>();
arrayList.add(1);
concretePrototype.setHobbies(arrayList);
System.out.println(concretePrototype);
Client client = new Client(concretePrototype);
ConcretePrototype protoType = (ConcretePrototype) client.startClone();
System.out.println(protoType);
System.out.println("克隆对象中的引用类型地址值:" + concretePrototype.getHobbies());
System.out.println("原对象中的引用类型地址值:" + protoType.getHobbies());
System.out.println("克隆对象中的hobbies:" + Arrays.asList(concretePrototype.getHobbies()));
System.out.println("原对象中的hobbies:" + Arrays.asList(protoType.getHobbies()));
System.out.println("对象地址比较:"+(protoType.getHobbies() == concretePrototype.getHobbies()));
ArrayList<Object> hobbies = (ArrayList<Object>) concretePrototype.getHobbies();
hobbies.add(2);
concretePrototype.setHobbies(hobbies);
System.out.println("克隆对象中的hobbies:" + Arrays.asList(concretePrototype.getHobbies()));
System.out.println("原对象中的hobbies:" + Arrays.asList(protoType.getHobbies()));
// com.vip.prototype.simple.ConcretePrototype@4b67cf4d
// com.vip.prototype.simple.ConcretePrototype@7ea987ac
// 克隆对象中的引用类型地址值:[1]
// 原对象中的引用类型地址值:[1]
// 克隆对象中的hobbies:[[1]]
// 原对象中的hobbies:[[1]]
// 对象地址比较:true
// 克隆对象中的hobbies:[[1, 2]]
// 原对象中的hobbies:[[1, 2]]
// 从测试结果看出 hobbies 的引用地址是相同的,意味着复制的不是值,而是引用的地址。
// 修改任意一个对象中的属性值,concretePrototype 和 protoType 的 hobbies 值都会改变。这就是我们常说的浅克隆。
// 只是完整 复制了值类型数据,没有赋值引用对象。
}
}
简单实例:
package prototype.simple;
import java.util.ArrayList;
import java.util.Date;
/**
* 克隆基于字节码
* 底层是c语言写的
* 不走构造方法
*/
public class ConcretePrototype implements Cloneable{
private int age;
private String name;
public ArrayList<String> list = new ArrayList<>();
private Date date;
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
@Override
protected Object clone() throws CloneNotSupportedException {
ConcretePrototype prototype = null;
//能够直接拷贝其实际内容的数据类型/只支持9种,八大基本数据类型+String
prototype = (ConcretePrototype) super.clone();
//手动克隆
prototype.list = (ArrayList<String>) list.clone();
return prototype;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package prototype.simple;
import java.util.Date;
/**
* 原型模式
* 数据内容完全一样,但实例不同
* 1. 能够直接拷贝其实际内容的数据类型/只支持9种,八大基本数据类型+String
* 2. 深拷贝,其它类型也能拷贝
*/
public class Test {
public static void main(String[] args){
ConcretePrototype cp = new ConcretePrototype();
cp.setAge(11);
cp.setName("abc");
cp.list.add("hhhh");
cp.setDate(new Date());
try {
ConcretePrototype clone = (ConcretePrototype) cp.clone();
System.out.println(clone==cp);
System.out.println(clone.list==cp.list);
System.out.println(clone.getAge()+" "+clone.getName()+" "+clone.list.size());
System.out.println(clone.getDate().getTime());
System.out.println(cp.getDate().getTime());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
-------------------------------------------------
package prototype.prototype;
import java.util.Date;
/**
* 鸣人
*/
public class MingRen {
protected int height;
protected Date date;
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
package prototype.prototype;
import java.io.Serializable;
/**
* 手里剑
*/
public class ShouLiJian implements Serializable {
private float height = 10;
public void grow(){
this.height *= 2;
}
public float getHeight() {
return height;
}
}
package prototype.prototype;
import java.io.*;
import java.util.Date;
/**
*
*/
public class YingFenSheng extends MingRen implements Cloneable, Serializable {
private ShouLiJian shouLiJian;
public YingFenSheng() {
System.out.println("构造执行");
this.shouLiJian = new ShouLiJian();
this.height = 170;
this.date = new Date();
}
@Override
protected Object clone(){
//深度克隆
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
//序列化
//对象持久化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this);
//反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
YingFenSheng yingFenSheng = (YingFenSheng) ois.readObject();
// 设置新的时间
yingFenSheng.date = new Date();
return yingFenSheng;
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if (bos!=null){
bos.close();
}
if (oos!=null){
oos.close();
}
if (bis!=null){
bis.close();
}
if (ois!=null){
ois.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
return null;
}
public void change(){
YingFenSheng copy = (YingFenSheng)clone();
System.out.println("mingren: "+this.date.getTime()+" "+copy.getHeight());
System.out.println("copy: "+copy.getDate().getTime()+" "+copy.getHeight());
System.out.println(this==copy);
System.out.println(this.getShouLiJian()==copy.getShouLiJian());
this.getShouLiJian().grow();
System.out.println("------------");
System.out.println("yuan ShouLiJian: "+this.getShouLiJian().getHeight());
System.out.println("copy ShouLiJian: "+copy.getShouLiJian().getHeight());
}
public ShouLiJian getShouLiJian() {
return shouLiJian;
}
public void setShouLiJian(ShouLiJian shouLiJian) {
this.shouLiJian = shouLiJian;
}
}
package prototype.prototype;
/**
*
*/
public class Test {
public static void main(String[] args){
YingFenSheng yingFenSheng = new YingFenSheng();
yingFenSheng.change();
}
//mingren: 1582682313951 0
//copy: 1582682314078 0
//false
//false
//------------
//yuan ShouLiJian: 20.0
//copy ShouLiJian: 10.0
}
克隆破坏单例模式
如果我们克隆的目标的对象是单例对象,那意味着,深克隆就会破坏单例。实际上防止 克隆破坏单例解决思路非常简单,禁止深克隆便可。要么你我们的单例类不实现 Cloneable 接口;要么我们重写 clone()方法,在 clone 方法中返回单例对象即可,具体 代码如下:
@Override
protected Object clone() throws CloneNotSupportedException {
return INSTANCE;
}
ArrayList的Cloneable 源码
/**
* Returns a shallow copy of this <tt>ArrayList</tt> instance. (The
* elements themselves are not copied.)
*
* @return a clone of this <tt>ArrayList</tt> instance
*/
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}