1原型模式简介
定义:
Specify the kinds of objects to create using a prototypical instance,and create new objects by copying this prototype.(用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。)
使用场景:
依赖于外部资源或硬件密集型操作进行新对象的创建的情况。
获取相同对象在相同状态的拷贝而无须进行重复获取状态操作的情况。
在不确定所属具体类时需要对象的实例的情况。
通用类图
通过类图得知,原型模式的核心是一个clone方法,通过该方法进行对象的拷贝,Java提供了一个Cloneable接口来标示这个对象是可拷贝的
2克隆羊案例
原型模式经常用的一个案例就是克隆羊
2.1错误实现方式
public class Sheep {
private String name;
private int age;
private String color;
public Sheep(String name, int age, String color) {
super();
this.name = name;
this.age = age;
this.color = color;
}
//get set toString省略
}
public class Client {
public static void main(String[] args) {
Sheep sheep = new Sheep("tom", 1, "白色");
Sheep sheep2 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
Sheep sheep3 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
Sheep sheep4 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
Sheep sheep5 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
System.out.println(sheep);
System.out.println(sheep2);
System.out.println(sheep3);
System.out.println(sheep4);
System.out.println(sheep5);
}
}
上述代码存在的问题:①在复制克隆羊的时候,总是需要重新获取原始对象的属性,如果创建的对象比较复杂时,效率较低,②总是需要重新初始化(new)对象,不是动态的获取对象运行时的状态,不够灵活
2.2浅拷贝
对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象
对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象时,浅拷贝会进行引用传递,将该成员变量的引用值(内存地址)复制一份给新的对象。实际上两个对象的成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。
public class Thing implements Cloneable, Serializable{
//定义一个私有变量
private ArrayList<String> arrayList = new ArrayList<String>();
@Override
public Thing clone(){
Thing thing=null;
try {
thing = (Thing)super.clone();
// thing.arrayList = (ArrayList<String>)this.arrayList.clone();//深拷贝
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return thing;
}
//设置HashMap的值
public void setValue(String value){
this.arrayList.add(value);
}
//取得arrayList的值
public ArrayList<String> getValue(){
return this.arrayList;
}
}
public class Client {
public static void main(String[] args) {
//设置一个值
Thing thing = new Thing();
thing.setValue("张三");
//拷贝一个对象
Thing cloneThing = thing.clone();
cloneThing.setValue("李四");
System.out.println(thing.getValue());
}
}
运行结果为
原因是,Object类提供的方法clone只是拷贝本对象,其对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址,这种拷贝就叫做浅拷贝。
2.3深拷贝
复制对象的所有基本数据类型的成员变量值
为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,对象进行深拷贝要对整个对象(包括对象的引用类型)进行拷贝
方式1:重写clone(),打开注释就是深拷贝
thing.arrayList = (ArrayList<String>)this.arrayList.clone();//深拷贝
方式2:通过对象序列化实现深拷贝
public Object cloneByIO() {
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);
return ois.readObject();
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch (Exception e2) {
System.out.println(e2.getMessage());
}
}
}
两种执行效果都是相同的
总结:
原型模式是在内存二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个循环体内产生大量的对象时,原型模式可以更好地体现其优点。
原型模式直接在内存中拷贝,构造方式是不会执行的
一个实现了Cloneable并重写了clone方法的类A,有一个无参构造或有参构造B,通过new关键字产生了一个对象S,再然后通过S.clone()方式产生了一个新的对象T,那么在对象拷贝时构造函数B是不会被执行的。
在实际开发中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者。
spring中bean的创建中 scope的标签就是原型和单例模式