Java克隆(Clone)的应用

  Java克隆(Clone)是 Java语言的特性之一,但在实际中应用比较少见。但有时候用克隆会更方便更有效率。

    对于克隆(Clone),Java有一些限制:
    1、被克隆的类必须自己实现Cloneable 接口,以指示 Object.clone() 方法可以合法地对该类实例进行按字段复制。Cloneable 接口实际上是个标识接口,没有任何接口方法。
    2、实现Cloneable接口的类应该使用公共方法重写 Object.clone(它是受保护的)。某个对象实现了此接口就克隆它是不可能的。即使 clone 方法是反射性调用的,也无法保证它将获得成功。
    3、在Java.lang.Object类中克隆方法是这么定义的:
    protected Object clone()
                    throws CloneNotSupportedException
    创建并返回此对象的一个副本。表明是一个受保护的方法,同一个包中可见。
    按照惯例,返回的对象应该通过调用 super.clone 获得。


    引题:

    举个例子说吧,现在有一个对象比如叫foo,你需要在创建当前对象的一个副本作为存根你能怎么做?

    假如你不用Clone,那么你可以先new一个对象foo1:Foo foo1=new Foo(),然后用foo给foo1对象set值,这样就得到foo的副本foo1;除此之外,别无选择。

    这样说,也许有人会觉得说的过于绝对了,不过事实如此啊。

    要产生一个副本,那副本要不要内存?----当然要了,那就对了!既然需要内存,(不克隆的情况下)你不new还有什么办法呢?请大家时刻铭记对象是Java运行时产生的,驻留在计算机内存中。

    常见错误:
    下面我澄清几个初学者容易犯迷糊的错误,同样的问题,产生foo对象的副本:
    1、Foo foo1=new Foo();
       foo1=foo;
       然后就想当然的认为副本foo1生成了!

    错误原因:foo1没错是申请了内存,但是执行foo1=foo后,foo1就不在指向刚申请的内存区域了,转而指向foo对象的内存区域,这时候,foo1、foo指向了同一内存区域。刚才new的操作制造一堆垃圾等着JVM回收。

    2、Foo foo1=foo;
    错误原因:还是两个变量都指向了同一块内存。

    3、有些老鸟更厉害一些:在Foo中定义一个返回自身的方法:
        public Foo getInstance(){
            return this;
        }

        然后,Foo foo1=foo.getInstance();

    错误原因:同上,主要还是没有重新开辟内存,this在对象里是什么?----就是对象自己的引用!那么getInstance()自然返回的就是对象自己,反正又是两个对象穿了一条裤子----***,哈哈。错得心服口服吧。为了节省篇幅,我在最后写个例子,留给那些对此有异议的人看。

    引入克隆

    看了这么多方法都不行,还很麻烦!干脆用克隆吧,简单明了。

    废话不说了,看例子:
    定义两个类CloneFooA、CloneFooB,然后写个测试类CloneDemo分别克隆这两个类的对象,然后打印测试结果到控制台。


   

 1 ExpandedBlockStart.gif   /**
 2InBlock.gif     * 简单类克隆实现
 3InBlock.gif     * 要实现克隆,必须实现Cloneable接口,这是一个标识接口,没有接口方法
 4InBlock.gif     * 实现了 Cloneable 接口,以指示 Object.clone() 方法可以合法地对该类实例进行按字段复制。
 5InBlock.gif     * 按照惯例,实现此接口的类应该使用公共方法重写 Object.clone(它是受保护的)。
 6ExpandedBlockEnd.gif     */

 7 ExpandedBlockStart.gif     public   class  CloneFooA  implements  Cloneable  {
 8InBlock.gif        private String strA;
 9InBlock.gif        private int intA;
10InBlock.gif
11ExpandedSubBlockStart.gif        public CloneFooA(String strA, int intA) {
12InBlock.gif            this.strA = strA;
13InBlock.gif            this.intA = intA;
14ExpandedSubBlockEnd.gif        }

15InBlock.gif
16ExpandedSubBlockStart.gif        public String getStrA() {
17InBlock.gif            return strA;
18ExpandedSubBlockEnd.gif        }

19InBlock.gif
20ExpandedSubBlockStart.gif        public void setStrA(String strA) {
21InBlock.gif            this.strA = strA;
22ExpandedSubBlockEnd.gif        }

23InBlock.gif
24ExpandedSubBlockStart.gif        public int getIntA() {
25InBlock.gif            return intA;
26ExpandedSubBlockEnd.gif        }

27InBlock.gif
28ExpandedSubBlockStart.gif        public void setIntA(int intA) {
29InBlock.gif            this.intA = intA;
30ExpandedSubBlockEnd.gif        }

31InBlock.gif
32ExpandedSubBlockStart.gif        /**
33InBlock.gif         * @return 创建并返回此对象的一个副本。
34InBlock.gif         * @throws CloneNotSupportedException
35ExpandedSubBlockEnd.gif         */

36ExpandedSubBlockStart.gif        public Object clone() throws CloneNotSupportedException {
37InBlock.gif            //直接调用父类的clone()方法,返回克隆副本
38InBlock.gif            return super.clone();
39ExpandedSubBlockEnd.gif        }

40ExpandedBlockEnd.gif    }

41 None.gif
42 None.gif
 1 ExpandedBlockStart.gif /**
 2InBlock.gif     * 深度克隆对象,当类存在聚合关系的时候,克隆就必须考虑聚合对象的克隆
 3ExpandedBlockEnd.gif     */

 4 ExpandedBlockStart.gif     public   class  CloneFooB  implements  Cloneable  {
 5InBlock.gif        private CloneFooA fooA;
 6InBlock.gif        private Double douB;
 7InBlock.gif
 8ExpandedSubBlockStart.gif        public CloneFooB(Double douB) {
 9InBlock.gif            this.douB = douB;
10ExpandedSubBlockEnd.gif        }

11InBlock.gif
12ExpandedSubBlockStart.gif        public CloneFooB(CloneFooA fooA, Double douB) {
13InBlock.gif            this.fooA = fooA;
14InBlock.gif            this.douB = douB;
15ExpandedSubBlockEnd.gif        }

16InBlock.gif
17ExpandedSubBlockStart.gif        public CloneFooA getFooA() {
18InBlock.gif            return fooA;
19ExpandedSubBlockEnd.gif        }

20InBlock.gif
21ExpandedSubBlockStart.gif        public void setFooA(CloneFooA fooA) {
22InBlock.gif            this.fooA = fooA;
23ExpandedSubBlockEnd.gif        }

24InBlock.gif
25ExpandedSubBlockStart.gif        public Double getDouB() {
26InBlock.gif            return douB;
27ExpandedSubBlockEnd.gif        }

28InBlock.gif
29ExpandedSubBlockStart.gif        public void setDouB(Double douB) {
30InBlock.gif            this.douB = douB;
31ExpandedSubBlockEnd.gif        }

32InBlock.gif
33ExpandedSubBlockStart.gif        /**
34InBlock.gif         * 克隆操作
35InBlock.gif         *
36InBlock.gif         * @return 自身对象的一个副本
37InBlock.gif         * @throws CloneNotSupportedException
38ExpandedSubBlockEnd.gif         */

39ExpandedSubBlockStart.gif        public Object clone() throws CloneNotSupportedException {
40InBlock.gif            //先调用父类的克隆方法进行克隆操作
41InBlock.gif            CloneFooB cloneFooB = (CloneFooB) super.clone();
42InBlock.gif            //对于克隆后出的对象cloneFooB,如果其成员fooA为null,则不能调用clone(),否则出空指针异常
43InBlock.gif            if (this.fooA != null)
44InBlock.gif                cloneFooB.fooA = (CloneFooA) this.fooA.clone();
45InBlock.gif
46InBlock.gif            return cloneFooB;
47ExpandedSubBlockEnd.gif        }

48ExpandedBlockEnd.gif    }

49 None.gif
50 None.gif

 1 ExpandedBlockStart.gif /**
 2InBlock.gif     * 测试类:分别克隆CloneFooA和CloneFooB类,并打印克隆前后的结果.
 3ExpandedBlockEnd.gif     */

 4 ExpandedBlockStart.gif     public   class  CloneDemo  {
 5ExpandedSubBlockStart.gif        public static void main(String args[]) throws CloneNotSupportedException {
 6InBlock.gif            //CloneFooA克隆前
 7InBlock.gif            CloneFooA fooA1 = new CloneFooA("FooA"11);
 8InBlock.gif            System.out.println("CloneFooA的对象克隆前对象fooA1值为: " + fooA1.getStrA() + "," + fooA1.getIntA());
 9InBlock.gif            //CloneFooA克隆后
10InBlock.gif            CloneFooA fooA2 = (CloneFooA) fooA1.clone();
11InBlock.gif            System.out.println("CloneFooA的对象克隆后对象fooA2值为: " + fooA2.getStrA() + "," + fooA2.getIntA());
12InBlock.gif            //比较fooA1和fooA2内存地址
13InBlock.gif            if (fooA1 == fooA2) System.out.println("比较fooA1和fooA2内存地址:相等!");
14InBlock.gif            else System.out.println("比较fooA1和fooA2内存地址:不相等!");
15InBlock.gif
16InBlock.gif            System.out.println("-------------------------");
17InBlock.gif
18InBlock.gif            //CloneFooB克隆前
19InBlock.gif            CloneFooB fooB1 = new CloneFooB(fooA1, new Double("33"));
20InBlock.gif            System.out.println("CloneFooB的对象克隆前对象fooB1值为: " + fooB1.getFooA().getStrA() + "," + fooB1.getFooA().getIntA() + " | " + fooB1.getDouB());
21InBlock.gif            //CloneFooB克隆后
22InBlock.gif            CloneFooB fooB2 = (CloneFooB) fooB1.clone();
23InBlock.gif            System.out.println("CloneFooB的对象克隆前对象fooB2值为: " + fooB2.getFooA().getStrA() + "," + fooB2.getFooA().getIntA() + " | " + fooB2.getDouB());
24InBlock.gif
25InBlock.gif            if (fooA1 == fooA2) System.out.println("比较fooB1和fooB2内存地址:相等!");
26InBlock.gif            else System.out.println("比较fooB1和fooB2内存地址:不相等!");
27ExpandedSubBlockEnd.gif        }

28ExpandedBlockEnd.gif    }

