- 浏览: 177690 次
- 性别:
- 来自: 未知的世界
最新评论
-
xuanxufeng:
看了一点就看不下去了 看了一下评论,果然楼主误人啊~ 楼主 ...
java clone方法使用详解 -
零度锋芒2016:
整体不错,不过笔误较多!文中public class A im ...
java clone方法使用详解 -
Jenercy:
楼主笔误了。<div class="quote ...
java clone方法使用详解 -
Jshen:
别浪费时间
java clone方法使用详解 -
dreajay:
误人子弟
java clone方法使用详解
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; } }
评论
文中
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”也会编译出错的!
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;
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
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);
结果没改变啊
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(); }
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
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
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 `Object`类中的一个`protected`方法,这意味着在使用`clone`时,需要考虑以下几个关键点: 1. **什么是`clone`**: 当我们需要创建一个已有对象的精确副本时,就可以使用`clone`方法。它不是...
Java中的clone方法详解 在Java语言中,clone方法是一个非常重要的概念,它允许对象被复制,从而创造出一个新的对象。下面我们将详细介绍Java中的clone方法,并讨论它的实现机制和应用场景。 什么是clone方法 ...
### Java中的`clone`方法详解:浅拷贝与深拷贝 #### 一、引言 在Java中,`clone`方法提供了一种快速复制对象的方式。它属于`Object`类的一部分,但需要显式地在子类中声明并实现`Cloneable`接口才能正常使用。本文...
本文将深入讲解如何使用`clone()`方法来实现数组复制,并通过实例代码分析其工作原理和注意事项。 首先,`clone()`方法是Java中的一个内置功能,它允许我们创建一个对象的浅拷贝。对于基本类型的数组,`clone()`会...
Java 中 clone() 的使用方法 Java 中的 clone() 方法是对象的复制方法,其主要作用是创建一个与原对象相同的新对象。下面将详细介绍 Java 中 clone() 方法的使用方法。 什么是 clone() 方法? clone() 方法是 ...
Java中的克隆(Clone)机制是一种创建对象副本的方法,它允许程序员复制一个对象的状态,而不会影响原始对象。克隆在编程中常用于创建对象的独立副本,使得新副本与原对象之间相互独立,对其中一个对象的修改不会...
使用clone方法需要实现java.lang.Cloneable接口,并重写protected方法clone。如果没有实现Clonebale接口会抛出CloneNotSupportedException。在克隆java对象的时候不会调用构造器。java提供一种叫浅拷贝(shallowcopy...
Java中的`Object`类是所有类的根,这意味着无论你定义的任何自定义类,如果没有显式地声明继承自其他类,那么它们都会隐式地继承`Object`类。...掌握这些方法的使用,能帮助开发者更有效地管理和操作Java对象。
本文将深入探讨这两种拷贝方式,并着重讲解如何通过重写`clone()`方法以及使用序列化来实现深拷贝。 1. 浅拷贝: 浅拷贝是指创建一个新对象,该对象拥有原始对象的引用字段的副本。这意味着如果原始对象的字段包含...
【对象克隆(clone)详解】 对象克隆是Java编程中的一个重要概念,它允许我们创建一个已有对象的副本,而不影响原始对象。在Java中,克隆主要涉及到`Object`类中的`clone()`方法,以及两种不同的克隆类型:浅克隆和...
这个压缩包文件"详解Java中Object 类的使用.rar"包含了对Java中Object类的深入探讨,通过阅读其中的"详解Java中Object 类的使用.txt"文本,我们可以了解到关于Object类的一些关键知识点。 1. **对象的创建与类型...
【Java原型模式详解】 原型模式(Prototype Pattern)是一种创建型设计模式,它的主要目标是通过复制现有的对象来创建新对象,以减少重复的构造过程,提高性能。在Java中,原型模式通常涉及到对象的克隆操作,即...
### Java 高级特性详解 #### 一、`hashCode` ...正确地重写 `equals` 和 `hashCode` 方法、使用 `Comparator` 进行排序、利用反射机制和序列化技术,以及实现 `clone` 方法都是开发高质量 Java 应用程序的重要技能。
需要注意的是,默认情况下,`clone()`方法是受保护的,因此如果想使用它,通常需要显式地声明并重写这个方法。 **示例代码**: ```java public class MyClass implements Cloneable { private int myField; ...
### Java常用方法集合 #### 一、`java.lang.*`包概述 `java.lang.*` 包含了Java语言中最基本且最常用的类,这些类是自动导入到每一个程序中的,因此无需显式地导入。它包括了如 `String`, `Integer`, `System`, `...
### Java设计模式详解 #### 1. 创建型模式 创建型模式主要关注的是对象的创建方式,它们提供了创建对象的最佳方法。以下是对几种常见的创建型模式的深入解析: ##### 1.1.1 工厂方法(Factory Method) **定义**...