Java 语言设计
Java语言设计为:一个类的对象如果想要被克隆,这个类必须实现Cloneable接口。我们先看Cloneable接口:
![f93e08d82b4e392f4c352ac136db16b2.png](https://i-blog.csdnimg.cn/blog_migrate/639cf38edd8159c90c503ed4c79604f6.jpeg)
Java 8 Cloneable接口
- 一个类实现Cloneable接口,才可以合法调用Object类的clone方法。注意:Cloneable接口是空的,并不包含clone方法。clone方法是在Object类中被声明为protected。如果一个类没有实现Cloneable接口而调用clone方法,会抛出CloneNotSupportedException异常。
- 按照惯例实现Cloneable接口的类都要重写Object的clone方法。
既然clone方法再Object中,我们进入Object类看看clone方法的声明:
![da4f352e79a414e6b0e44a85f862968d.png](https://i-blog.csdnimg.cn/blog_migrate/74e177a37582ff7a2af58f4b53e5a885.jpeg)
Object类的clone方法
clone方法上面的声明太长了就不一块截下来了,可以自己进去看看。
clone方法用于创建此对象的拷贝。拷贝的确切含义可能取决于对象具体的类。一般意图是对于任何对象:
x.clone() == x; 返回falsex.clone().getClass() == x.getClass(); 返回true
虽然通常情况下
x.clone().equals(x); 返回true
但这些并不是绝对的要求。
所有实现了Cloneable接口的类都应该用一个共有的方法覆盖Object的clone方法,并且在该clone方法内部应该先调用super.clone()方法,然后再修正需要调整的域。因为这样才可以递归调用到Object的clone方法,从而创建出正确的克隆。否则如果我们直接再clone方法里通过构造器创建对象,就可能得不到预期的类。
所有数组都被认为是实现了Cloneable接口,并且数组类型 T [] 的clone方法的返回类型是 T [],其中T可以是任何引用类型或原始类型。
深拷贝和浅拷贝
先看一段代码:
![32f76c283fa11e49e087380878021479.png](https://i-blog.csdnimg.cn/blog_migrate/1a98f065d3e1e91d58410e8531f9a658.jpeg)
clone示例
上面这个代码输出结果为:
Person@4554617c/Person@4554617c/Person@74a14482
可以看大,p1和p2输出相同,p3输出不同。下面来解释一下:
看过Java虚拟机的都知道,Java运行时数据区的概念。在这个例子里面,p1、p2、p3三个变量会在栈中创建,new出来的剑桥大学张三这个Person对象在Java堆中创建。代码中,p1和p2指向的是Java堆中的同一个对象,所以输出相同。但p1.clone()语句是在Java堆中克隆一个新的张三,并被p3引用,这是两个对象,所以p3输出不同于p1和p2。
知道了这个概念,那么什么是深拷贝什么是浅拷贝?
如果我们把上面的输出语句换成:
System.out.println(p1.getSchool()+"/"+p2.getSchool()+"/"+p3.getSchool());
那么看一下输出:
School@6bc168e5/School@6bc168e5/School@6bc168e5
p1、p2、p3输出的school对象信息都一样。这是因为Java执行的是浅拷贝。虽然我克隆了Person,但Person里面的School并没有克隆,仅仅是将拷贝对象里面的school引用指向了原new出来的Person对象创建的School。也就是默认克隆仅仅克隆本对象,本对象内部引用的其他对象,仅仅是传递引用,并没有为它们克隆新的实例。那么Java如何进行深拷贝呢?我只需要为School类添加clone方法并且修改Person类的clone方法即可,主函数中p1、p2、p3赋值关系不变:
![35e5a57a08f68c17580b307f92c0837f.png](https://i-blog.csdnimg.cn/blog_migrate/118681a5a4ea9bf43ed4936ca88bbeb2.jpeg)
深拷贝
输出结果为:
School@4554617c/School@4554617c/School@74a14482
可以看出,p3的School也进行了拷贝。
如果School类中也组合了其他对象呢?如果需要深拷贝,School中组合的类也应该重写clone方法,并且在School类的clone方法中调用。也就是说这是一个clone调用链。如果想要进行完全深拷贝,必须保证这个clone调用链的完整。
其他文章:
为什么要重写equals方法和hashCode方法
如何理解清楚Java的抛出结构
Java泛型和通配符类型你真的能搞清楚吗
其实Java枚举类和普通类没什么区别
Java字符串:StringBuilder 和 StringBuffer