原型实例指定创建对象的种类,并且通过复制这些原型创建新对象。
使用场景:
浅克隆
浅克隆只是完整复制了值类型数据,没有赋值引用对象。换言之,所有的引用对象仍然指向原来的对象。
eg:
/**
* 原型接口
*/
public interface Prototype {
Prototype clone();
}
/**
* 需要克隆的类 实现原型接口类
*/
@Data
public class ConcretePrototypeA implements Prototype{
private String name;
private Integer age;
private List<String> hobbies;
@Override
public ConcretePrototypeA clone() {
ConcretePrototypeA concretePrototypeA=new ConcretePrototypeA();
concretePrototypeA.setAge(this.age);
concretePrototypeA.setHobbies(this.hobbies);
concretePrototypeA.setName(this.name);
return concretePrototypeA;
}
}
/**
* 类
*/
public class Client {
private Prototype prototype;
public Client(){}
public Client (Prototype prototype){
this.prototype =prototype;
}
public Prototype startClone(Prototype concretePrototypeA){
return concretePrototypeA.clone();
}
}
/**
* 测试类
*/
public class qianCloneTest {
public static void main(String[] args) {
ConcretePrototypeA concretePrototypeA = new ConcretePrototypeA();
concretePrototypeA.setName("小明");
concretePrototypeA.setAge(33);
ArrayList<String> list = new ArrayList<>();
list.add("排球");
concretePrototypeA.setHobbies(list);
System.out.println( "new出来的 ==== 》 "+concretePrototypeA);
//clone出来---浅克隆 ,hobbies的引用地址是相同的,意味着复制的不是值,而是引用的地址
// 浅克隆只是完整复制了值类型数据,没有赋值引用对象。换言之,所有的引用对象仍然指向原来的对象
Client client = new Client(concretePrototypeA);
ConcretePrototypeA prototypeClone =(ConcretePrototypeA) client.startClone(concretePrototypeA);
System.out.println("浅克隆====》"+prototypeClone);
System.out.println("克隆的对象类型的内存地址"+prototypeClone.getHobbies());
System.out.println("创建的的对象类型的内存地址"+concretePrototypeA.getHobbies());
System.out.println("对象类型的内存地址比较"+ (prototypeClone.getHobbies() == concretePrototypeA.getHobbies()));
}
}
测试结果
new出来的 ==== 》 ConcretePrototypeA(name=小明, age=33, hobbies=[排球])
浅克隆====》ConcretePrototypeA(name=小明, age=33, hobbies=[排球])
克隆的对象类型的内存地址[排球]
创建的的对象类型的内存地址[排球]
对象类型的内存地址比较true
深克隆
深克隆会将类的值类型,引用类型数据完全copy一份。会产生新的内存地址。
eg:
/**
* 实体类--monkey
*/
@Data
public class Monkey {
private Integer height;
private Integer weight;
private Date birthday;
}
/**
* 金箍棒类--附属属性
*/
@Data
public class Jingubang implements Serializable {
private float h=1000;
private float d=10;
public void small(){
this.h/=2;
this.d/=2;
}
public void big(){
this.h*=2;
this.d*=2;
}
}
/**
* 齐天大圣类
*/
@Data
public class QiTianDaSheng extends Monkey implements Serializable ,Cloneable {
private Jingubang jingubang;
QiTianDaSheng(){
//初始化数据
this.setBirthday(new Date());
this.setJingubang(new Jingubang());
}
@Override
protected Object clone() throws CloneNotSupportedException {
return this.deepClone();
}
private Object 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);
QiTianDaSheng copy = (QiTianDaSheng)ois.readObject();
copy.setBirthday(new Date());
return copy;
}catch (Exception e){
e.printStackTrace();
return null;
}
}
//浅克隆
public QiTianDaSheng shallowClone(QiTianDaSheng target){
QiTianDaSheng qiTianDaSheng = new QiTianDaSheng();
qiTianDaSheng.setBirthday(new Date());
qiTianDaSheng.setJingubang(target.getJingubang());
qiTianDaSheng.setHeight(target.getHeight());
qiTianDaSheng.setWeight(target.getWeight());
return qiTianDaSheng;
}
}
/**
* 测试类
*/
public class DeepCloneTest {
public static void main(String[] args) {
QiTianDaSheng qiTianDaSheng = new QiTianDaSheng();
try {
QiTianDaSheng clone = (QiTianDaSheng)qiTianDaSheng.clone();
System.out.println("深clone:"+(qiTianDaSheng.getJingubang()==clone.getJingubang()));
}catch (Exception e){
e.printStackTrace();
}
QiTianDaSheng q = new QiTianDaSheng();
QiTianDaSheng n = q.shallowClone(q);
System.out.println("浅克隆:"+(q.getJingubang()==n.getJingubang()));
}
}
测试结果
深clone:false
浅克隆:true
总结:
deepClone方法利用序列化实现深度克隆
把对象写到流里的过程是序列化(Serialization)过程;而把对象从流中读出来的过程则叫反序列化(Deserialization)过程。应当指出的是,写到流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。
在Java语言里深度克隆一个对象,常常可以先使对象实现Serializable接口,然后把对象(实际上只是对象的拷贝)写到一个流里(序列化),再从流里读回来(反序列化),便可以重建对象。
这样做的前提就是对象以及对象内部所有引用到的对象都是可序列化的,否则,就需要仔细考察那些不可序列化的对象可否设成transient,从而将之排除在复制过程之外。
浅度克隆显然比深度克隆更容易实现,因为Java语言的所有类都会继承一个clone()方法,而这个clone()方法所做的正是浅度克隆。
Java 提供了一个 Cloneable 接口来标示这个对象是可拷贝的,为什么说是“标示”呢?翻开 JDK 的帮助看看 Cloneable 是一个方法都没有的接口,这个接口只是一个标记作用,在 JVM 中具有这个标记的对象才有可能被拷贝,覆盖clone()方法就可以了。