从克隆到原型模式

来源:互联网 发布:淘宝网店认证照片 编辑:程序博客网 时间:2024/06/05 19:46

<!-- gte="" mso="" 9=""><![endif]--><!-- gte="" mso="" 9=""><![endif]--><!-- gte="" mso="" 10="">

从克隆到原型模式

预备知识回顾

1 我们知道,类成员的访问控制修饰符有public , protected , private,它们形成了四种控制级别:

public :能访问该类的任何地方可以访问public成员。

protected :该类所属包内的类或该类的派生类可以访问protected成员。

无修饰符 :该类所属包内的类可以访问无修饰符成员。

private: 该类本身能访问private成员。

 

2 对于protected和默认成员,有一些容易误解的地方,请看下面的代码

 

package test;  //test

public class Base {

protectedint i=0;

}

 

package test1;  //test1

import test.Base ;

public class Sub extends Base{

 

publicstatic void main(String[] args) {

Sub sub = new Sub();

System.out.println(sub.i); //正确 ,派生类中继承了基类的protected成员,派生类中能访问派生类对象所继承的protected成员

Base base = new Base();

System.out.println(base.i); //编译错误 , protected成员不可见

 

    }

 

}

 

3 通过以上代码,可以得出结论:在生成派生类时,派生类可以继承基类的protected普通成员,这个继承的protected普通成员在派生类内部是可以访问的但是在派生类内部无法直接访问基类对象的protected普通成员

 

4 再看一个例子,一种派生类内部只能访问该种派生类的对象继承的基类protected普通成员,不能访问基类的其他派生类对象继承的基类protected普通成员。请看代码:

 

 

 

package test;  //test

public class Base {

protected int i=0;

}

 

 

 

package test1;  //test1

import test.Base;

public class Sub extends Base {

public static void main(String [] args) {

Sub sub = new Sub();

Derive derive = new Derive();

System.out.println(sub.i); // 正确

System.out.println(derive.i); //编译错误

 

       }

}

class Derive extends Base {

}

 

 

5.关于继承

A  extends B意味着:

1、如果AB同包,A继承Bpublicprotected和默认级别的成员方法和属性;

2、如果AB不通包,A继承Bpublicprotected级别的成员方法和属性

 

6.稍微总结一下

1AB包,都继承自同包的C,在A中,可以:

1、用C访问Cprotected和默认级别属性和方法;

2AC赋值访问Cprotected和默认级别属性和方法(多态);

3、实例化A访问Cprotected和默认级别属性和方法;

4、实例化B访问Cprotected和默认级别属性和方法;

5BC赋值在访问Cprotected和默认级别属性和方法(多态);

 

2AB同包,都继承自不同包的C,在A中,可以:

1、用C访问Cprotected的静态方法和属性(类和对象都可以),非静态的无法访问;默认级别的属性和方法都无法访问;

2AC赋值在访问Cprotected的静态方法和属性(类和对象都可以),非静态的无法访问(所以没有多态);默认级别的属性和方法都无法访问;

3、实例化A访问Cprotected级别属性和方法;默认级别的属性和方法都无法访问;

4、实例化B访问Cprotected级别静态属性和方法(类和对象都可以),非静态的无法访问;默认级别的属性和方法都无法访问。如果B本身自己有protected和默认级别的方法和属性,A可以访问B的这些属性和方法,原理与上面的第4条相似;

5BC赋值访问Cprotected级别静态属性和方法(类和对象都可以),非静态的无法访问;默认级别的属性和方法都无法访问。如果B本身自己有protected和默认级别的方法和属性,A也无法访问B的这些属性和方法;

 

3ABC都两两不同包,B继承自C,在A中访问B,可以:

1、实例化B访问Cprotected级别和默认级别的属性和方法都无法访问;

2、如果A继承自B,实例化A访问C,可以访问protected级别的属性和方法(如果B自己拥有同名的属性和方法,则覆盖);默认级别的属性和方法无法访问;

3、如果A继承自B,实例化B访问C,可以访问protected级别的静态属性和方法,非静态的无法访问(如果B自己拥有同名的属性和方法,则静态属性和方法覆盖,因为普通方法无法访问);默认级别的属性和方法无法访问;