29 None.gif
30 None.gif

运行结果:

    CloneFooA的对象克隆前对象fooA1值为: FooA,11
    CloneFooA的对象克隆后对象fooA2值为: FooA,11
    比较fooA1和fooA2内存地址:不相等!
    -------------------------
    CloneFooB的对象克隆前对象fooB1值为: FooA,11 | 33.0
    CloneFooB的对象克隆前对象fooB2值为: FooA,11 | 33.0
    比较fooB1和fooB2内存地址:不相等!

    Process finished with exit code 0


    反面教材:

最后,我给出我上面提出到最后要给出的反面例子。

    随便写一个,在CloneFooA 的基础上做了少许改动,内容如下:

 1 ExpandedBlockStart.gif public   class  CloneFooA  implements  Cloneable  {
 2InBlock.gif        private String strA;
 3InBlock.gif        private int intA;
 4InBlock.gif
 5ExpandedSubBlockStart.gif        public CloneFooA(String strA, int intA) {
 6InBlock.gif            this.strA = strA;
 7InBlock.gif            this.intA = intA;
 8ExpandedSubBlockEnd.gif        }

 9InBlock.gif
10ExpandedSubBlockStart.gif        public String getStrA() {
11InBlock.gif            return strA;
12ExpandedSubBlockEnd.gif        }

13InBlock.gif
14ExpandedSubBlockStart.gif        public void setStrA(String strA) {
15InBlock.gif            this.strA = strA;
16ExpandedSubBlockEnd.gif        }

17InBlock.gif
18ExpandedSubBlockStart.gif        public int getIntA() {
19InBlock.gif            return intA;
20ExpandedSubBlockEnd.gif        }

21InBlock.gif
22ExpandedSubBlockStart.gif        public void setIntA(int intA) {
23InBlock.gif            this.intA = intA;
24ExpandedSubBlockEnd.gif        }

25InBlock.gif
26ExpandedSubBlockStart.gif        /**
27InBlock.gif         * @return 创建并返回此对象的一个副本。
28InBlock.gif         * @throws CloneNotSupportedException
29ExpandedSubBlockEnd.gif         */

30ExpandedSubBlockStart.gif        public Object clone() throws CloneNotSupportedException {
31InBlock.gif            //直接调用父类的clone()方法,返回克隆副本
32InBlock.gif            return super.clone();
33ExpandedSubBlockEnd.gif        }

34InBlock.gif
35ExpandedSubBlockStart.gif        /**
36InBlock.gif         * @return 返回运行时的对象
37ExpandedSubBlockEnd.gif         */

38ExpandedSubBlockStart.gif        public CloneFooA getInstance(){
39InBlock.gif            return this;
40ExpandedSubBlockEnd.gif        }

41InBlock.gif
42ExpandedSubBlockStart.gif        public static void main(String args[]){
43InBlock.gif            CloneFooA fooA=new CloneFooA("aa",11);
44InBlock.gif            System.out.println(fooA.getStrA()+"  "+fooA.getIntA());
45InBlock.gif
46InBlock.gif            CloneFooA fooA1=fooA.getInstance();
47InBlock.gif            System.out.println(fooA1.getStrA()+"  "+fooA1.getIntA());
48InBlock.gif            if(fooA==fooA1) System.out.println("fooA和fooA1内存地址相等!");
49InBlock.gif
50InBlock.gif            System.out.println("-------------------------");
51InBlock.gif
52InBlock.gif            //改变后fooA或者fooA1中任何一个,看看另外一个是否会改变
53InBlock.gif            fooA1.setStrA("bb");
54InBlock.gif            System.out.println(fooA.getStrA()+"  "+fooA.getIntA());
55InBlock.gif            System.out.println(fooA1.getStrA()+"  "+fooA1.getIntA());
56InBlock.gif
57InBlock.gif            if(fooA==fooA1) System.out.println("fooA和fooA1内存地址相等,改变fooA1后,fooA的值也跟着变化了");
58ExpandedSubBlockEnd.gif        }

59ExpandedBlockEnd.gif    }

60 None.gif
61 None.gif

运行结果:

    aa  11
    aa  11
    fooA和fooA1内存地址相等!
    -------------------------
    bb  11
    bb  11
    fooA和fooA1内存地址相等,改变fooA1后,fooA的值也跟着变化了

    Process finished with exit code 0

 

来自:http://www.cnitblog.com/lzzzing/archive/2008/05/14/43711.html

你可能感兴趣的:(什么是,算法,java,测试,null,object,jvm,制造)