Java代码
class User implements Cloneable { String name; int age; @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } class User implements
cloneable {
String name;
int age;
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
Java代码
// 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);
// 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的引用指向原型对象新的克隆体。
给个例子。
Java代码
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倍左右。
原文地址:
可以对考的文章:
?uid=111253&do=blog&id=8905
以上就是关于cloneable的全部内容,相信你一定会非常满意。
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-3899-2.html
联想不思进取