4、如果A继承自BAB赋值访问C,可以访问protected级别的静态属性和方法,非静态的无法访问(如果B自己拥有同名的属性和方法,则静态属性和方法覆盖,因为普通方法无法访问;而且静态属性和方法不是动态绑定,所以最终访问的是B的静态属性和方法,即使A有同名的属性和方法);默认级别的属性和方法无法访问。

<!-- gte="" mso="" 9=""><![endif]--><!-- gte="" mso="" 9=""><![endif]--><!-- gte="" mso="" 10="">

原型模式

允许根据一个对象创建另一个可以定制的对象,而无需知道创建的细节。

 

 

1.     基本知识:比较相等

 

(1)对于8种基本数据类型只能使用==。

(2)对于基本类型的包装类(BooleanIntegerFloatLongDouble)和String类型,==比较的是地址,equals比较的是内容。

(3)一般对象,equals是根据equals()方法比较(如果没有覆盖equals()方法,那么equls==等价,这是Objectequals()方法

publicbooleanequals(Object obj) {

        return (this == obj);

  }

如果覆盖了equals()方法,则根据实际内容比较)。

(4)如果覆写equals()方法,一定要满足:自反性、传递性、一致性(如果参与比较的对象没有任何改变,那么比较的结果也不应该有任何改变)、非空性(任何非空的X引用值,都有X.equals(null)一定为false)。

 

2.     关于clone()方法

 

Object顶级类提供了clone()方法,但是考虑到安全性,它的访问级别是protected的,另外还强制要求子类必须要实现Cloneable接口(否则会抛出CloneNotSupported-Exception,其实此接口是一个标记接口,没有任何方法。

 

网上说,克隆要求:

①为了获取对象的一份拷贝,我们可以利用Object类的clone()方法。
在派生类中覆盖基类的clone()方法,并声明为public为什么?)。
在派生类的clone()方法中,调用super.clone()为什么?)。
在派生类中实现Cloneable接口(为什么?)。

 

也就是说,一个类需要克隆,必须覆盖clone()方法(还是public)和实现Cloneable接口。

为什么必须覆盖clone()方法?(见后面)

 

下面看一个例子

publicclass TestClone {

   publicstaticvoid main(String[] args) {

     MyClone myClone1 = new MyClone("clone1");

 

     MyClone myClone2 = (MyClone)myClone1.clone();

 

     if (myClone2 !=null) {

       System.out.println(myClone2.getName());//true

       System.out.println("Clone2 equals Clone1: "

             +myClone2.equals(myClone1));//false

     } else {

       System.out.println("不支持克隆");

     }

   }

 

}

 

class MyCloneimplements Cloneable{//如果没有实现,则……

   private Stringname;

 

   public MyClone(String name) {

     this.name = name;

   }

 

   public String getName() {

     returnname;

   }

 

   publicvoid setName(String name) {

     this.name = name;

   }

 

   /**

    * 实现的克隆方法

    */

   public Object clone() {

     try {

       /**

        * 常用的克隆调用方法

         */

       returnsuper.clone();

     } catch (CloneNotSupportedException e) {

       returnnull;

     }

   }

}

这样,调用Objectclone()方法可以成功。

注意:System.out.println("Clone2equals Clone1: "

                                  +myClone2.equals(myClone1));

打印false,克隆出的对象和源对象内存空间不同。

但是为什么要实现cloneable接口,覆盖clone()方法?

 

再看一个例子,注意Prototype是一个接口

/**

 *原型模式接口

 */

publicinterface Prototype {

   Prototype clone();

}

 

下面的两个类实现了接口,自然实现了clone()方法,但是没有实现Cloneable接口

 

package cn.steven.pattern.demo.prototype;

 

/**

 *实现了原型模式的简单类

 */

publicclass SampleClassimplements Prototype {

   privateinti;

 

   publicint getI() {

     returni;

   }

 

   publicvoid setI(int i) {

     this.i = i;

   }

   public SampleClass(int i) {

     super();

     this.i = i;

   }

 

   @Override

   public Prototype clone() {

     returnnew SampleClass(this.getI());

   }

 

   @Override

   public String toString() {

     return Integer.toString(this.getI());

   }

 

}

 

import java.util.List;

 

/**

 *实现了原型模式的复杂类

 */

publicclass ComplexClassimplements Prototype {

   private Stringstr;

   private Listlist;

   public String getStr() {

     returnstr;

   }

   publicvoid setStr(String str) {

     this.str = str;

   }

 

   public List getList() {

     returnlist;

   }

   publicvoid setList(List list) {

     this.list = list;

   }

   @Override

   public Prototype clone() {

     ComplexClass cc = new ComplexClass();

     cc.setList(this.getList());

     cc.setStr(this.getStr());

     return cc;

   }

   @Override

   public String toString() {

     returnthis.getStr() +"" +this.getList();

   }

 

}

 

客户代码

import java.util.List;

import java.util.ArrayList;

publicclass Client_1 {

   publicstaticvoid main(String[] args) {

 

     /**

      * 克隆简单对象

      */

     SampleClass sc = new SampleClass(99);

     Prototype scc = sc.clone();

 

     /**

      * 判断对象是否是相同的指针

      */

     System.out.println("sc==scc ?" + (sc == scc));

     /**

      * 判断值是否相等

      */

     System.out.println("sc: " + sc.toString());

     System.out.println("scc: " + scc.toString());

 

     /**

      * 克隆复杂对象

      */

     ComplexClass cc = new ComplexClass();

     cc.setStr("一个字符串");

     List list = new ArrayList();

     list.add("字串");

     list.add(25);

     cc.setList(list);

     

     Prototype ccc = cc.clone();

     

     /**

      * 判断对象是否是相同的指针

      */

     System.out.println("cc==ccc ?" + (cc == ccc));

     /**

      * 判断值是否相等

      */

     System.out.println("cc: " + cc.toString());

     System.out.println("ccc: " + ccc.toString());

   }

}

结果:

sc==scc ? false

sc: 99

scc: 99

cc==ccc ? false

cc:一个字符串 [字串, 25]

ccc:一个字符串 [字串, 25]

 

上面两个类都实现了自定义接口的clone()方法,但是没有实现Cloneable接口,也同样实现了克隆的功能。难道网上说得有误?看API

<!-- gte="" mso="" 9=""><![endif]--><!-- gte="" mso="" 9=""><![endif]--><!-- gte="" mso="" 10=""><![endif]-->

文档API

 

protectednative Object clone()throws

          CloneNotSupportedException;  

/**

  ………………………………………………


    * The class <tt>Object</tt> does notitself implement the interface

    * <tt>Cloneable</tt>, so calling the<tt>clone</tt> method on an object

    * whose class is <tt>Object</tt>will result in throwing an

    * exception at run time.

    *

    * @return     a clone of this instance.

    * @exception  CloneNotSupportedException if the object's class does not

    *               support the<code>Cloneable</code> interface. Subclasses

    *               that override the<code>clone</code> method can also

    *               throw this exception to indicatethat an instance cannot

    *               be cloned.

    * @seejava.lang.Cloneable

 

Cloneable

/**

 * A class implements the<code>Cloneable</code> interface to

 * indicate to the {@linkjava.lang.Object#clone()} method that it

 * is legal for that method tomake a

 * field-for-field copy ofinstances of that class.

 * <p>

 *Invoking Object's clone method on an instancethat does not implement the

 * <code>Cloneable</code> interfaceresults in the exception

 * <code>CloneNotSupportedException</code>being thrown.

 * <p>

 *Byconvention, classes that implement this interface should override

 *<tt>Object.clone</tt> (which isprotected) with a public method.

 * See {@linkjava.lang.Object#clone()} for details on overriding this

 * method.

 * <p>

 * Note that this interface does<i>not</i> contain the <tt>clone</tt> method.

 * Therefore, it is not possibleto clone an object merely by virtue of the

 * fact that it implements thisinterface. Even if the clone method isinvoked

 * reflectively, there is noguarantee that it will succeed.

 

clone()方法是native的,native是什么东西?

网上资源:如果一个含有本地native方法的类被继承,子类会继承这个本地方法并且可以用java语言重写这个方法(这个似乎看起来有些奇怪),同样的如果一个本地方法被final标识,它被继承后不能被重写。

看了这么多,我的理解是:

 

native方法可以被继承和覆写。Objectclone()方法就是native的,但是直接子类也可以使用。API说,如果子类没有实现Cloneable,调用clone()会抛出异常,或者说在一个对象上调用Objectclone()方法,如果它没有实现Cloneable接口,会有异常。传统上(就是约定)classes that implement thisinterface should override  <tt>Object.clone</tt> (which is protected) with apublic method不是说必须啊。没有说覆写clone()方法但是不调用Objectclone()方法也有以异常啊。而且只是约定实现Cloneable接口的类应该(不是一定,有可能实现Cloneable接口,但是不覆写clone()方法)用public级别的方法覆盖clone()方法。

 

但是,我错了,看下面的例子

publicclass TestClone {

   publicstaticvoid main(String[] args) {

     MyClone myClone1 = new MyClone("clone1");

 

     MyClone myClone2 = (MyClone) myClone1.clone();//出错,clone()方法不可见

     if (myClone2 !=null) {

        System.out.println(myClone2.getName());

       System.out.println("Clone2equals Clone1: "

             + myClone2.equals(myClone1));

     } else {

       System.out.println("不支持克隆");

     }

   }

 

}

 

class MyCloneimplements Cloneable{

   private Stringname;

 

   public MyClone(String name) {

     this.name = name;

   }

 

   public String getName() {

     returnname;

   }

 

   publicvoid setName(String name) {

     this.name = name;

 

   }    

//注释掉克隆方法

/**

   public Object clone() {

     try {

       /**

        * 常用的克隆调用方法

        */

       return super.clone();

     } catch (CloneNotSupportedException e) {

       return null;

     }

   }

*/

}

出错,因为Objectclone()方法是protected级别的方法(不是静态方法),clone()方法虽然可以继承,但是无法在其他对象中访问,clone()方法不可见(TestClone类只能访问自己类继承的protected属性和方法,可以参见前面预备知识回顾)。而我们一般是在外部克隆一个对象(只有被克隆的对象实现了Cloneable接口),自己不会克隆自己。所以必须覆写clone()方法,因为这时是被克隆的对象自己的clone()方法,而不是继承来的。至于为什么约定是public级别的,主要是可以供外部调用(protected级别的只能同包和子类使用,满足条件时protected级别也可以;当然不能比protected级别低)。

 

另外,覆盖Objectclone()方法,但是没有调用Objectclone()方法,也是没有问题的:

publicclass TestClone {

   publicstaticvoid main(String[] args) {

     MyClone myClone1 = new MyClone("clone1");

 

     Object  myClone2 =myClone1.clone();

 

       System.out.println("克隆的对象"

             + myClone2.toString());

     

   }

 

}

 

class MyClone {

   private Stringname;

 

   public MyClone(String name) {

     this.name = name;

   }

 

 

   protected Object clone() {

     returnnew String("hello");

   }

}

上面不会抛异常的。但是我们clone是有目的和有意的,所以实际肯定不会这么做,所以网络上的说法有可行之处:

 

①为了获取对象的一份拷贝,我们可以利用Object类的clone()方法。
在派生类中覆盖基类的clone()方法,并声明为publicprotected也没错)

