`
ArtShell
  • 浏览: 27660 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类

Java中的clone问题(浅克隆+深克隆)

阅读更多

认识:跟克隆有关的两个类型,Cloneable(接口类型)、Object(类类型);JDK API中这个两个类型的源代码和描述如下(添加了部分内容,以及内容顺序有改动):

 

1Cloneable

public interface Cloneable {}

 

对应 api document的描述:

 

实现了 Cloneable接口的类,以指示 Object.clone()方法可以合法地对该类实例进行按字段复制。

如果在没有实现 Cloneable接口的类实例上调用 Object clone 方法,则会导致抛出 CloneNotSupportedException异常【见example.1

按照惯例,实现此接口的类应该使用公共方法重写 Object.clone(它是受保护的)。请参阅 Object.clone(),以获得有关重写此方法的详细信息。

注意,此接口 包含 clone方法。因此,因为某个对象实现了此接口就克隆它是不可能的。即使 clone 方法是反射性调用的,也无法保证它将获得成功。(Object. clone()方法是protected类型的,反射时只有public类型的方法能够被获取(当且仅当该类实现Cloneable接口后并重写Object. clone()方法,才能被获取))

 

example.1

public class NotImplCloneable { //此类没有实现Cloneable接口
	public static void main(String[] args) throws CloneNotSupportedException {
		NotImplCloneable notIClone = new NotImplCloneable();
		notIClone.clone(); //Object类是所有类的父类,clone()从何而来不做解释
	}
}

 

 

 

2Object

public class Object {
   ……  // 其他方法省略
	protected native Object clone() throws CloneNotSupportedException;
}

 

对应 api document的描述:

 

创建并返回此对象的一个副本。副本的准确含义可能依赖于对象的类。这样做的目的是,对于任何对象 x,表达式:

x.clone() != x

true,表达式:

x.clone().getClass() == x.getClass()

也为 true,但这些并非必须要满足的要求。一般情况下:

x.clone().equals(x)

true,但这并非必须要满足的要求。

按照惯例,返回的对象应该通过调用 super.clone 获得。如果一个类及其所有的超类(Object 除外)都遵守此约定,则 x.clone().getClass() == x.getClass()

 

Object 类的 clone 方法执行特定的复制操作。首先,如果此对象的类没有实现接口 Cloneable,则会抛出 CloneNotSupportedException注意,所有的数组对象都被视为已经实现Cloneable接口,但并不一定代表数组元素对象实现了Cloneable接口(前提是数组元素对象对应的类已经实现Cloneable接口后并重写Object. clone()方法),创建此对象的类的一个新实例,并像通过分配那样,严格使用此对象相应字段的内容初始化该对象的所有字段;这些字段的内容没有被自我复制。所以,此方法执行的是该对象的浅表复制,而不深层复制操作。【这段文字描述的是浅克隆,见example.2

 

按照惯例,此方法返回的对象应该独立于该对象(正被复制的对象)。要获得此独立性,在 super.clone 返回对象之前,有必要对该对象的一个或多个字段进行修改。这通常意味着要复制包含正在被复制对象的内部深层结构的所有可变对象,并使用对副本的引用替换对这些对象的引用。如果一个类只包含基本类型(primitive)字段或对不可变类型(final)字段的引用,那么通常不需要修改 super.clone 返回的对象中的字段【这段文字描述的深克隆,见example.3】。

 

Object 类本身不实现接口 Cloneable,所以在类为 Object 的对象上调用 clone 方法将会导致在运行时抛出异常。

 

 

【浅克隆example.2

class DeepStructure {
	public boolean boo = true; // 基本类型字段
	public final char ch = 'a'; // final类型字段,即:不可变引用
}

public class ImplCloneable implements Cloneable {
	public int i = 0; // 基本类型字段
	public final String str = "abcd"; // final类型字段,即:不可变引用
	public DeepStructure deepStructure = new DeepStructure(); // 可变引用
	
	@Override
	public ImplCloneable clone() throws CloneNotSupportedException {
		return (ImplCloneable) super.clone();// 浅克隆
	}
	public static void main(String[] args) throws CloneNotSupportedException {
		ImplCloneable implClone1 = new ImplCloneable();
		ImplCloneable implClone2 = (ImplCloneable)implClone1.clone();
		
		System.out.println("---------implClone1 of attribute---------");
		System.out.println("implClone1.i: " + implClone1.i);
		System.out.println("implClone1.str: " + implClone1.str);
		System.out.println("implClone1.deepStructure.boo: " + implClone1.deepStructure.boo);
		System.out.println("implClone1.deepStructure.ch: " + implClone1.deepStructure.ch);
		
		System.out.println("---------implClone2 of attribute---------");
		System.out.println("implClone2.i: " + implClone2.i);
		System.out.println("implClone2.str: " + implClone2.str);
		System.out.println("implClone2.deepStructure.boo: " + implClone2.deepStructure.boo);
		System.out.println("implClone2.deepStructure.ch: " + implClone2.deepStructure.ch);
		
		/*
		 * 修改 implClone2 对象的属性,观察implClone1和implClone2的输出变化
		 */
		implClone2.i = 10;
		implClone2.deepStructure.boo = false;
		
		System.out.println("---------implClone1 of attribute---------");
		System.out.println("implClone1.i: " + implClone1.i);
		System.out.println("implClone1.str: " + implClone1.str);
		System.out.println("implClone1.deepStructure.boo: " + implClone1.deepStructure.boo);
		System.out.println("implClone1.deepStructure.ch: " + implClone1.deepStructure.ch);
		
		System.out.println("---------implClone2 of attribute---------");
		System.out.println("implClone2.i: " + implClone2.i);
		System.out.println("implClone2.str: " + implClone2.str);
		System.out.println("implClone2.deepStructure.boo: " + implClone2.deepStructure.boo);
		System.out.println("implClone2.deepStructure.ch: " + implClone2.deepStructure.ch);
	}
}

 

 

 

 

 

【深克隆example.3

class DeepStructure implements Cloneable {
	public boolean boo = true; // 基本类型字段
	public final char ch = 'a'; // final类型字段,即:不可变引用
	
	@Override
	public DeepStructure clone() throws CloneNotSupportedException {
		return (DeepStructure)super.clone();
	}
}

public class ImplCloneable implements Cloneable {
	public int i = 0; // 基本类型字段
	public final String str = "abcd"; // final类型字段,即:不可变引用
	public DeepStructure deepStructure = new DeepStructure(); // 可变引用
	
	@Override
	public ImplCloneable clone() throws CloneNotSupportedException {
		ImplCloneable object =(ImplCloneable)super.clone();
		object.deepStructure = (DeepStructure)deepStructure.clone();// 深克隆
		return object;
	}
	public static void main(String[] args) throws CloneNotSupportedException {
		ImplCloneable implClone1 = new ImplCloneable();
		ImplCloneable implClone2 = (ImplCloneable)implClone1.clone();
		
		/*
		 *修改 implClone2 对象的属性,观察implClone1和implClone2的输出变化
		 */
		implClone2.i = 10;
		implClone2.deepStructure.boo = false;
		
		System.out.println("---------implClone1 of attribute---------");
		System.out.println("implClone1.i: " + implClone1.i);
		System.out.println("implClone1.str: " + implClone1.str);
		System.out.println("implClone1.deepStructure.boo: " + implClone1.deepStructure.boo);
		System.out.println("implClone1.deepStructure.ch: " + implClone1.deepStructure.ch);
		
		System.out.println("---------implClone2 of attribute---------");
		System.out.println("implClone2.i: " + implClone2.i);
		System.out.println("implClone2.str: " + implClone2.str);
		System.out.println("implClone2.deepStructure.boo: " + implClone2.deepStructure.boo);
		System.out.println("implClone2.deepStructure.ch: " + implClone2.deepStructure.ch);
	}
}

 

 

以下:这段文字内容摘自;http://gabrielcjx.iteye.com/blog/445388

Cloning is a potentially dangerous action, because it can cause unintended side effects. For example, if the object being cloned contains a reference variable called obRef, then when the clone is made, obRef in the clone will refer to the same object as does obRef in the original. If the clone makes a change to the contents of the object referred to by obRef, then it will be changed for the original object, too. Here is another example. If an object opens an I/O stream and is then cloned, two objects will be capable of operating on the same stream. Further, if one of these objects closes the stream, the other object might still attempt to write to it, causing an error.

 复制是一种存在潜在危险的行为,因为它会引起一些意想不到的负作用。例如,如果被复制的对象包含一个名为 obRef引用变量,在复制时,复制对象的 obRef原来对象的 obRef都会指向同一个对象。如果复制对象对 obRef指向的对象的内容做出一些改变,对于原来对象来说,也就相当于它也被改变了。还有另一个例子,如果一个操作I/O流的对象被复制了,这两个对象都能对同一I/O流进行操作。进一步说,如果它们两个中的一个关闭了I/O 流,而另一个对象可能试图对I/O流进行写操作,这就会引起错误。

 

综上所述:上边这段文字描述的是【浅克隆,见example.2】,如果按照【深克隆,见example.3】的方法来避免上边这段文字中描述的问题,显得非常的笨拙(笨在何处:如果ImplCloneable类对象持有多个可变引用,那么就会去为每一个可变引用的所属类都实现一遍Object. clone()方法)。下边介绍终极解决方案:通过【序列化和反序列化】来实现一次性深克隆,请移步:http://www.cnblogs.com/kadinzhu/archive/2011/07/14/2106254.html

 

 

参考:JDK1.6  API Document

           http://gabrielcjx.iteye.com/blog/445388

         

 http://www.cnblogs.com/kadinzhu/archive/2011/07/14/2106254.html

 

      请各位大神多拍砖!多谢!

  • 大小: 29.9 KB
  • 大小: 74.9 KB
  • 大小: 43 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics