设计模式之原型模式

一、定义
通过原型实例指定创建对象的种类,并通过拷贝这些原型实例构建新的对象。(我觉简单的说,就是一种更快更省资源的创建新的
对象的方法)

二、实例
定义什么的总是难懂,还是上实例吧!
定义一个Ball类型,实现Cloneable接口,使得这个类的对象能够被克隆。

public class Ball implements Cloneable{
 public int id = -1;
 public String teststring ;
 public int[] num ;
 public ArrayList<String> list = new ArrayList<String>();
 public T t ; 
 @Override
 protected Object clone() throws CloneNotSupportedException {
  Ball ball = null ;
  try {
   ball = (Ball) super.clone();
  } catch (CloneNotSupportedException e) {
   e.printStackTrace();
  }
  return ball ;
 }
}

//T类型
class T{
 public int t = 0;
}
测试代码如下:
Ball ball = new Ball();
  ball.setId(0);
  for(int i=1;i<100;i++){
   try {
  Ball cloneball =  (Ball)ball.clone();
    System.out.println(cloneball.id);
   } catch (CloneNotSupportedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
  }

运行结果为:0

三、理论知识
3.1使用场景
原型模式很简单,基本上就是实现以下clone方法就好了,而且代码基本都是一样的。
3.2和new的区别
clone出来的对象是不经过构造函数构造的。也就是说Ball cloneball = (Ball)ball.clone()这句话没有用到构造函数就直接 定义了一个新的对象出来。
3.3clone出的对象的来源
clone方法是从内存中直接拷贝一个对象的数据到新开辟的内存空间中。

四、两种拷贝方式浅拷贝和深拷贝
4.1 区别
浅拷贝:在执行clone来创建对象的时候,新出来的对象其实是和原对象共享数组和引用的(除了基本数据类型)。
深拷贝:用clone生成对象后,这个对象是完全独立的,它的变量不会被其他clone出来的或者原型对象修改。

4.2实例
4.2.1浅拷贝:看下面的测试函数:
System.out.println("ball信息:");
  System.out.println("id:"+ball.id+" num:"+ball.num[0]+" "+ball.num[1]+" T:"+ball.t.t+" list[0]:"+ball.list.get(0)+" string:"+ball.teststring);
  System.out.println("cloneball信息:");
  System.out.println("id:"+cloneball.id+" num:"+cloneball.num[0]+" "+cloneball.num[1]+" T:"+cloneball.t.t+" list[0]:"+
    cloneball.list.get(0)+" string:"+cloneball.teststring);
  System.out.println("两者的teststring地址是否相同:"+(ball.teststring == cloneball.teststring));
  System.out.println("修改ball信息:");
  ball.id = 2;
  ball.num[0] = 2;
  ball.num[1] = 3;
  ball.t.t = 2;
  ball.list.remove(0);
  ball.teststring = "i am ball after change!" ;
  ball.list.add("i am the first string ball.list after change !");
  System.out.println("id:"+ball.id+" num:"+ball.num[0]+" "+ball.num[1]+" T:"+ball.t.t+" list[0]:"+ball.list.get(0)+" string:"+ball.teststring);
  System.out.println("此时cloneball的信息变化为:");
  System.out.println("id:"+cloneball.id+" num:"+cloneball.num[0]+" "+cloneball.num[1]+" T:"+cloneball.t.t+" list[0]:"+
    cloneball.list.get(0)+" string:"+cloneball.teststring);
    它的运行结果如下:
ball信息:
id:1 num:1 2 T:1 list[0]:i am the first string ball.list string:i am ball!
cloneball信息:
id:1 num:1 2 T:1 list[0]:i am the first string ball.list string:i am ball!
两者的teststring地址是否相同:true
修改ball信息:
id:2 num:2 3 T:2 list[0]:i am the first string ball.list after change ! string:i am ball after change!
此时cloneball的信息变化为:
id:1 num:2 3 T:2 list[0]:i am the first string ball.list after change ! string:i am ball!

仔细对比一下可以发现,修改ball中的基本数据类型变量id(int型)、teststring(string),不会导致cloneball中的
相应的变量改变,但是修改list(ArrayList)和num(int[])时就会导致cloneball中变量也改变。这就是浅拷贝。
(注意string类型的变量,在cloneball刚生成的时候里面的teststring和ball中的teststring是同一个东西,他们的地址是一样的,
但是一方的改变对另一方无影响,所以在这里把string也当做普通的数据类型处理)。
我觉得在堆里面的变量在克隆的时候都是共享的,而栈里面的东西都是会重新建立一个。

    4.2.1 深拷贝
        就是在拷贝对象的时候将原型类中所有的引用对象再拷贝一遍。clone方法改成:
protected Object clone() throws CloneNotSupportedException {
  Ball ball = null ;
  try {
   ball = (Ball) super.clone();
   ball.list = (ArrayList<String>) this.list.clone();
   ball.t = new T();
  } catch (CloneNotSupportedException e) {
   e.printStackTrace();
  }
  return ball ;
 }

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