一、什么是原型模式
所谓原型模式就是克隆模式
也可以理解为复制一个对象
克隆存在两个方向:浅克隆、深克隆
二、浅克隆
首先我们要知道对象中除了常量类型(int、boolean、double等)、特殊的字符串类型外还有引用类型(其实就是java类中的另外的一个java对象),其次就是要知道这些类型都是有一个内存地址指向的
最后要注意的是被克隆的对象必须Cloneable,Serializable这两个接口
那么浅克隆是什么呢?
被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。 Object类提供的方法clone只是拷贝本对象 , 其对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址
举例
//这里用了lombok插件,不知道的建议可以百度一下
@Data
public class Psn implements Cloneable,Serializable{
private String name;
private String age;
private Date birth;
/**
* 实现克隆的方法
*/
public Object clone() throws CloneNotSupportedException{
return super.clone();
}
}
Test
public static void main(String[] args) throws CloneNotSupportedException {
Date date = new Date(1231231231231l);
Psn psn= new Psn ();
psn.setName("test");
psn.setAge(18);
psn.setBirth(date);
System.out.println("----输出原型对象的属性------");
System.out.println(psn);
System.out.println(psn.getName());
System.out.println(psn.getBirth());
// 克隆对象
Psn psn1 =(Psn ) psn.clone();
// 修改原型对象中的属性
date.setTime(123231231231l);
System.out.println(user.getBirth());
// 修改参数
psn1.setName("test1");
System.out.println("-------克隆对象的属性-----");
System.out.println(psn1);
System.out.println(psn1.getName());
System.out.println(psn1.getBirth());
}
这里就不输出结果了,自己观察一下日志
三、深克隆
被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。
第一种方式,重写实例clone方法内的部分
Object object = super.clone();
// 实现深度克隆(deep clone)
PsnCopy psnCopy = (PsnCopy)object;
psnCopy.birth = (Date) this.birth.clone();
return object;
第二种方式,使用序列化和反序列化
public static void main(String[] args) throws CloneNotSupportedException {
Date date = new Date(1231231231231l);
Psn psn= new Psn ();
psn.setName("test");
psn.setAge(18);
psn.setBirth(date);
System.out.println("----输出原型对象的属性------");
System.out.println(psn);
System.out.println(psn.getName());
System.out.println(psn.getBirth());
//使用序列化和反序列化实现深复制
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(psn);
byte[] bytes = bos.toByteArray();
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bis);
//克隆好的对象!
Psn psn1 = (Psn) ois.readObject();
// 修改原型对象的值
date.setTime(221321321321321l);
System.out.println(psn.getBirth());
System.out.println("------克隆对象的属性-------");
System.out.println(psn1);
System.out.println(psn1.getName());
System.out.println(psn1.getBirth());
四、原型模式和直接new对象方式的比较
当我们需要大量的同一类型对象的时候可以使用原型模式,下面是两种方式的性能对比:
用两种方式同时生成1000个对象
/**
* 测试普通new方式创建对象和clone方式创建对象的效率差异!
* 如果需要短时间创建大量对象,并且new的过程比较耗时。则可以考虑使用原型模式!
* @author 波波烤鸭
*
*/
public class Client4 {
public static void testNew(int size){
long start = System.currentTimeMillis();
for(int i=0;i<size;i++){
Psn t = new Psn();
}
long end = System.currentTimeMillis();
System.out.println("new的方式创建耗时:"+(end-start));
}
public static void testClone(int size) throws CloneNotSupportedException{
long start = System.currentTimeMillis();
User t = new User();
for(int i=0;i<size;i++){
Psn temp = (Psn) t.clone();
}
long end = System.currentTimeMillis();
System.out.println("clone的方式创建耗时:"+(end-start));
}
public static void main(String[] args) throws Exception {
testNew(1000);
testClone(1000);
}
}
class User implements Cloneable { //用户
public User() {
try {
Thread.sleep(10); //模拟创建对象耗时的过程!
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
protected Object clone() throws CloneNotSupportedException {
Object obj = super.clone(); //直接调用object对象的clone()方法!
return obj;
}
}
输出结果:
小结:通过clone的方式在获取大量对象的时候性能开销基本没有什么影响,而new的方式随着实例的对象越来越多,性能会急剧下降,所以原型模式是一种比较重要的获取实例的方式。大家应该掌握好
原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者。
• spring中bean的创建实际就是两种:单例模式和原型模式。(原型模式需要和工厂模式搭配起来)