`
lovelace
  • 浏览: 176843 次
  • 性别: Icon_minigender_1
  • 来自: 未知的世界
社区版块
存档分类
最新评论

java clone方法使用详解

    博客分类:
  • Java
阅读更多

http://tuzki.us  我们都是兔斯基

 

----------------------------------------我是小小分割线--------------------------------

 

Java语言的一个优点就是取消了指针的概念,但也导致了许多程序员在编程中常常忽略了对象与引用的区别,特别是先学c、c++后学java的程序员。并且由于Java不能通过简单的赋值来解决对象复制的问题,在开发过程中,也常常要要应用clone()方法来复制对象。比如函数参数类型是自定义的类时,此时便是引用传递而不是值传递。以下是一个小例子:

public class A {
	public String name;
}

 

public class testClone {
	
	public void changeA(A a){
		a.name="b";
	}
	public void changInt(int i){
		i=i*2+100;
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		testClone test=new testClone();
		A a=new A();
		a.name="a";
		System.out.println("before change : a.name="+a.name);
		test.changeA(a);
		System.out.println("after  change : a.name="+a.name);
		int i=1;
		System.out.println("before change : i="+i);
		test.changInt(i);
		System.out.println("after  change : i="+i);
	}

}



此时输出的结果是:

before change : a.name=a
after  change : a.name=b
before change : i=1
after  change : i=1



从这个例子知道Java对对象和基本的数据类型的处理是不一样的。在Java中用对象的作为入口参数的传递则缺省为"引用传递",也就是说仅仅传递了对象的一个"引用",这个"引用"的概念同C语言中的指针引用是一样的。当函数体内部对输入变量改变时,实质上就是在对这个对象的直接操作。
除了在函数传值的时候是"引用传递",在任何用"="向对象变量赋值的时候都是"引用传递",如:

		A a1=new A();
		A a2=new A();
		a1.name="a1";
		a2=a1;
		a2.name="a2";
		System.out.println("a1.name="+a1.name);
		System.out.println("a2.name="+a2.name);



此时输出的结果是:

a1.name=a2
a2.name=a2



如果我们要用a2保存a1对象的数据,但又不希望a2对象数据被改变时不影响到a1。实现clone()方法是其一种最简单,也是最高效的手段。
下面我们来实现A的clone方法

public class A implements Cloneable {
	public String name;

	public Object clone() {
		A o = null;
		try {
			o = (A) super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return o;
	}

}


首先要实现Cloneable接口,然后在重载clone方法,最后在clone()方法中调用了super.clone(),这也意味着无论clone类的继承结构是什么样的,super.clone()直接或间接调用了java.lang.Object类的clone()方法。

		A a1=new A();
		A a2=new A();
		a1.name="a1";
		a2=a1;
		a2.name="a2";
		System.out.println("a1.name="+a1.name);
		System.out.println("a2.name="+a2.name);



此时输出的结果是:

a1.name=a1
a2.name=a2



当Class A成员变量类型是java的基本类型时(外加String类型),只要实现如上简单的clone(称影子clone)就可以。但是如果Class A成员变量是数组或复杂类型时,就必须实现深度clone。

public class A implements Cloneable {
	public String name[];
	
	public A(){
		name=new String[2];
	}

	public Object clone() {
		A o = null;
		try {
			o = (A) super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return o;
	}
}


测试代码

A a1=new A();
A a2=new A();
a1.name[0]="a";
a1.name[1]="1";
a2=(A)a1.clone();
a2.name[0]="b";
a2.name[1]="1";
System.out.println("a1.name="+a1.name);
System.out.println("a1.name="+a1.name[0]+a1.name[1]);
System.out.println("a2.name="+a2.name);
System.out.println("a2.name="+a2.name[0]+a2.name[1]);


输出结果:

a1.name=[Ljava.lang.String;@757aef
a1.name=b1
a2.name=[Ljava.lang.String;@757aef
a2.name=b1


看到了吧,a1.name,a2.name的hash值都是@757aef,也就是说影子clone对name数组只是clone他们的地址!解决该办法是进行深度clone。

public Object clone() {
		A o = null;
		try {
			o = (A) super.clone();
            o.name=(String[])name.clone();//其实也很简单^_^
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return o;
	}


此时输出结果是:

a1.name=[Ljava.lang.String;@757aef
a1.name=a1
a2.name=[Ljava.lang.String;@d9f9c3
a2.name=b1


需要注意的是Class A存在更为复杂的成员变量时,如Vector等存储对象地址的容器时,就必须clone彻底。

public class A implements Cloneable {
	public String name[];
	public Vector<B> claB;
	
	public A(){
		name=new String[2];
		claB=new Vector<B>();
	}

	public Object clone() {
		A o = null;
		try {
			o = (A) super.clone();
            o.name==(String[])name.clone();//深度clone
            o.claB=new Vector<B>();//将clone进行到底
            for(int i=0;i<claB.size();i++){
	            B temp=(B)claB.get(i).clone();//当然Class B也要实现相应clone方法
	            o.claB.add(temp);
		    }
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
				return o;
	}
}

 

 

分享到:
评论
22 楼 xuanxufeng 2017-03-16  
看了一点就看不下去了  看了一下评论,果然楼主误人啊~  楼主你连重载和重写都分不清楚,还来写博客  语句混乱啊,语文老师怎么教的
21 楼 零度锋芒2016 2016-08-23  
整体不错,不过笔误较多!
文中
public class A implements Cloneable { 
    public String name; 
 
    public Object clone() { 
        A o = null; 
        try { 
            o = (A) super.clone(); 
        } catch (CloneNotSupportedException e) { 
            e.printStackTrace(); 
        } 
        return o; 
    } 
 
} 这里name作为类的属性,应该设置private权限,而不是public String name,要符合封装性,而且设置set与get方法,否则下面测试类a1.name=“a1”也会编译出错的!
20 楼 Jenercy 2015-05-05  
楼主笔误了。
引用
首先要实现Cloneable接口,然后在重载clone方法,最后在clone()方法中调用了super.clone(),这也意味着无论clone类的继承结构是什么样的,super.clone()直接或间接调用了java.lang.Object类的clone()方法。
Java代码  收藏代码
A a1=new A(); 
A a2=new A(); 
a1.name="a1"; 
a2=a1; 
a2.name="a2"; 
System.out.println("a1.name="+a1.name); 
System.out.println("a2.name="+a2.name); 


此时输出的结果是:
Java代码  收藏代码
a1.name=a1 
a2.name=a2 

这里的a2=a1应该要写成:a2 = a1.clone()
另外,A a2=new A()这么写不好,应该写成A a2 = null;
19 楼 Jshen 2014-12-12  
别浪费时间
18 楼 dreajay 2014-11-30  
误人子弟
17 楼 风飞叶扬 2014-10-08  
看代码之前先看看1L的评论吧
16 楼 ljh_uncle 2014-08-21  
脑残。。。
15 楼 daijunjian 2013-08-31  
a2=a1; 这块代码有些问题,应该是是a2 = (A)al.clone;
14 楼 zjuttsw 2013-01-10  
误人子弟啊。还好先看了评论
13 楼 Wuaner 2012-09-11  
楼主 先搞明白Java里没有引用传递,再说Clone的问题。
12 楼 wzb56 2012-08-09  
jersey109 写道
他这个代码有问题,大家不要浪费时间了.

11 楼 7_iroy 2012-04-10  
10 楼 jersey109 2011-08-28  
他这个代码有问题,大家不要浪费时间了.
9 楼 zcwfeng 2011-07-21  

public class A implements Cloneable {
/**
* clone
*/
public String name;

public Object clone() {
A o = null;
try {
o = (A) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return o;
}

}
public class TestCloner {
public static void main(String[] args) {
cn.nusof.clone.cloner.A a1 = new A();
cn.nusof.clone.cloner.A a2 = new A();
a1.name = "a1";
a1 = a2;
a2.name = "a2";
System.out.println("a1.name=" + a1.name);
System.out.println("a2.name=" + a2.name);
}
}

结果和你的根本不一样,骗人了不是
a1.name=a2
a2.name=a2
8 楼 jingtao416 2011-07-17  
public class A implements Cloneable 后
A a1=new A();  
A a2=new A();  
a1.name="a1";  
a2=a1;  
a2.name="a2";  
System.out.println("a1.name="+a1.name);  
System.out.println("a2.name="+a2.name); 
结果没改变啊
7 楼 8040 2008-11-17  
序列化与反序列化 深度克隆
   
 private static Object cloneObject(Object obj) throws Exception{
        ByteArrayOutputStream  byteOut = new ByteArrayOutputStream();  
        ObjectOutputStream out = new ObjectOutputStream(byteOut);  
        out.writeObject(obj);         
        ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());  
        ObjectInputStream in =new ObjectInputStream(byteIn);        
        return in.readObject();
}


6 楼 lovelace 2008-09-10  
引用
windshjw 昨天
Your deep clone coding is also not correct.
The correct coding should be:

public class A implements Cloneable {
public String name;
public String[] group = new String[2];


public Object clone() {
A o = null;
try {
o = (A) super.clone();
o.group = (String[])group.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return o;
}

}

just you said ,it's wrong.
thank you for kind reply
5 楼 lovelace 2008-09-10  
引用
windshjw 昨天
A a1=new A();
A a2=new A();
a1.name="a1";
a2=a1;
a2.name="a2";
System.out.println("a1.name="+a1.name);
System.out.println("a2.name="+a2.name);
result:
a1.name=a2
a2.name=a2

Obviously, It is not correct. Pls confirm whether you copy error coding.The sentence "a2=(A)a1.clone();" should be used, instead "a2=a1;"

i don't think so.
My example is in the case:
除了在函数传值的时候是"引用传递",在任何用"="向对象变量赋值的时候都是"引用传递"
not in clone
4 楼 taupo 2008-09-09  
干嘛用英语??????好累啊,看了一般
3 楼 niwtsew 2008-09-09  
Why Clone
Suppose you have an object ‘a’ of class ‘A’. Sometimes you may need another new object ‘b’. It also belongs to class ‘A’ and has the same data with object ‘a’. But if you do some modification on b, it has no effect to the value of ‘a’. We call this process which produced new object ‘b’ as clone object ‘a’. The commonest time that you need to clone an object is when it is a parameter or return value of one of your public methods. If it is a parameter that you save somewhere, then you don't want the caller to be able to modify it later. So you save a copy of the object. Likewise, if you are returning an object that is part of your class's internal state, you need to return a copy instead so that callers can't accidentally or deliberately change that internal state.

Conventions of clone
1. x.clone() !=  x                                                 // x.clone() will return a new object
2. x.clone().equals(x)                                          // this is the meaning of ‘copy’
3. x.clone().getClass() == x.getClass()
4. The object returned by clone method should be independent of the object (which is 
    being cloned).

These are not absolute requirements but are general intends of clone method which is also recommended in Java Documents.

How to write clone method
By convention, the approach of writing clone method is:

      1.      Implements Cloneable interface

            This approach ensures your clone method can directly or indirectly call Object.clone(). Otherwise, calling Object.clone() will throws CloneNotSupportedException. Why we need to call Object.clone() in our clone method? Please see approach 2.2.

      2.      Override the clone method

            2.1     Make the clone method to public method

                  Please be noted that the clone method type of Object class is:

          protected Object clone()
                throws CloneNotSupportedException
In order to support other class can use our clone method, we should define it as public method.

            2.2     Call super.clone() to produce the new object

                  By convention, the object returned by clone method should be obtained by calling super.clone (this means it’s better to produce the new object by super.clone() than directly use “new” operator). If a class and all of its superclasses (except Object) obey this convention, it will be the case that x.clone().getClass() == x.getClass().

                  Key point: why we should use super.clone() to produce the new object instead of directly use “new” operator?

                        v       First of all, if all classes obey this convention, our clone method will directly or indirectly call Object.clone method. This method is a native method, it will be more efficient than directly “new” an object.

                        v       Secondly, Object.clone method can recognize the class type which called the clone method using RTTI mechanism. And it will return the new object which has the correct class type. For example:

                              class A implements Cloneable
                              class B extends A implements Cloneable {

                                                        public Object clone() throws CloneNotSupportedException{
                                                                B b = null;
      
                                                                b = (B) super.clone();  // It seems that super.clone() is  

                                                                                                //A.clone(), so it will return an

                                                                                                 //object of Class A. This is incorrect.

                                                                                                 //If the clone method of class A calls

                                                                                                 //super.clone method too, it will

                                                                                                //return a new object belongs to

                                                                                                //class B. Thus, we can cast it to

                                                                                                //class B. This is the benefit of

                                                                                                //Object.clone().
                                                                return b;
                                                }
                                        }
                                        Now, let’s consider another case, if we write clone method of class A like this:

                                        class A {
                                                public Object clone() {
                                                A a = null;
                                                a = new A();
                                                // Then do some copy data operation.
                                                         return a;
                                        }
                                        }
When B.clone() calls super.clone(),unfortunately we can only get the object whose class is A. And we can’t cast the new object to class B since B is a subclass of A.

That’s why it’s strongly recommended that clone method of all classes obey the convention that obtained the new object by calling super.clone().

            2.3     Clone members

                  There are two cases: If the member supports clone, it’s better to call the clone method of the member to return a copy object of this member. If the member doesn’t support clone, you should create a new object which is the copy of the member. After this approach, it will be ensured that x.clone.equals(x) and x.clone() is independent with x.

Examples
/**
* class B support clone
* @author xzhu2
*
*/
class B implements Cloneable {
        private int intMember;
      
        public B(int i) {
                intMember = i;
        }
      
        public void setIntMember(int i) {
                intMember = i;
        }
      
        public Object clone()
                 throws CloneNotSupportedException {
                B clonedObject = null;
              
                // Firstly, call super.clone to return new object
                clonedObject = (B)super.clone();
              
                // Secondly, clone member here
                clonedObject.setIntMember(intMember);
              
                // The end, return new object
                return clonedObject;
        }
}

/**
* class C doesn't support clone
* @author xzhu2
*
*/
class C {
        private int intMember;
      
        public C(int i) {
                intMember = i;
        }
      
        public void setIntMember(int i) {
                intMember = i;
        }
      
        public int getIntMember() {
                return intMember;
        }
}

class A implements Cloneable {
        private int intMember = 0;
        private String stringMember = "";
        private B supportCloneMember = null;
        private C notSupportCloneMember = null;
      
        public void setIntMember(int i) {
                intMember = i;
        }
      
        public void setStringMember(String s) {
                stringMember = s;
        }
      
        public void setB(B b) {
                supportCloneMember = b;
        }
      
        public void setC(C c) {
                notSupportCloneMember = c;
        }
      
        public Object clone()
                throws CloneNotSupportedException {
                A clonedObject = null;

                // Firstly, call super.clone to return new object
                clonedObject = (A)super.clone();

                // Secondly, clone members here
              
                // For basic type member, directly set it to clonedObject     
                // Because basic type parameter passes value. Modify
                // clonedObject.intMember can not effect the intMember
                // of itself.
                clonedObject.setIntMember(intMember);
                // For immutable member, directly set it to clonedObject.
                // Becasue we can not change the value of immutable
            // variable once it was setted.
                clonedObject.setStringMember(stringMember);
                // For member which support clone, we just clone it and
                // set the return object to the member of new object.
                B clonedB = (B)supportCloneMember.clone();
                clonedObject.setB(clonedB);
                // For member which do not support clone, we need to create
                // new object.
                C clonedC = new C(notSupportCloneMember.getIntMember());
                clonedObject.setC(clonedC);
              
                // The end, return new object
                return clonedObject;
        }
}

相关推荐

    Java中的clone方法详解_动力节点Java学院

    Java中的clone方法详解_动力节点Java学院,动力节点口口相传的Java黄埔军校

    Java clone方法详解及简单实例

    主要介绍了 Java clone方法详解及简单实例的相关资料,需要的朋友可以参考下

    Java 数组复制clone方法实现详解

    主要介绍了Java 数组复制clone方法实现详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

    Java中的clone方法详解_动力节点Java学院整理

    clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象。下面通过本文给大家介绍java中的clone方法,感兴趣的朋友一起看看吧

    详解Java中的clone方法?原型模式

     clone顾名思义是复制, 在Java语言中, clone方法被对象调用,所以会复制对象。所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象。那么在java语言中,有几种方式可以创建...

    java 中clone()的使用方法

    主要介绍了java 中clone()的使用方法的相关资料,希望通过本文能帮助大家能掌握clone()的克隆方法,需要的朋友可以参考下

    Clone详解.doc

    现在Clone已经不是一个新鲜词语了,伴随着“多莉”的产生这个词语确实很“火”过一阵子,在java中也有这么一个概念,它可以让我们很方便的“制造”出一个对象的副本来,下面来具体看看java中的Clone机制是如何工作的...

    Java中的数组复制(clone与arraycopy)代码详解

    主要介绍了Java中的数组复制(clone与arraycopy)代码详解,本文并未全部介绍数组复制的几种方式,仅对clone和copy的相关内容进行了解析,具有一定参考价值,需要的朋友可以了解下。

    对象克隆(clone)详解.docx

    查看了很多文章对于clone()方法讲解都不慎透彻,特别是对于深层克隆的讲解语言晦涩难懂,很难理解,包括Oracle公司出版的JDK帮助文档也讲的不清楚,本人通过具体实践通过浅显易懂的语言详细讲解了clone()方法。...

    详解Java中clone的写法

    主要介绍了Java中clone的写法,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下

    详解java中的深拷贝和浅拷贝(clone()方法的重写、使用序列化实现真正的深拷贝)

    主要介绍了java中的深拷贝和浅拷贝(clone()方法的重写、使用序列化实现真正的深拷贝),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

    java面向对象之JVM创建及分配策略方法详解.docx

    前言 Java是面向对象的语言,所谓“万事万物皆对象”就是Java是基于对象来设计程序的,没有对象程序就无法...clone 反序列化 Unsafe.allocateInstance 为了便于说明和理解,下文仅针对new出来的对象进行讨论。

    JAVA入门1.2.3:一个老鸟的JAVA学习心得 PART1(共3个)

    如何使用方法的返回值? 166 7.4.4 使用return结束方法 166 7.5 方法重载(overload):给汽车加速添个限制 168 7.5.1 什么是方法的签名 168 7.5.2 什么是重载?为什么要重载? 168 7.5.3 给汽车加个重载的方法...

    Java入门1·2·3:一个老鸟的Java学习心得.PART3(共3个)

    如何使用方法的返回值? 166 7.4.4 使用return结束方法 166 7.5 方法重载(overload):给汽车加速添个限制 168 7.5.1 什么是方法的签名 168 7.5.2 什么是重载?为什么要重载? 168 7.5.3 给汽车加个重载的方法...

    javascript中clone对象详解

     JavaScript中,简单的方法就是用JSON函数,将对象stringify成字符串,再parse成一个新对象。要么就是从网上搜个代码,开源社区里面clone的代码还是有不少的。  代码虽然可以找得到,但,东西永远是别人的,动手学...

    JAVA核心知识点整理(有效)

    25 JAVA8 与元数据.................................................................................................................................25 2.4. 垃圾回收与算法 .................................

    拍卖源码java-prebid-server-java:Java版的PrebidServer

    拍卖源码 ...我们使用并尝试引入最小的依赖。 运行时,服务器响应多个 HTTP 。 建造 按照后续步骤创建可以在本地部署的 JAR 文件。 下载或克隆一个项目: git clone https://github.com/prebid/preb

    leetcode题目详解-leetcode-solutions:几个Leetcode问题的简单解释和时间复杂度

    编译器/终端运行Main.java或运行Main.java的main方法(如果使用 IDE)。 3. 贡献 非常欢迎每个人为解决方案做出贡献。 3.1. 设置: 从主菜单中,选择VCS | 从 Version Control &gt; Git &gt; Clone &gt; ...

    Android代码-RxJava入门教程例子

    一个在 Java VM 上使用可观测的序列来组成异步的、基于事件的程序的库 RxAndroid RxAndroid 是 RxJava 的一个针对 Android 平台的扩展 RxBinding JakeWharton Android函数式相应编程(FRP)框

    Java进阶版本管理工具Git全套视频教程

    5.2.2概念即详解 6.Git 的基本使用01-TortoiseGit 操作本地仓库 6.1 初始化仓库 6.2 添加文件 6.3 提交文件至本地仓库 6.4 修改文件,与再次提交文件 6.5 文件状态讲解 6.6 修改文件,不提交和上一个版本比较差异(diff...

Global site tag (gtag.js) - Google Analytics