1. 原型模式
原型模式用来解决对象的创建问题,它是指用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的对象。其类图结构如下:
- 抽象原型类(Prototype):声明了clone()方法的接口或基类,其中clone()方法必须由派生类实现。
- 具体原型类(ConcretePrototype):用于实现或扩展clone()方法的类,clone()方法必须要实现。
原型模式的优点:
- JAVA自带的原型模式基于内存二进制流的复制,在性能上比直接new一个对象更加优良。
- 原型模式保留了对象在某一时刻的状态,简化了对象创建过程,可辅助实现撤销操作恢复到历史某一个状态。
原型模式的缺点:
- 需要为每一个具体的原型类配置一个clone()方法
- clone()方法位于类的内部,当对已有类进行改造时需要修改代码,违背开闭原则。
- 当对象之间存在多重嵌套引用时,为了实现深克隆,每层对象都必须支持深克隆,实现起来比较麻烦。
2. 原型实现
生物学上有著名的克隆羊案例,接下来从程序的角度利用原型模式体会克隆羊案例。
新建一个羊类,类名为:Sheep,实现Cloneable接口,内容如下:
public class Sheep implements Cloneable{
private Double weight;
private String color;
public Sheep (){
}
public Sheep (Double weight, String color){
this.weight = weight;
this.color = color;
}
@Override
public String toString() {
return String.format("对象地址:%d 体重:%.2fKg 颜色:%s", hashCode(), weight, color);
}
@Override
protected Sheep clone() {
try{
return (Sheep) super.clone();
} catch (Exception exception){
exception.printStackTrace();
}
return new Sheep();
}
}
新建一个客户端类,类名为:Client,内容如下:
public class Client {
public static void main(String[] args) {
Sheep sheep = new Sheep(30.0D, "白色");
System.out.println("原型羊:" + sheep);
Sheep cloneSheep = sheep.clone();
System.out.println("克隆羊:" + cloneSheep);
}
}
4. 深浅拷贝
- 浅拷贝:对象的浅拷贝是指拷贝一个对象时,新对象和原来对象完全相同,但是对于可变的引用数据类型时,仍指向原型对象属性所指向的内存地址。
- 深拷贝:对象的深拷贝是指拷贝一个对象时,属性中引用的其他对象也会被克隆,不再指向原型对象属性的内存地址。
4.1 浅拷贝案例
克隆羊案例中使用的super.clone()方法使用的是Object类的clone()方法,它是一个浅拷贝,我们给羊这个类新增一个属性friend表示羊的朋友,代码如下:
public class Sheep implements Cloneable{
private Double weight;
private String color;
private Sheep friend;
public Sheep (){
}
public Sheep (Double weight, String color, Sheep friend){
this.weight = weight;
this.color = color;
this.friend = friend;
}
public Sheep getFriend() {
return friend;
}
public Double getWeight() {
return weight;
}
public void setWeight(Double weight) {
this.weight = weight;
}
@Override
public String toString() {
return String.format("对象地址:%d 体重:%.2fKg 颜色:%s 朋友:[%s]", hashCode(), weight, color, friend);
}
@Override
protected Sheep clone() {
try{
return (Sheep) super.clone();
} catch (Exception exception){
exception.printStackTrace();
}
return new Sheep();
}
}
新建客户端测试,内容如下:
public class Client {
public static void main(String[] args) {
Sheep friend = new Sheep(30.0D, "白色", null);
Sheep sheep = new Sheep(20.0D, "黑色", friend);
Sheep cloneSheep = sheep.clone();
System.out.println(String.format("原型羊:%s", sheep));
System.out.println(String.format("克隆羊:%s", cloneSheep));
System.out.println("----------------------朋友羊长胖了-----------------------------");
sheep.getFriend().setWeight(friend.getWeight() + 10);
System.out.println(String.format("原型羊:%s", sheep));
System.out.println(String.format("克隆羊:%s", cloneSheep));
}
}
在这个案例中我们修改原型羊的朋友的体重时,克隆羊的朋友的体重也一起跟着修改了,克隆羊的朋友属性和原型羊的朋友属性指向的是同一个对象的地址,这是因为friend属性的类型是一个Sheep引用类型,clone()方法克隆原型羊的时候克隆的是引用。
4.2 深拷贝案例
有时我们希望克隆对象的可变引用类型也是一个新的对象,那么原型羊的朋友羊再长胖了和克隆羊也没有关系,我们的朋友不是同一个朋友,这就是对象的深拷贝,此时需要对原型对象中的可变引用类型的属性做特殊处理,特殊处理clone()方法如下:
@Override
protected Sheep clone() {
try{
Sheep cloneSheep = (Sheep) super.clone();
if (!Objects.isNull(cloneSheep.friend)){
cloneSheep.friend = cloneSheep.friend.clone();
}
return cloneSheep;
} catch (Exception exception){
exception.printStackTrace();
}
return new Sheep();
}
新建客户端测试,内容如下:
public class Client {
public static void main(String[] args) {
Sheep friend = new Sheep(30.0D, "白色", null);
Sheep sheep = new Sheep(20.0D, "黑色", friend);
Sheep cloneSheep = sheep.clone();
System.out.println(String.format("原型羊:%s", sheep));
System.out.println(String.format("克隆羊:%s", cloneSheep));
System.out.println("----------------------朋友羊长胖了-----------------------------");
sheep.getFriend().setWeight(friend.getWeight() + 10);
System.out.println(String.format("原型羊:%s", sheep));
System.out.println(String.format("克隆羊:%s", cloneSheep));
}
}
在对象的深克隆中,克隆羊和原型羊的朋友不再是同一个对象,原型羊的朋友长胖了,但是克隆羊的朋友并没有长胖。
4.3 序列化实现深拷贝
对于对象的深度克隆需要保证该对象中每个可变的引用类型属性都实现Cloneable接口重写clone()方法,在clone()方法中还得对这些属性特殊处理,这样clone()方法变得复杂。JAVA中还可以通过序列化接口Serializable将对象在内存中序列化和反序列化一下来对对象进行深克隆,此时需要对象中可变的引用属性类型都要实现Serializable接口。
新建一个马类作为原型羊的朋友,实现Serializable接口,类名为Horse,内容如下:
public class Horse implements Serializable {
private Double weight;
public Horse(){
}
public Horse(Double weight){
this.weight = weight;
}
public Double getWeight() {
return weight;
}
public void setWeight(Double weight) {
this.weight = weight;
}
@Override
public String toString() {
return String.format("对象地址:%d 体重:%.2fKg ", hashCode(), weight);
}
}
新建一个羊类,包含一个朋友属性类型为马类,实现Serializable接口,类名为Sheep,内容如下:
public class Sheep implements Serializable {
private Double weight;
private String color;
private Horse friend;
public Sheep(){
}
public Sheep(Double weight, String color, Horse friend){
this.weight = weight;
this.color = color;
this.friend = friend;
}
public Horse getFriend() {
return friend;
}
@Override
public String toString() {
return String.format("对象地址:%d 体重:%.2fKg 颜色:%s 朋友:[%s]", hashCode(), weight, color, friend);
}
}
新建一个工具类用来进行序列化和反序列化来克隆,类名为BeanUtil,内容如下:
public class BeanUtil {
public static <T> T clone(Serializable bean, Class<T> type){
ByteArrayOutputStream byteArrayOutputStream = null; new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = null;
ByteArrayInputStream byteArrayInputStream = null;
ObjectInputStream objectInputStream = null;
try {
byteArrayOutputStream = new ByteArrayOutputStream();
objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(bean);
objectOutputStream.flush();
byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
objectInputStream = new ObjectInputStream(byteArrayInputStream);
return (T) objectInputStream.readObject();
} catch (Exception exception) {
exception.printStackTrace();
} finally {
try{
if (!Objects.isNull(objectInputStream)) {
objectInputStream.close();
}
if (!Objects.isNull(byteArrayInputStream)) {
byteArrayInputStream.close();
}
if (!Objects.isNull(byteArrayOutputStream)) {
byteArrayOutputStream.close();
}
if (!Objects.isNull(objectOutputStream)) {
objectOutputStream.close();
}
} catch (Exception exception){
exception.printStackTrace();
}
}
return null;
}
}
新建一个客户端测试,类名为Client,内容如下:
public class Client {
public static void main(String[] args) {
Horse friend = new Horse(30.0D);
Sheep sheep = new Sheep(20.0D, "白色", friend);
Sheep cloneSheep = BeanUtil.clone(sheep, Sheep.class);
System.out.println(String.format("原型羊:%s", sheep));
System.out.println(String.format("克隆羊:%s", cloneSheep));
System.out.println("----------------------朋友马长胖了-----------------------------");
sheep.getFriend().setWeight(friend.getWeight() + 10);
System.out.println(String.format("原型羊:%s", sheep));
System.out.println(String.format("克隆羊:%s", cloneSheep));
}
}
原型羊的朋友长胖了但是克隆羊的朋友并没有长胖,实现了对象的深克隆。
|