在派生类的clone()方法中,调用super.clone()这才是目的)。
在派生类中实现Cloneable接口(调用了Object#clone()方法)。

还是前面的例子:

publicclass TestClone {

   publicstaticvoid main(String[] args) {

     MyClone myClone1 = new MyClone("clone1");

 

     MyClone myClone2 = (MyClone) myClone1.clone();

 

     if (myClone2 !=null) {

       System.out.println(myClone2.getName());

       System.out.println("Clone2equals Clone1: "

             + myClone2.equals(myClone1));

     } else {

       System.out.println("不支持克隆");

     }

   }

 

}

 

class MyCloneimplements Cloneable{

   private Stringname;

 

   public MyClone(String name) {

     this.name = name;

   }

 

   public String getName() {

     returnname;

   }

 

   publicvoid setName(String name) {

     this.name = name;

   }

 

  

   protected Objectclone() {

     try {

       /**

        * 常用的克隆调用方法

        */

       returnsuper.clone();

     } catch (CloneNotSupportedException e) {

       returnnull;

     }

   }

}

<!-- gte="" mso="" 9=""><![endif]--><!-- gte="" mso="" 9=""><![endif]--><!-- gte="" mso="" 10=""><![endif]-->

这下明白多了。

<!-- gte="" mso="" 9=""><![endif]--><!-- gte="" mso="" 9=""><![endif]--><!-- gte="" mso="" 10=""><![endif]-->

深浅克隆

克隆出的对象与源对象是不是同一个对象呢?

浅克隆:默认的都是浅克隆,只是克隆了一个新对象,但是对象的对象属性(不是基本属性)仍然是原来的。如

publicclass TestClone {

   publicstaticvoid main(String[] args) {

     MyClone myClone1 = new MyClone("clone1");

 

     MyClone myClone2 = (MyClone) myClone1.clone();

 

      if (myClone2 !=null) {

       System.out.println(myClone2.getName()==myClone1.getName());//true

       System.out.println(myClone2.getInt()==myClone1.getInt());

       System.out.println("Clone2equals Clone1: "

             + myClone2.equals(myClone1));

     } else {

       System.out.println("不支持克隆");

     }

   }

 

}

 

class MyCloneimplements Cloneable{

   private Stringname;

   privateinti =2;

 

   public MyClone(String name) {

     this.name = name;

   }

   publicint getInt(){

    returni;

   }

 

   public String getName() {

     returnname;

   }

 

   publicvoid setName(String name) {

     this.name = name;

   }

   protected Object clone() {

     try {

       returnsuper.clone();

     } catch (CloneNotSupportedException e) {

       returnnull;

     }

   }

}

发现String类型的属性并未克隆(实际上所有对象属性默认都没有克隆),但是由于String不可变类,线程安全,所以可以共享。但是对已一般的属性,要实现共享,就要深克隆

 

1、可变类:当你获得这个类的一个实例引用时,你可以改变这个实例的内容。
2
、不可变类:当你获得这个类的一个实例引用时,你不可以改变这个实例的内容。不可变类的实例一但创建,其内在成员变量的值就不能被修改。

<!-- gte="" mso="" 9=""><![endif]--><!-- gte="" mso="" 9=""><![endif]--><!-- gte="" mso="" 10=""><![endif]-->

不可变类

不可变类,需要涉及到final

final成员

当你在类中定义变量时,在其前面加上final关键字,那便是说,这个变量一旦被初始化便不可改变,这里不可改变的意思对基本类型来说是其不可变,而对于对象变量来说其引用不可再变。

 

其初始化可以在两个地方,一是其定义处,二是在构造函数中,两者只能选其一。一般的final字段要初始化要么声明时初始化,要么在构造方法中初始化,否则出错。而对于staticfinal字段,只能在声明时初始化。否则出错下面程序很简单的演示了final的常规用法:

 

public class Test{    

final int t = 1; // 在定义时给值     

// 或者(两者只能选其一)     

final int t;    

public Test(){

               t = 3; // 构造时给值

}

}

 

还有一种用法是定义方法中的参数为final

对于基本类型的变量,这样做并没有什么实际意义,因为基本类型的变量在调用方法时是传值的,也就是说你可以在方法中更改这个参数变量而不会影响到调用语句。

然而对于对象变量,却显得很实用,因为对象变量在传递时是传递其引用,这样你在方法中对对象变量的修改也会影响到调用语句中的对象变量,当你在方法中不需要改变作为参数的对象变量时,明确使用final进行声明,会防止你无意的修改而影响到调用方法。

另外方法中的内部类在用到方法中的参变量和局部变量时,此参变和局部变量也必须声明为final才可使用,如下代码所示:

public class Test{   

   void print(final String str){

       class InnerTest{          

           InnerTest (){                

               System.out.println(str);            

            }        

       }        

       InnerTest it=new InnerTest ();    

   }  

   public staticvoid main(String[] args){      

      Test test=newTest();      

      test.print("Helloword!!!"); 

   }

}

final方法

将方法声明为final那有两个原因,第一就是说明你已经知道这个方法提供的功能已经满足你要求,不需要进行扩展,并且也不允许任何从此类继承的类来覆写这个方法,但是继承仍然可以继承这个方法,也就是说可以直接使用。第二就是允许编译器将所有对此方法的调用转化为inline(行内)调用的机制,它会使你在调用final方法时,直接将方法主体插入到调用处,而不是进行例行的方法调用,例如保存断点,压栈等,这样可能会使你的程序效率有所提高,然而当你的方法主体非常庞大时,或你在多处调用此方法,那么你的调用主体代码便会迅速膨胀,可能反而会影响效率,所以你要慎用final进行方法定义。

 

final

当你将final用于类身上时,你就需要仔细考虑,因为一个final类是无法被任何人继承的,那也就意味着此类在一个继承树中是一个叶子类,并且此类的设计已被认为很完美而不需要进行修改或扩展。对于final类中的成员,你可以定义其为final,也可以不是final。而对于方法,由于所属类为 final的关系,自然也就成了final型的。你也可以明确的给final类中的方法加上一个final,但这显然没有意义。

 

缺省情况下,一个类所有的non-private,non-static函数都可以被子类所覆盖(over
ride)
,那么如何做才能阻止子类覆盖某个方法呢?答案是将该方法声明为final

所以final类并不能保证类是不可变的。final关键字到底修饰了什么?

final
使得被修饰的变量"不变",但是由于对象型变量的本质是引用,使得不变也有了两种含义:引用本身的不变,和引用指向的对象不变。

引用本身的不变:
final StringBuffer a=new StringBuffer("immutable");
final StringBuffer b=new StringBuffer("not immutable");
a=b;//
编译期错误

引用指向的对象不变:
final StringBuffer a=new StringBuffer("immutable");
a.append(" broken!"); //
编译通过

可见,final只对引用的”(也即它所指向的那个对象的内存地址)有效,它迫使引用只能指向初始指向的那个对象,改变它的指向会导致编译期错误。至于它所指向的对象的变化,final是不负责的。这很类似==操作符:==操作符只负责引用的相等,至于这个地址所指向的对象内容是否相等,==作符是不管的。


理解final问题有很重要的含义。final只能保证引用永远指向固定对象,不能保证那个对象的状态不变。在多线程的操作,一个对象会被多个线程共享或修改,一个线程对对象无意识的修改可能会导致另一个使用此对象的线程崩溃。一个错误的解决方法就是在此对象新建的时候把它声明为final,意图使得它永远不变。其实那是徒劳的。

 

  在JDK 1.5的基本类库中,对一些不可变类,如Integer类做了优化,它具有一个实例缓存,用来存放程序中经常使用的Integer实例。JDK 1.5Integer类新增了一个参数,为int类型的静态工厂方法valueOfint i),它的处理流程如下:

  if(在实例缓存中存在取值为i的实例)

     直接返回这个实例

  else{

     new语句创建一个取值为iInteger实例

     把这个实例存放在实例缓存中

     返回这个实例

  }

  在以下程序代码中,分别用new语句和Integer类的valueOfint i)方法来获得Integer实例。

  Integer a=new Integer10);

  Integer b=newInteger10);

  Integerc=Integer.valueOf10);

  Integer d= Integer.valueOf10);

  System.out.printlna==b); //打印false

  System.out.printlna==c); //打印false

  System.out.printlnc==d); //打印true

  以上代码共创建了3Integer对象,每个new语句都会创建一个新的Integer对象。而Integer.valueOf10)方法仅在第一次被调用时,创建取值为10Integer对象,在第二次被调用时,直接从实例缓存中获得它。由此可见,在程序中用valueOf()静态工厂方法获得Integer对象,可以提高Integer对象的可重用性。

  到底如何实现实例的缓存呢?缓存并没有固定的实现方式,完善的缓存实现不仅要考虑何时把实例加入缓存,还要考虑何时把不再使用的实例从缓存中及时清除,以保证有效合理地利用内存空间。一种简单的实现是直接用Java集合来作为实例缓存。

  下面的例程,它拥有实例缓存和相应的静态工厂方法valueOf()Name类的实例缓存中可能会加入大量Name对象,为了防止耗尽内存,在实例缓存中存放的是Name对象的软引用(SoftReference)。如果一个对象仅仅持有软引用,Java虚拟机会在内存不足的情况下回收它的内存。

