`

Java_Clone

    博客分类:
  • Java
 
阅读更多

via: http://www.java3z.com/cwbwebhome/article/article8/81145.html?id=2600

目录 

预备知识 
为什么要clone 
Object的clone以及为什么如此实现 
如何clone 
对clone的态度 
其他的选择 
和Serializable的比较 
性能 

预备知识 

为了理解java的clone,有必要先温习以下的知识。 
java的类型,java的类型分为两大类,一类为primitive,如int,另一类为引用类型,如String,Object等等。 
java引用类型的存储,java的引用类型都是存储在堆上的。

 
public class B {
	int a;
	String b;
	public B(int a, String b) {
		super();
		this.a = a;
		this.b = b;
	}
}


对这样一个引用类型的实例,我们可以推测,在堆上它的内存存储形式(除去指向class的引用,锁的管理等等内务事务所占内存),应该有一个int值表示a,以及一个引用,该引用指向b在堆上的存储空间。 


  


为什么要clone 

恩,因为需要。废话。 
有名的GoF设计模式里有一个模式为原型模式,用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 
简单的说就是clone一个对象实例。使得clone出来的copy和原有的对象一模一样。 

插一个简单使用clone的例子,如果一个对象内部有可变对象实例的话,public API不应该直接返回该对象的引用,以防调用方的code改变该对象的内部状态。这个时候可以返回该对象的clone。 

问题来了,什么叫一模一样。 
一般来说,有 
x.clone() != x 
x.clone().getClass() == x.getClass() 
x.clone().equals(x) 
但是这些都不是强制的。 
我们需要什么样的clone就搞出什么样的clone好了。 
一般而言,我们要的clone应该是这样的。copy和原型的内容一样,但是又是彼此隔离的。即在clone之后,改变其中一个不影响另外一个。 


Object的clone以及为什么如此实现 

Object的clone的行为是最简单的。以堆上的内存存储解释的话(不计内务内存),对一个对象a的clone就是在堆上分配一个和a在堆上所占存储空间一样大的一块地方,然后把a的堆上内存的内容复制到这个新分配的内存空间上。 
看例子。 

class User {
	String name;
	int age;
}
class Account implements Cloneable {
	User user;
	long balance;
	@Override
	public Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
}



		// user.
		User user = new User();
		user.name = "user";
		user.age = 20;
		// account.
		Account account = new Account();
		account.user = user;
		account.balance = 10000;
		// copy.
		Account copy = (Account) account.clone();
		// balance因为是primitive,所以copy和原型是相等且独立的。
		Assert.assertEquals(copy.balance, account.balance);
		copy.balance = 20000;
		// 改变copy不影响原型。
		Assert.assertTrue(copy.balance != account.balance);
		// user因为是引用类型,所以copy和原型的引用是同一的。
		Assert.assertTrue(copy.user == account.user);
		copy.user.name = "newName";
		// 改变的是同一个东西。
		Assert.assertEquals("newName", account.user.name);




  


恩,默认实现是帮了我们一些忙,但是不是全部。 
primitive的确做到了相等且隔离。 
引用类型仅仅是复制了一下引用,copy和原型引用的东西是一样的。 
这个就是所谓的浅copy了。 
要实现深copy,即复制原型中对象的内存copy,而不仅仅是一个引用。只有自己动手了。 
等等,是不是所有的引用类型都需要深copy呢? 
不是! 
我们之所以要深copy,是因为默认的实现提供的浅copy不是隔离的,换言之,改变copy的东西,会影响到原型的内部。比如例子中,改变copy的user的name,影响了原型。 
如果我们要copy的类是不可变的呢,如String,没有方法可以改变它的内部状态呢。 

class User implements Cloneable {
	String name;
	int age;
	@Override
	public Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
}

 

		// user.
		User user = new User();
		user.name = "user";
		user.age = 20;
		// copy
		User copy = (User) user.clone();
		// age因为是primitive,所以copy和原型是相等且独立的。
		Assert.assertEquals(copy.age, user.age);
		copy.age = 30;
		// 改变copy不影响原型。
		Assert.assertTrue(copy.age != user.age);
		// name因为是引用类型,所以copy和原型的引用是同一的。
		Assert.assertTrue(copy.name == user.name);
		// String为不可变类。没有办法可以通过对copy.name的字符串的操作改变这个字符串。
		// 改变引用新的对象不会影响原型。
		copy.name = "newname";
		Assert.assertEquals("newname", copy.name);
		Assert.assertEquals("user", user.name);

可见,在考虑clone时,primitive和不可变对象类型是可以同等对待的。 

java为什么如此实现clone呢? 
也许有以下考虑。 
1 效率和简单性,简单的copy一个对象在堆上的的内存比遍历一个对象网然后内存深copy明显效率高并且简单。 
2 不给别的类强加意义。如果A实现了Cloneable,同时有一个引用指向B,如果直接复制内存进行深copy的话,意味着B在意义上也是支持Clone的,但是这个是在使用B的A中做的,B甚至都不知道。破坏了B原有的接口。 
3 有可能破坏语义。如果A实现了Cloneable,同时有一个引用指向B,该B实现为单例模式,如果直接复制内存进行深copy的话,破坏了B的单例模式。 
4 方便且更灵活,如果A引用一个不可变对象,则内存deep copy是一种浪费。Shadow copy给了程序员更好的灵活性。 

如何clone 
clone三部曲。 
1 声明实现Cloneable接口。 
2 调用super.clone拿到一个对象,如果父类的clone实现没有问题的话,在该对象的内存存储中,所有父类定义的field都已经clone好了,该类中的primitive和不可变类型引用也克隆好了,可变类型引用都是浅copy。 
3 把浅copy的引用指向原型对象新的克隆体。 
给个例子。 

class User implements Cloneable {
	String name;
	int age;
	@Override
	public User clone() throws CloneNotSupportedException {
		return (User) super.clone();
	}
}
class Account implements Cloneable {
	User user;
	long balance;
	@Override
	public Account clone() throws CloneNotSupportedException {
		Account account = null;
		account = (Account) super.clone();
		if (user != null) {
			account.user = user.clone();
		}
		return account;
	}
}




  


对clone的态度 

clone嘛,我觉得是个好东西,毕竟系统默认实现已经帮我们做了很多事情了。 
但是它也是有缺点的。 
1 手工维护clone的调用链。这个问题不大,程序员有责任做好。 
2 如果class的field是个final的可变类,就不行了。三部曲的第三步没有办法做了。 

考虑一个类对clone的态度,有如下几种。 
1 公开支持:好吧,按照clone三部曲实现吧。前提是父类支持(公开或者默默)。 
2 默默支持:不实现Cloneable接口,但是在类里面有正确的protected的clone实现,这样,该类不支持clone,但是它的子类如果想支持的话也不妨碍。 
3 不支持:好吧,为了明确该目的,提供一个抛CloneNotSupportedException 异常的protected的clone实现。 
4 看情况支持:该类内部可以保存其他类的实例,如果其他类支持则该类支持,如果其他类不支持,该类没有办法,只有不支持。 


其他的选择 

可以用原型构造函数,或者静态copy方法来手工制作一个对象的copy。 
好处是即使class的field为final,也不会影响该方法的使用。不好的地方是所有的primitive赋值都得自己维护。 


和Serializable的比较 

使用Serializable同样可以做到对象的clone。但是: 
Cloneable本身就是为clone设计的,虽然有一些缺点,但是如果它可以clone的话无疑用它来做clone比较合适。如果不行的话用原型构造函数,或者静态copy方法也可以。 

Serializable制作clone的话,添加了太多其它的东西,增加了复杂性。 
1 所有的相关的类都得支持Serializable。这个相比支持Cloneable只会工作量更大 
2 Serializable添加了更多的意义,除了提供一个方法用Serializable制作Clone,该类等于也添加了其它的public API,如果一个类实现了Serializable,等于它的2进制形式就已经是其API的一部分了,不便于该类以后内部的改动。 
3 当类用Serializable来实现clone时,用户如果保存了一个老版本的对象2进制,该类升级,用户用新版本的类反系列化该对象,再调用该对象用Serializable实现的clone。这里为了一个clone的方法又引入了类版本兼容性的问题。不划算。 


性能 

不可否认,JVM越来越快了。 
但是系统默认的native实现还是挺快的。 
clone一个有100个元素的int数组,用系统默认的clone比静态copy方法快2倍左右。 

  • 大小: 8.2 KB
  • 大小: 14.5 KB
  • 大小: 23.7 KB
分享到:
评论

相关推荐

    java_clone用法

    ### Java中的`clone`方法详解:浅拷贝与深拷贝 #### 一、引言 在Java中,`clone`方法提供了一种快速复制对象的方式。它属于`Object`类的一部分,但需要显式地在子类中声明并实现`Cloneable`接口才能正常使用。本文...

    java_2048_clone

    标题 "java_2048_clone" 暗示我们正在讨论一个使用 Java 语言实现的 2048 游戏克隆版。2048 是一款流行的小游戏,玩家通过上下左右滑动数字方块,每移动一次,所有方块都会向滑动方向移动,如果有两个相同数字的方块...

    Test_Clone.rar_Java编程_Java_

    在Java编程领域,"Clone"是一个重要的概念,它涉及到对象复制和对象的深拷贝、浅拷贝。本文将深入探讨Java中的克隆机制,如何实现以及它在实际编程中的应用。 首先,Java中的克隆(Clone)是通过实现`Cloneable`...

    streama_netflix_clone_v1.7.0_Netflixclone_

    【标题】"streama_netflix_clone_v1.7.0_Netflixclone_" 提供的是一个仿Netflix的在线视频流媒体平台的克隆脚本,版本号为v1.7.0。这个项目旨在创建一个与Netflix类似的用户体验,让用户能够在自建的平台上观看电影...

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

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

    Java深浅clone

    在Java编程语言中,`Cloneable`接口和`clone()`方法是两个重要的概念,它们用于对象复制。在本文中,我们将深入探讨Java中的浅克隆(shallow clone)和深克隆(deep clone),并结合测试代码进行分析。 首先,让...

    Java_SoloPi 自动化测试工具.zip

    熟悉Git命令,如clone、pull、push、branch和merge,是协作开发和版本管理的基础。 7. **构建工具Maven或Gradle**:Java项目通常会使用Maven或Gradle进行依赖管理和构建。了解它们的配置文件(pom.xml或build....

    Youtube_Clone_BackEnd:Java后端

    Youtube_Clone_BackEnd:Java后端

    java clone的小例子

    在Java编程语言中,`clone()`方法是一个非常重要的概念,特别是在对象复制和克隆方面。这个小例子将帮助初学者理解如何在Java中使用`clone()`来创建对象的副本。让我们深入探讨`clone()`方法以及它在实际编程中的...

    java中clone的用法实例

    clone的用法 希望有帮助,仅供参考 通过例子的分析,可以对克隆的方法有所深入了解

    Java clone方法使用

    详细的描述了Java中 clone方法使用

    JAVA_高级特性(hashCode,clone,比较器,Class反射,序列化)

    ### Java 高级特性详解 #### 一、`hashCode` ...正确地重写 `equals` 和 `hashCode` 方法、使用 `Comparator` 进行排序、利用反射机制和序列化技术,以及实现 `clone` 方法都是开发高质量 Java 应用程序的重要技能。

    java clone

    在Java编程语言中,`clone`是一个非常重要的概念,它涉及到对象复制和对象克隆。本文将深入探讨Java中的`clone`方法,包括其工作原理、使用场景、注意事项以及一些个人实践心得。 首先,让我们理解什么是`clone`。...

    JAVA_对象克隆

    在Java中,对象克隆可以通过实现`Cloneable`接口并覆盖`Object`类中的`clone()`方法来完成。`Cloneable`接口本身不包含任何方法,只是一个标记接口,表明该类的对象可以被克隆。`Object`类的`clone()`方法会创建一个...

    java_lessons:Java课程

    java_课程这是井口的java课成员井口隆档案档案你好世界HelloWorld.class如何释放小贴士使用 git clone 将任何远程存储库带到本地 iguchitakashi@ta-mac $ git clone https://github.com/iguchikoma/java_lessons.git...

    java_性能调优.pdf

    文档还展示了Java中关于单例模式的实现,提到了静态类变量的使用以及clone方法的重写。在Java中,单例模式保证一个类只有一个实例,并提供全局访问点。通过使用static变量和私有构造函数,可以实现单例。而clone方法...

    Java_公共存储库文档示例.zip

    4. **版本控制**:由于提到了Git仓库,所以示例可能包含版本控制的使用,如git clone、git commit、git push等操作。 5. **Jenkins集成**:从"cloudbees-examples_master.zip"推断,这部分可能涉及到Jenkins的配置...

    Java中clone方法共6页.pdf.zip

    在Java编程语言中,`clone()`方法是一个非常重要的概念,特别是在对象复制和克隆方面。这个方法来源于`java.lang.Object`类,所有Java类都默认继承了这个方法。本资料"Java中clone方法共6页.pdf.zip"可能包含了关于...

    JAVA_bject类讲解

    Java中的`Object`类是所有Java类的根类,无论你定义的类是否明确声明了继承自某个类,它都隐式地继承自`Object`。`Object`类位于`java.lang`包中,这个包是Java的标准库,所以在编写Java程序时无需显式导入即可使用`...

    espresso_2_clone:浓缩咖啡的克隆 2

    "espresso_2_clone"项目看起来是一个针对"浓缩咖啡机"的软件克隆或复制品,版本号为2。...如果你对这个项目感兴趣,你可以下载"espresso_2_clone-master"压缩包,解压后用Java IDE打开,逐步探索其中的代码细节。

Global site tag (gtag.js) - Google Analytics