1、前言
当需要克隆100个名称为Dolly 年龄 1的羊,以往的写法都是直接new 100个Sheep对象;
public class PrototypeDemo {
public static void main(String[] args) {
// 克隆100个 名称为Dolly 年龄 1;
Sheep dolly1 = new Sheep("Dolly", 1);
Sheep dolly2 = new Sheep("Dolly", 1);
Sheep dolly3 = new Sheep("Dolly", 1);
Sheep dolly4 = new Sheep("Dolly", 1);
// ...
Sheep dolly100 = new Sheep("Dolly", 1);
}
@Data
static
class Sheep {
private String name;
private int age;
public Sheep (String name,int age){
this.name = name;
this.age = age;
}
}
}
- 优点
- 代码直观,易于理解
- 缺点
- 在创建新的对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂时,效率较低
- 总是需要重新初始化对象,而不是动态地获得对象运行时的状态,不够灵活
2、什么是原型模式
原型模式(Prototype Pattern)通过拷贝这些原型,创建新的对象,是用于创建重复的对象,同时又能保证性能。原型模式属于创建型模式,它提供了一种创建对象的最佳方式。
原型模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。
Java中Object类提供了一个clone()方法,该方法可以将一个Java对象复制一份,但是需要实现clone的Java类必须要实现一个接口Cloneable,该接口表示该类能够复制且具有复制的能力
-
原型模式的实例的拷贝包括
- 浅复制:将一个对象复制后,其基本数据类型的变量都会重新创建,而引用类型的变量指向的还是原对象所指向的,也就是指向的内存堆地址没变。
- 深复制:将一个对象复制后,不论是基本数据类型还是引用类型,都是重新创建的
-
通过实现Cloneable接口,
Java默认的实现方式是浅复制,而非深复制。
由于Object并没有实现Cloneable接口,所以子类必须实现Cloneable,并调用基类的clone方法才能实现浅复制。
3、原型模式实现
/**
* 原型模式
* @author liushiwei
*/
public class PrototypeDemo {
public static void main(String[] args) throws Exception {
prototype();
}
private static void prototype() throws Exception {
ArrayList arrayList = new ArrayList();
arrayList.add("foreFoot");
Sheep dolly1 = new Sheep("Dolly", 1,arrayList);
// 对象深拷贝
Sheep dolly2 = (Sheep)dolly1.clone();
ArrayList list = dolly2.getListFoot();
list.add("hindFoot2");
// 序列化的方式深拷贝
Sheep dolly3 = (Sheep)dolly1.deepClone();
ArrayList list3 = dolly3.getListFoot();
list3.add("hindFoot3");
System.out.println(dolly1 == dolly2);
// 验证是拷贝类型,结果浅拷贝
System.out.println("dolly1.list="+dolly1.getListFoot());
System.out.println("dolly2.list="+dolly2.getListFoot());
System.out.println("dolly3.list="+dolly3.getListFoot());
}
private static void tradition() {
// 克隆100个 名称为Dolly 年龄 1;
Sheep dolly1 = new Sheep("Dolly", 1,null);
Sheep dolly2 = new Sheep("Dolly", 1,null);
Sheep dolly3 = new Sheep("Dolly", 1,null);
Sheep dolly4 = new Sheep("Dolly", 1,null);
// ...
Sheep dolly100 = new Sheep("Dolly", 1,null);
}
}
public class Sheep implements Cloneable, Serializable {
private String name;
private int age;
private ArrayList listFoot;
public Sheep (){
}
public Sheep (String name,int age,ArrayList listFoot){
this.name = name;
this.age = age;
this.listFoot = listFoot;
}
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 ArrayList getListFoot() {
return listFoot;
}
public void setListFoot(ArrayList listFoot) {
this.listFoot = listFoot;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Sheep sheep = (Sheep)super.clone();
// 对listFoot深拷贝
sheep.listFoot = (ArrayList) this.listFoot.clone();
return sheep;
}
/**
* 通过序列化的方式 深拷贝
* @return
*/
protected Object deepClone() throws Exception{
//把对象写入到字节流中
ByteArrayOutputStream baos =new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
//把字节流转化为对象
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
return (Sheep) ois.readObject();
}
}
4、原型模式优缺点
-
原型模式的优点:
-
提高了创建对象的性能,避免了调用构造器创建对象。
-
对于创建一个对象需要很多资源的情况,可以减少资源的浪费。
-
-
原型模式的缺点:
-
如果使用Cloneable接口的方式,需要实现Cloneable接口,对代码有一定的侵入性。
-
如果使用序列化方式,则需要实现Serializable接口,对代码也有一定的侵入性。
-
5、原型模式的更多实现
- Spring
Sheep dolly4 = new Sheep(); BeanUtils.copyProperties(dolly1,dolly4); ArrayList list4 = dolly4.getListFoot(); list4.add("hindFoot4"); System.out.println("list4.list="+dolly4.getListFoot());
- fastjson
- 通过JSONObject.parseObject方法实现