import java.util.Set;

import java.util.HashSet;

import java.util.Iterator;

import java.lang.ref.*;

public class Name {

……

//实例缓存,存放Name对象的软引用

private static finalSet<SOFTREFERENCE>  
                      names=newHashSet<SOFTREFERENCE>();

//静态工厂方法
public static Name valueOf(String firstname, String lastname){
                                       
  Iterator<SOFTREFERENCE> it=names.iterator();
  while(it.hasNext()){
       SoftReference  ref=it.next();

Name name=ref.get();//获得软引用所引用的Name对象
      if(name!=null&&name.firstname.equals(firstname)&&
                           name.lastname.equals(lastname))
               return name;
 }
 //
如果在缓存中不存在Name对象,就创建该对象,并把它加入到实例缓存
 Name name=new Name(firstname,lastname);
 names.add(new SoftReference(name));
 return name;
}
public static void main(String args[]){
  Name n1=Name.valueOf("
小红","");
  Name n2=Name.valueOf("
小红","");
  Name n3=Name.valueOf("
小东","");
  System.out.println(n1);
  System.out.println(n2);
  System.out.println(n3);
  System.out.println(n1==n2); //
打印true
 }
 }

 

  在程序中,既可以通过new语句创建Name实例,也可以通过valueOf()方法创建Name实例。在程序的生命周期中,对于程序不需要经常访问的Name实例,应该使用new语句创建它,使它能及时结束生命周期;对于程序需要经常访问的Name实例,那就用valueOf()方法来获得它,因为该方法能把Name实例放到缓存中,使它可以被重用。

  从例程11-12Name类也可以看出,在有些情况下,一个类可以同时提供public的构造方法和静态工厂方法。用户可以根据实际需要,灵活地决定到底以何种方式获得类的实例。

  另外要注意的是,没有必要为所有的不可变类提供实例缓存。随意创建大量实例缓存,反而会浪费内存空间,降低程序的运行性能。通常,只有满足以下条件的不可变类才需要实例缓存。

  1.不可变类的实例的数量有限。

  2.在程序运行过程中,需要频繁访问不可变类的一些特定实例。这些实例拥有与程序本身同样长的生命周期。

 

如何创建一个自己的不可变类:
      .
所有成员都是private
      .
不提供对成员的改变方法,例如:setXXXX
      .
确保所有的方法不会被重载。手段有两种:使用final(强不可变类),或者将所有类方法加上final(弱不可变类)
      .
如果某一个类成员不是原始变量(primitive)或者不可变类,必须通过在成员初始化(in)或者get方法(out)时通过深clone()方法,来确保类的不可变。

 

正题:深克隆

 

package cn.steven.pattern.demo.prototype.pattern;

 

import java.util.ArrayList;

import java.util.List;

 

/**

 *测试深克隆

 */

publicclass TestClone2 {

   publicstaticvoid main(String[] args) {

     //源对象

     DeepClone dc = new DeepClone();

     dc.getList().add(new SomeThing("一个对象"));

     //克隆对象

     DeepClone dcc = (DeepClone) dc.clone();

// 判断对象是否为同一对象

     System.out.println("DeepClone是否为同一对象:" + (dc == dcc));//false

     System.out.println("DeepClone::list是否为同一对象:"

          + (dc.getList() == dcc.getList()));

//打印false,因为DoSomething实现了Cloneable并且覆盖了clone()方法,所以是深克隆;如果注释下面黄颜色的代码,就打印true    System.out.println("DeepClone::list[0]是否为同一对象:"+

          (dc.getList().get(0) == dcc.getList().get(0)));

   }

}

 

class DeepCloneimplements Cloneable {

 

   /**

    * 一个集合

    */

   List<SomeThing> list =new ArrayList<SomeThing>();

 

   public List<SomeThing> getList() {

     returnlist;

   }

publicvoid setList(List<SomeThing> list) {

     this.list = list;

   }

   public Object clone() {

     try {

       DeepClone dc = (DeepClone)super.clone();

       dc.setList(newArrayList<SomeThing>());

       for (SomeThingsomeThing : list) {

          dc.getList().add((SomeThing)someThing.clone());

       }

       return dc;

     } catch (CloneNotSupportedException e) {

       returnnull;

     }

   }

}

class SomeThingimplements Cloneable {

   private Stringname;

 

   public String getName() {

     returnname;

   }

 

   publicvoid setName(String name) {

     this.name = name;

   }

 

   public SomeThing(String name) {

     super();

     this.name = name;

   }

  

   public Object clone() {

     try {

       returnsuper.clone();

     } catch(CloneNotSupportedException e) {

       returnnull;

     }

  }

}

 

<!-- gte="" mso="" 9=""><![endif]--><!-- gte="" mso="" 9=""><![endif]--><!-- gte="" mso="" 10=""><![endif]--><!-- gte="" mso="" 9=""><![endif]--><!-- gte="" mso="" 9=""><![endif]--><!-- gte="" mso="" 10=""><![endif]--><!-- gte="" mso="" 9=""><![endif]--><!-- gte="" mso="" 9=""><![endif]--><!-- gte="" mso="" 10=""><![endif]-->

 

 

当初有些问题不太明白,自己就试验了一下,查找了一些书籍和网上资料,具体的网址不记得了。

原创粉丝点击