设计模式(五):原型模式

原型模式的目的:解决创建重复对象的问题。

原型模式的原理:Java中Object类是所有类的根类,Object类提供了一个clone()方法,该方法可以将一个Java对象复制。但是需要实现clone的Java类必须实现Cloneable接口,该接口表示该类能够且具有复制的能力。

  1. 传统方式
    我们知道如果有一个对象A a,要想获取一个和a相同的对象,需要新建一个A b = new A(); 并且将a中的内容逐一传给b。而不能直接用A b = a; 因为在java中这样只是引用传递,并没有真正复制一个新的对象出来。

    public class Demo
    {
    	static class Person
    	{
        	int id;
        	int age;
        	String sex;
    
        	public Person(int id, int age, String sex)
        	{
            	super();
            	this.id = id;
            	this.age = age;
            	this.sex = sex;
        	}
    
        	@Override
        	public String toString()
        	{
            	return "Person [id=" + id + ", age=" + age + ", sex=" + sex + "]";
        	}
    	}
    
    	public static void main(String[] args)
    	{
        	Person person1 = new Person(1, 27, "male");
        	Person person2 = person1;   // 错误的拷贝方式,只是引用传递
        	Person person3 = new Person(person1.id, person1.age, person1.sex);
    
        	System.out.println("person1: " + person1.toString() + " hashCode:"
                	+ person1.hashCode());
        	System.out.println("person2: " + person2.toString() + " hashCode:"
                	+ person2.hashCode());
        	System.out.println("person3: " + person3.toString() + " hashCode:"
                	+ person3.hashCode());
    	}
    }
    

    运行结果为:

    person1: Person [id=1, age=27, sex=male] hashCode:118352462
    person2: Person [id=1, age=27, sex=male] hashCode:118352462
    person3: Person [id=1, age=27, sex=male] hashCode:1550089733
    

    我们可以看到这种方式,需要代码获取原对象的属性,才能创建新的对象。如果原对象比较复杂,那么这个过程将会痛苦而漫长。如果原对象类的属性有所修改,那么采用这种方式拷贝的代码也都需要修改。这就需要我们的原型模式。

  2. 原型模式(默认为浅拷贝)
    Person类实现Cloneable接口,并重载clone()方法。在复制对象时,只需要调用该对象的clone()方法即可。

    public class PrototypeDemo
    {
    	static class Person implements Cloneable
    	{
        	int id;
        	int age;
        	String sex;
    
        	public Person(int id, int age, String sex)
        	{
            	super();
            	this.id = id;
            	this.age = age;
            	this.sex = sex;
        	}
    
        	@Override
        	public String toString()
        	{
            	return "Person [id=" + id + ", age=" + age + ", sex=" + sex + "]";
        	}
    
        	@Override
        	protected Object clone() throws CloneNotSupportedException
        	{
            	return super.clone();
        	}
    	}
    
    	public static void main(String[] args)
    	{
        	Person person1 = new Person(1, 27, "male");
        	System.out.println("person1: " + person1.toString() + " hashCode:"
                	+ person1.hashCode());
        	try
        	{
            	Person person2 = (Person) person1.clone();		// 对象拷贝
            	System.out.println("person2: " + person2.toString() + " hashCode:"
                    	+ person2.hashCode());
        	}
        	catch (CloneNotSupportedException e)
        	{
            	e.printStackTrace();
        	}
    	}
    }
    

    运行结果:

    person1: Person [id=1, age=27, sex=male] hashCode:118352462
    person2: Person [id=1, age=27, sex=male] hashCode:1550089733
    

    该方法即为原型模式,对象拷贝方式简单,并且无论原对象的类属性是否变化,只需调用clone()方法,即可完成对象拷贝。但是该方式默认为浅拷贝,对于数据类型是基本类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象;对于数据类型是引用的成员变量,比如成员变量是其它的类对象,那么浅拷贝只能进行引用传递,也就是把该成员变量的引用值复制给新的对象,但两者实际上还是指向同一个实例。(关于浅拷贝和深拷贝,如有需求,需另找资料进行学习)。如果需要深拷贝,也就是对于数据类型是引用的成员变量,也都把引用的实例复制给新的对象,那么需要第3步实现。

  3. 深拷贝
    为验证原型模式为浅拷贝,修改原型模式代码,在Person中添加引用数据类型的成员变量friend。

    public class PrototypeDemo
    {
    	static class Person implements Cloneable
    	{
        	int id;
        	int age;
        	String sex;
        	Person friend;
    
        	public Person(int id, int age, String sex)
        	{
            	super();
            	this.id = id;
            	this.age = age;
            	this.sex = sex;
        	}
    
        	@Override
        	public String toString()
        	{
            	return "Person [id=" + id + ", age=" + age + ", sex=" + sex
                    	+ ", friend.hashCode=" + friend.hashCode() + "]";
        	}
    
        	@Override
        	protected Object clone() throws CloneNotSupportedException
        	{
            	return super.clone();
        	}
    	}
    
    	public static void main(String[] args)
    	{
        	Person person1 = new Person(1, 27, "male");
        	person1.friend = new Person(2, 27, "female");
        	System.out.println("person1: " + person1.toString() + " hashCode:"
                	+ person1.hashCode());
        	try
        	{
            	Person person2 = (Person) person1.clone();
            	System.out.println("person2: " + person2.toString() + " hashCode:"
                    	+ person2.hashCode());
        	}
        	catch (CloneNotSupportedException e)
        	{
            	e.printStackTrace();
        	}
    	}
    }
    

    运行结果:

    person1: Person [id=1, age=27, sex=male, friend.hashCode=118352462] hashCode:1550089733
    person2: Person [id=1, age=27, sex=male, friend.hashCode=118352462] hashCode:865113938
    

    我们可以看到person1和person2的hashCode不同,确实是两个对象,但是他们的friend.hashCode相同,说明在person1的拷贝过程中,person1的friend只是将其引用给了person2的friend,所以他俩还是指向了一个对象。深拷贝就是为了让person1.friend复制一个新的对象给person2.friend。
    实现深拷贝有两种方式,第一种方式是在clone()方法中添加代码实现类中对象的拷贝,第二种方式通过序列化和反序列化来实现深拷贝。我理解第二种方式不能叫做原型模式吧?下面通过示例说明

    1. 重写clone()方法
      @Override
      protected Object clone() throws CloneNotSupportedException
      {
          Person personClone = (Person)super.clone();
          if (friend != null)
          {
              personClone.friend = (Person)friend.clone();
          }
          return personClone;
      }
      
      这里只粘贴部分代码,因为我不舍得重新写代码,friend.clone()调用的还是这个函数,所以有一个递归,如果这里的friend还有friend,就会接着往下走。运行结果如下:
      person1: Person [id=1, age=27, sex=male, friend.hashCode=118352462] hashCode:1550089733
      person2: Person [id=1, age=27, sex=male, friend.hashCode=865113938] hashCode:1442407170
      
      搞定!
    2. 通过序列化和反序列化来实现
      import java.io.ByteArrayInputStream;
      import java.io.ByteArrayOutputStream;
      import java.io.IOException;
      import java.io.ObjectInputStream;
      import java.io.ObjectOutputStream;
      import java.io.Serializable;
      
      public class DeepCloneDemo
      {
      	static class Person implements Serializable
      	{
      		/**
       		* generated serial version ID
       		*/
      		private static final long serialVersionUID = 9079128548395795470L;
      		
      		int id;
      		int age;
      		String sex;
      		Person friend;
      
      		public Person(int id, int age, String sex)
      		{
          		super();
          		this.id = id;
          		this.age = age;
          		this.sex = sex;
      		}
      
      		@Override
      		public String toString()
      		{
          		return "Person [id=" + id + ", age=" + age + ", sex=" + sex
                  		+ ", friend.hashCode=" + friend.hashCode() + "]";
      		}
      
      		public Object deepClone() throws IOException, ClassNotFoundException
      		{
          		// 序列化操作
          		ByteArrayOutputStream bos = new ByteArrayOutputStream();
          		ObjectOutputStream oos = new ObjectOutputStream(bos);
          		oos.writeObject(this);
          		oos.flush();
          
          		// 反序列化操作
          		ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
          		ObjectInputStream ois = new ObjectInputStream(bis);
          		Person personClone = (Person)ois.readObject();
          
          		return personClone;
      		}
      	}
      
      	public static void main(String[] args)
      	{
      		Person person1 = new Person(1, 27, "male");
      		person1.friend = new Person(2, 27, "female");
      		System.out.println("person1: " + person1.toString() + " hashCode:"
              		+ person1.hashCode());
      		try
      		{
          		Person person2 = (Person) person1.deepClone();
          		System.out.println("person2: " + person2.toString() + " hashCode:"
                  		+ person2.hashCode());
      		}
      		catch (ClassNotFoundException e)
      		{
          		e.printStackTrace();
      		}
      		catch (IOException e)
      		{
          		e.printStackTrace();
      		}
      	}
      }
      
      运行结果:
      person1: Person [id=1, age=27, sex=male, friend.hashCode=118352462] hashCode:1550089733
      person2: Person [id=1, age=27, sex=male, friend.hashCode=1831932724] hashCode:1747585824
      
      搞定,收工!对于序列化的了解不足,就不先在这多解释了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值