设计模式学习笔记(原型模式)

原型模式
用原型的实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

在Java中使用原型模式需要实现 java.lang.Cloneable 接口。对于有些构造函数要执行很长时间的对象,在不需要改变初始化信息的情况下使用克隆可以大大的提高性能。
Java API文档 写道

实现了 Cloneable 接口,以指示 Object.clone() 方法可以合法地对该类实例进行按字段复制。
如果在没有实现 Cloneable 接口的实例上调用 Object 的 clone 方法,则会导致抛出 CloneNotSupportedException 异常。
按照惯例,实现此接口的类应该使用公共方法重写 Object.clone(它是受保护的)。
Object.clone 方法执行的是“浅复制”而不是“深复制”操作。


Java的浅复制与深复制概念,这里首先要理解Java的另一个概念,传值与传引用
1) 浅复制
浅复制的对象变量都含有和原来对象相同的值(这里变量指基本类型的变量,String类也算),但那些引用其他对象的变量仍然指向原来的对象。

2) 深复制
深复制的对象变量都含有和原来对象相同的值,但那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。

一个简单的浅复制代码事例,小狗复制成小猫
public class Food {
	public String name;
	public Food(String name) {
		this.name = name;
	}
}
//实现 java.lang.Cloneable 接口
public class Pet implements Cloneable {
	public String name;
	public int age;
	public List<Food> foods = new ArrayList<Food>();
	public Pet(String name, int age) {
		this.name = name;
		this.age = age;
	}
	@Override
	protected Pet clone()  {
		Pet copy = null;
		try {
			copy = (Pet) super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return copy;
	}
}
public class Test {
	
	public static void main(String[] args) {
		Pet source = new Pet("小狗", 2);
		source.foods.add(new Food("小狗爱吃狗粮"));
		Pet copy = source.clone();
		
		//修改前
		System.out.println("-----------值修改前-----------");
		System.out.println("source name: " + source.name);
		System.out.println(" source age: " + source.age);
		System.out.println("source food: " + source.foods.get(0).name);
		System.out.println("  copy name: " + copy.name);
		System.out.println("   copy age: " + copy.age);
		System.out.println("  copy food: " + copy.foods.get(0).name);
 
		System.out.println("-----------值修改后-----------");
		copy.name = "小狗变小猫";
		copy.age = 5;
		copy.foods.get(0).name = "小猫要吃猫粮";
		System.out.println("source name: " + source.name);
		System.out.println(" source age: " + source.age);
		System.out.println("source food: " + source.foods.get(0).name);
		System.out.println("  copy name: " + copy.name);
		System.out.println("   copy age: " + copy.age);
		System.out.println("  copy food: " + copy.foods.get(0).name);
	}
}

运行Test程序输出:
-----------值修改前-----------
source name: 小狗
 source age: 2
source food: 小狗爱吃狗粮
  copy name: 小狗
   copy age: 2
  copy food: 小狗爱吃狗粮

-----------值修改后-----------
source name: 小狗
 source age: 2
source food: 小猫要吃猫粮
  copy name: 小狗变小猫
   copy age: 5
  copy food: 小猫要吃猫粮


由此输出可见name与age字段都复制过来了,改变copy对象的值后source对象的值还是保持不变,但Pet对象中foods变量是一个List对象的引用,Object的clone方法是“浅复制”并没有复制变量引用的对象,因而copy对foods的修改就等于对source的修改。

要做到“深复制”在Java中有3种方式
1)所有对象都实现 java.lang.Cloneable 接口
2)利用Java的序列化和反序列化,要求所有对象都实现 java.io.Serializable 接口,如果有确实不可序列化的对象可使用 transient 关键字声明,从而将之排除在序列化之外
3)自己手动编码实现

你可能感兴趣的:(设计模式)