Welcome 微信登录

首页 / 操作系统 / Linux / Java clone方法使用详解

为了理解java的clone,有必要先温习以下的知识。 java的类型,java的类型分为两大类,一类为primitive,如int,另一类为引用类型,如String,Object等等。 java引用类型的存储,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的堆上内存的内容复制到这个新分配的内存空间上。 看例子。 Java代码 class User { String name; int age; } class Account implements Cloneable { User user; long balance; @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } Java代码 // 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,没有方法可以改变它的内部状态呢。 Java代码 class User implements Cloneable { String name; int age; @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } }