原型模式-浅克隆和深克隆的自我理解

原型模式是一种创建型设计模式,它通过复制一个已经存在的实例来返回新的实例,而不是新建实例.被复制的实例就是我们所称的原型,这个原型是可定制的.
原型模式多用于创建复杂的或者耗时的实例, 因为这种情况下,复制一个已经存在的实例可以使程序运行更高效,或者创建值相等,只是命名不一样的同类数据.

原型模式中的拷贝分为"浅拷贝"和"深拷贝":
浅拷贝: 对值类型的成员变量进行值的复制,对引用类型的成员变量只复制引用,不复制引用的对象.
深拷贝: 对值类型的成员变量进行值的复制,对引用类型的成员变量也进行引用对象的复制.

类图:

查看api object的clone()方法,这里面是这样说明的:

此方法返回的对象应该独立于该对象(正被复制的对象)。要获得此独立性,在 super.clone 返回对象之前,有必要对该对象的一个或多个字段进行修改。这通常意味着要复制包含正在被复制对象的内部“深层结构”的所有可变对象并使用对副本的引用替换对这些对象的引用如果一个类只包含基本字段或对不变对象的引用,那么通常不需要修改 super.clone 返回的对象中的字段。

Object 类的 clone 方法执行特定的复制操作。首先,如果此对象的类不能实现接口 Cloneable,则会抛出 CloneNotSupportedException注意,所有的数组都被视为实现接口 Cloneable。否则,此方法会创建此对象的类的一个新实例,并像通过分配那样,严格使用此对象相应字段的内容初始化该对象的所有字段;这些字段的内容没有被自我复制。所以,此方法执行的是该对象的“浅表复制”,而不“深层复制”操作。


实例一:浅拷贝

public class Prototype implements Cloneable {
 private String name;

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }

 public Object clone() {
  try {
   return super.clone();
  } catch (CloneNotSupportedException e) {   
   e.printStackTrace();
   return null;
  }
 } 
 
}

public class TestMain {

 public static void main(String[] args) {
  testPrototype();
 }
 
 private static void testPrototype(){
  Prototype pro = new Prototype();
  pro.setName("original object");
  Prototype pro1 = (Prototype)pro.clone();
  pro.setName("changed object1");
  
  System.out.println("original object:" + pro.getName());
  System.out.println("cloned object:" + pro1.getName());
  
 }

}

结果:
original object:changed object1
cloned object:original object

克隆的对象复制了String类型的name的引用,没有复制常量池中的对象,我们知道String是引用数据类型,在常量池中他的值只有一份,而且String的定义是,public final class String{},所以无法更改,当给克隆对象的name赋值时,会在常量池中重新建立一个新的字符串,对该name的赋值,指向新的字符串对象的地址。不能复制引用的对象。

原name的地址仍然指向原字符串对象。

浅拷贝: 对值类型的成员变量进行值的复制,对引用类型的成员变量只复制引用,不复制引用的对象.

既然String特性不能更改对象的值,再来一个自己写的小例子仔细说明:

package com.java.simpleclone;

public class ConcretePrototype implements Cloneable{
	
//引用类型
     String name;
     //引用类型
     int[]  array;//数组对象

	public int[] getArray() {
		return array;
	}
	public void setArray(int[] array) {
		this.array = array;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public ConcretePrototype() {
		
	}
	@Override
	protected Object clone(){
		// TODO Auto-generated method stub
		try {
			return super.clone();
		} catch (CloneNotSupportedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
	
	
}

客户端

package com.java.simpleclone;

public class CloneTest {


	public static void main(String[] args) {
	
		ConcretePrototype cp=new ConcretePrototype();
		
		cp.setName("Orginal Prototype");
		
		cp.setArray(new int[]{1000,200});
		int[]  b=cp.getArray();
		System.out.println("克隆前cp "+cp.getName()+"==数组=="+cp.getArray()[0]);
		
		ConcretePrototype  cp2=(ConcretePrototype)cp.clone();
		
		cp2.setName("Copy Prototype");
		int[] a=cp2.getArray();//
		a[0]=500;
		cp2.setArray(a);
		
		System.out.print("是否同属于一个数组:");
		System.out.println(a==b);
		
		System.out.println("克隆后cp "+cp.getName()+"==数组=="+cp.getArray()[0]);
		
		System.out.println("克隆后cp2 "+cp2.getName()+"==数组=="+cp2.getArray()[0]);
		/**
		 * 运行结果:
		 * 克隆前cp Orginal Prototype==数组==1000
            是否同属于一个数组:true
            克隆后cp Orginal Prototype==数组==500
            克隆后cp2 Copy Prototype==数组==500

		 */
		//克隆之后给cp2的数组重新定义,cp的数组的值改变了,这说明cp2.getArray() 获取的数组对象,跟cp里面的数组对象是同一个,  
	      //        非常明白的说明了浅克隆的 只复制对象的引用,不复制引用对象  
	}

}

运行结果:

克隆前cp Orginal Prototype==数组==1000
是否同属于一个数组:true
克隆后cp Orginal Prototype==数组==500
克隆后cp2 Copy Prototype==数组==500

下面对上面这个例子进行深克隆,深复制

package com.java.simpleclone;

public class ConcretePrototype implements Cloneable{
	
//引用类型
     String name;
     //引用类型
     int[]  array;//数组对象

	public int[] getArray() {
		return array;
	}
	public void setArray(int[] array) {
		this.array = array;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public ConcretePrototype() {
		
	}
	@Override
	protected Object clone(){
		ConcretePrototype cn=null;
		try {
			cn=(ConcretePrototype) super.clone();
			cn.array=this.array.clone();//api的说明,所有的数组都实现了clonable 接口,复制一个新的数组副本
			
			return cn;
		} catch (CloneNotSupportedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
	
	
}

客户端

package com.java.simpleclone;

public class CloneTest {


	public static void main(String[] args) {
	
		ConcretePrototype cp=new ConcretePrototype();
		
		cp.setName("Orginal Prototype");
		
		cp.setArray(new int[]{1000,200});
		int[]  b=cp.getArray();
		System.out.println("克隆前cp "+cp.getName()+"==数组=="+cp.getArray()[0]);
		
		ConcretePrototype  cp2=(ConcretePrototype)cp.clone();
		
		cp2.setName("Copy Prototype");
		int[] a=cp2.getArray();//
		a[0]=500;
		cp2.setArray(a);
		
		System.out.print("是否同属于一个数组:");
		System.out.println(a==b);
		
		System.out.println("克隆后cp "+cp.getName()+"==数组=="+cp.getArray()[0]);
		
		System.out.println("克隆后cp2 "+cp2.getName()+"==数组=="+cp2.getArray()[0]);
		/**
		 * 运行结果:
		 * 克隆前cp Orginal Prototype==数组==1000
            是否同属于一个数组:false
            克隆后cp Orginal Prototype==数组==1000
            克隆后cp2 Copy Prototype==数组==500

		 */
//		克隆之后给cp2的数组重新定义,cp的数组的值没有改变,这说明cp2.getArray() 获取的数组对象,跟cp里面的数组对象不是同一个,
	      //		说明我们的深复制成功了
	}

}


实例二: 浅拷贝
public class Prototype{
 private String name;

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 } 
 
}
public class NewPrototype implements Cloneable {
 
 private String id;
 
 public String getId() {
  return id;
 }

 public void setId(String id) {
  this.id = id;
 }

 private Prototype prototype;
 
 public Prototype getPrototype() {
  return prototype;
 }

 public void setPrototype(Prototype prototype) {
  this.prototype = prototype;
 }


 public Object clone(){ 
  try {
   return super.clone();
  } catch (CloneNotSupportedException e) {
   e.printStackTrace();
   return null;
  }  
 }

}
public class TestMain {

 public static void main(String[] args) {
  // TODO Auto-generated method stub
  testPrototype();
 }
 
 private static void testPrototype(){
  Prototype pro = new Prototype();
  pro.setName("original object");
  NewPrototype newObj = new NewPrototype();
  newObj.setId("test1");
  newObj.setPrototype(pro);
  
  NewPrototype copyObj = (NewPrototype)newObj.clone();
  copyObj.setId("testCopy");
  copyObj.getPrototype().setName("changed object");
  
  System.out.println("original object id:" + newObj.getId());
  System.out.println("original object name:" + newObj.getPrototype().getName());
  
  System.out.println("cloned object id:" + copyObj.getId());
  System.out.println("cloned object name:" + copyObj.getPrototype().getName());
  
 }

}

结果:
original object id:test1
original object name:changed object
cloned object id:testCopy
cloned object name:changed object


实例三: 深拷贝

public class Prototype implements Cloneable {
 private String name;

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }

 public Object clone() {
  try { 
   return super.clone();
  } catch (CloneNotSupportedException e) {   
   e.printStackTrace();
   return null;
  }
 } 
 
}

public class NewPrototype implements Cloneable {
 
 private String id;
 
 public String getId() {
  return id;
 }

 public void setId(String id) {
  this.id = id;
 }

 private Prototype prototype;
 
 public Prototype getPrototype() {
  return prototype;
 }

 public void setPrototype(Prototype prototype) {
  this.prototype = prototype;
 }


 public Object clone(){
  NewPrototype ret = null;
  try {
   ret = (NewPrototype)super.clone();
   ret.prototype = (Prototype)this.prototype.clone();
   return ret;
  } catch (CloneNotSupportedException e) {
   e.printStackTrace();
   return null;
  }  
 }

}

public class TestMain {

 /**
  * @param args
  */
 public static void main(String[] args) {
  testDeepCopy();
 }
 
 private static void testDeepCopy(){
  Prototype pro = new Prototype();
  pro.setName("original object");
  NewPrototype newObj = new NewPrototype();
  newObj.setId("test1");
  newObj.setPrototype(pro);
  
  NewPrototype copyObj = (NewPrototype)newObj.clone();
  copyObj.setId("testCopy");
  copyObj.getPrototype().setName("changed object");
  
  System.out.println("original object id:" + newObj.getId());
  System.out.println("original object name:" + newObj.getPrototype().getName());
  
  System.out.println("cloned object id:" + copyObj.getId());
  System.out.println("cloned object name:" + copyObj.getPrototype().getName());
  
 }

}

结果:
original object id:test1
original object name:original object
cloned object id:testCopy
cloned object name:changed object

实例四: 利用串行化来做深复制
把对象写道流里的过程是串行化(Serilization)过程;把对象从流中读出来是并行化(Deserialization)过程. 写在流里的是对象的一个拷贝,然后再从流里读出来重建对象.

public class PrototypeSe implements Serializable {

 private String name;

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }

}

public class NewPrototypeSe implements Serializable {
 
 private String id;
 
 public String getId() {
  return id;
 }

 public void setId(String id) {
  this.id = id;
 }

 private PrototypeSe prototype;
 
 public PrototypeSe getPrototype() {
  return prototype;
 }

 public void setPrototype(PrototypeSe prototype) {
  this.prototype = prototype;
 }
 
 public Object deepClone(){
  try {
   ByteArrayOutputStream bo = new ByteArrayOutputStream();
   ObjectOutputStream oo = new ObjectOutputStream(bo);   
   oo.writeObject(this);   
   
   ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
   ObjectInputStream oi = new ObjectInputStream(bi);
   return oi.readObject(); 
  } catch (IOException | ClassNotFoundException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
   return null;
  }
 }

}

public class TestDeepClone {

 public static void main(String[] args) {
  // TODO Auto-generated method stub
  PrototypeSe po = new PrototypeSe();
  po.setName("test1");
  NewPrototypeSe se = new NewPrototypeSe();
  se.setPrototype(po);
  
  NewPrototypeSe deepClone = (NewPrototypeSe)se.deepClone();
  deepClone.getPrototype().setName("test2");
  
  System.out.println("original name:" + se.getPrototype().getName());
  System.out.println("cloned name:" + deepClone.getPrototype().getName());

 }
}

结果:
original name:test1
cloned name:test2
本文部分摘自互联网。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值