Java 的设计模式有 23 种,前段时间小编已经介绍了单例模式,由于我们在学习 Spring 的时候在 bean 标签的学习中碰到了今天要讲的原型模式,那么小编就已本文来介绍下原型模式。
原型模式
在java中我们知道通过new关键字创建的对象是非常繁琐的(类加载判断,内存分配,初始化等),在我们需要大量对象的情况下,原型模式就是我们可以考虑实现的方式。
原型模式我们也称为克隆模式,即一个某个对象为原型克隆出来一个一模一样的对象,该对象的属性和原型对象一模一样。而且对于原型对象没有任何影响。原型模式的克隆方式有两种:浅克隆和深度克隆
浅克隆
在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。
简单来说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。
实现
被克隆的对象必须Cloneable,Serializable这两个接口
原型类
/**
* 原型类:被克隆的类型
*/
public class User implements Cloneable, Serializable {
private String name;
private Date birth;
private int age;
/**
* 实现克隆的方法
* @return
* @throws CloneNotSupportedException
*/
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
//此处略过get和set方法
}
测试类
public static void main(String[] args) throws CloneNotSupportedException {
Date date = new Date(1231231231231l);
User user = new User();
user.setName("zmf");
user.setAge(18);
user.setBirth(date);
System.out.println("----输出原型对象的属性------");
System.out.println(user);
System.out.println(user.getName());
System.out.println(user.getBirth());
// 克隆对象
User user1 =(User) user.clone();
// 修改原型对象中的属性
date.setTime(123231231231l);
System.out.println("原型对象修改后的属性:" + user.getBirth());
// 修改参数
user1.setName("知性人");
System.out.println("-------克隆对象的属性-----");
System.out.println(user1);
System.out.println(user1.getName());
System.out.println(user1.getBirth());
}
输出结果:
----输出原型对象的属性------
org.zmf.User@1b6d3586
zmf
Tue Jan 06 16:40:31 CST 2009
原型对象修改后的属性:Tue Nov 27 14:53:51 CST 1973
-------克隆对象的属性-----
org.zmf.User@14ae5a5
知性人
Tue Nov 27 14:53:51 CST 1973
说明:
克隆后的 date 属性和原型对象修改后的 date 属性的结果一样 说明两个对象的Date的引用是同一个。由此可以说明,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。
深克隆
在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。
简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制。
深度克隆(deep clone)有两种实现方式,第一种是在浅克隆的基础上实现,第二种是通过序列化和反序列化实现,我们分别来介绍
第一种方式
在浅克隆的基础上实现
原型类
/**
* 原型类:被克隆的类型
* 深度克隆测试
*/
public class User2 implements Cloneable, Serializable {
private String name;
private Date birth;
private int age;
/**
* 实现克隆的方法
* 深度克隆(deep clone)
*/
public Object clone() throws CloneNotSupportedException{
Object object = super.clone();
// 实现深度克隆(deep clone)
User2 user = (User2)object;
user.birth = (Date) this.birth.clone();
return object;
}
//此处略过 get 和 set 方法
}
测试类:
public static void main(String[] args) throws CloneNotSupportedException {
Date date = new Date(1231231231231l);
User2 user = new User2();
user.setName("zmf");
user.setAge(18);
user.setBirth(date);
System.out.println("----输出原型对象的属性------");
System.out.println(user);
System.out.println(user.getName());
System.out.println(user.getBirth());
// 克隆对象
User2 user1 =(User2) user.clone();
// 修改原型对象中的属性
date.setTime(123231231231l);
System.out.println("原型对象修改后的属性:" + user.getBirth());
// 修改参数
user1.setName("知性人");
System.out.println("-------克隆对象的属性-----");
System.out.println(user1);
System.out.println(user1.getName());
System.out.println(user1.getBirth());
}
测试结果:
----输出原型对象的属性------
org.zmf.User2@1b6d3586
zmf
Tue Jan 06 16:40:31 CST 2009
原型对象修改后的属性:Tue Nov 27 14:53:51 CST 1973
-------克隆对象的属性-----
org.zmf.User2@14ae5a5
知性人
Tue Jan 06 16:40:31 CST 2009
说明:
根据测试得出克隆后的对象的属性并没有随着我们对原型对象Date属性的修改而改变,说明克隆对象的Date属性和原型对象的Date属性引用的不是同一个对象,实现的深度复制。
第二种方式:序列化和反序列化
- 说明
序列化: 把对象转换为字节序列的过程。
反序列化: 把字节序列恢复为对象的过程。
public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
Date date = new Date(1231231231231L);
User user = new User();
user.setName("zmf");
user.setAge(18);
user.setBirth(date);
System.out.println("-----原型对象的属性------");
System.out.println(user);
System.out.println(user.getName());
System.out.println(user.getBirth());
//使用序列化和反序列化实现深复制
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(user);
byte[] bytes = bos.toByteArray();
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bis);
//克隆好的对象!
User user1 = (User) ois.readObject();
// 修改原型对象的值
date.setTime(221321321321321L);
System.out.println(user.getBirth());
System.out.println("------克隆对象的属性-------");
System.out.println(user1);
System.out.println(user1.getName());
System.out.println(user1.getBirth());
}
测试结果:
-----原型对象的属性------
org.zmf.User@1b6d3586
zmf
Tue Jan 06 16:40:31 CST 2009
Sat May 24 16:48:41 CST 8983
------克隆对象的属性-------
org.zmf.User@5f184fc6
zmf
Tue Jan 06 16:40:31 CST 2009
实现了和第一种实现方式相同的效果~实现了深度克隆
总结
实现对象克隆有两种方式:
1). 实现Cloneable接口并重写Object类中的clone()方法;
2). 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。