语雀链接
1、API
虽然clone方法定义在Object类中,但要正确使用它,我们需要在需要克隆的类中进行适当的设置和重写。 如果某个类的对象要想被克隆,则对象所在的类必须实现Cloneable接口,重写clone方法(来自Object的clone方法),否则报错CloneNotSupportedException Cloneable该接口没有定义任何方法,是一个标记方法
这是Java的一个设计缺陷。是个很糟糕的设计。如果Java在今天被重新设计一次的话,多半就不会设计成这样了。
当时的一些设计需求 / 限制是:
1 Java对象要支持clone功能。但不是所有Java对象都应该可以clone,而是要让用户自己标记出哪些类是可以clone的
2 clone()是一个特殊的多态操作,最好是有JVM的直接支持
3 早期Java不支持annotation。从Java 5开始支持。
4 早期Java支持接口形式的“声明多继承”
5 早期Java不支持任何“实现多继承”(简称“多继承”)。从Java 8开始可以通过接口的default method实现。
把上述几条结合起来,就得到了Cloneable接口这个糟糕的设计。
首先,我们要能标记出哪些类是可以clone的。在Java里,类型层面的元数据可以用几种方法来表示:
1 继承的基类
2 实现的接口
3 直接在Class文件中通过access flags实现的修饰符
4 使用annotation,无论是自定义的还是Java自带的
显然当初设计Java的时候,一个类是否应该支持clone,是一个重要的属性,但却还没重要到值得给它一个关键字修饰符来修饰class声明,于是不能用(3)。
然后Java类是单继承的,如果要出于标记目的而消耗掉“基类”这个资源,显然是有点别扭的(但想想看倒也不是完全不可以…),所以(1)也不太好。
那么就只剩下(2)和(4)了。可是早期Java不支持(4),就只剩下(2)了。
其次,clone()的语义有特殊性,最好是有JVM的直接支持,然后用户代码就算要自定义clone()最好也要调用JVM提供的基础实现然后再添加自己的功能(也就是大家经常简单的在clone()中先调用super.clone()的做法)。
JVM要直接支持,得在API里找地方来暴露出这个支持给Java代码调用才行啊。最直观的做法就是把clone()方法的基本实现放在一个所有可以clone的类都能访问到的基类中,让可clone的类继承这一实现。
但根据上面一点的讨论,我们不希望把clone()的基本实现放在一个特殊基类中,消耗掉Java类唯一的“基类”槽。那还能放哪里呢?干脆就放在java.lang.Object这个所有Java类的共通基类上吧。
所以说一个实现了Cloneable接口的类跟一个没实现该接口的类有啥区别呢?
从JVM的角度看,这就是一个标记接口而已。实现了就是打上cloneable标记,没实现就是没这个标记。 然后到clone()的基本实现中,JVM会去检测要clone的对象的类有没有被打上这个标记,有就让clone,没有就抛异常。就这么简单。
Java里的数组类型是由JVM直接实现的,没有对应的Java源码文件。具体JVM如何实现是它们的自由。
2、深拷贝 VS 浅拷贝
2-1、引入传递
下面的例子,p1、p2内存地址一样,无论修改p1还是p2,另一个都会收到影响
public class Test {
public static void main ( String[ ] args ) {
Person p1 = new Person ( ) ;
p1. setName ( "p1" ) ;
Person p2 = p1;
System. out. println ( p1) ;
System. out. println ( p2) ;
p1. setName ( "xxx" ) ;
System. out. println ( p1. getName ( ) ) ;
System. out. println ( p2. getName ( ) ) ;
}
}
2-2、浅拷贝
所有的java类都继承的java.lang.Object,Object里面有个native clone()方法
protected native Object clone ( ) throws CloneNotSupportedException;
Person.java 添加clone方法,发现上述代码运行会报错,CloneNotSupportedException
public class Person {
private String name;
public String getName ( ) {
return name;
}
public void setName ( String name ) {
this . name = name;
}
@Override
protected Object clone ( ) throws CloneNotSupportedException {
return super . clone ( ) ;
}
}
查看源码可知:native关键字修饰的方法,代表这个方法实现体被调用,是告知 jvm去调用非java代码编写的实现体,例如C语言编写的等。而 jvm能否去调用这个实现体,也就是根据咱们是否有实现了Cloneable这个接口做为标记
public class Person implements Cloneable {
private String name;
public String getName ( ) {
return name;
}
public void setName ( String name ) {
this . name = name;
}
@Override
protected Object clone ( ) throws CloneNotSupportedException {
return super . clone ( ) ;
}
}
public class Test {
public static void main ( String[ ] args) throws CloneNotSupportedException {
Person p1 = new Person ( ) ;
p1. setName ( "p1" ) ;
Person p2 = ( Person) p1. clone ( ) ;
System. out. println ( p1) ;
System. out. println ( p2) ;
p1. setName ( "xxx" ) ;
System. out. println ( p1. getName ( ) ) ;
System. out. println ( p2. getName ( ) ) ;
}
}
2-3、深拷贝
Person中还有一个Age类,Person实现了Cloneable接口,但是Age还没有
public class Person implements Cloneable {
private String name;
private Age age;
public String getName ( ) {
return name;
}
public void setName ( String name ) {
this . name = name;
}
public Age getAge ( ) {
return age;
}
public void setAge ( Age age ) {
this . age = age;
}
@Override
protected Object clone ( ) throws CloneNotSupportedException {
return super . clone ( ) ;
}
}
验证结果:Person是不同内存地址,但是Age内存地址一样
public class Test {
public static void main ( String[ ] args) throws CloneNotSupportedException {
Person p1 = new Person ( ) ;
p1. setName ( "p1" ) ;
Age age = new Age ( ) ;
age. setAge ( 10 ) ;
p1. setAge ( age) ;
Person p2 = ( Person) p1. clone ( ) ;
System. out. println ( p1) ;
System. out. println ( p2) ;
p1. setName ( "xxx" ) ;
p1. getAge ( ) . setAge ( 20 ) ;
System. out. println ( p1. getName ( ) ) ;
System. out. println ( p1. getAge ( ) ) ;
System. out. println ( p1. getAge ( ) . getAge ( ) ) ;
System. out. println ( p2. getName ( ) ) ;
System. out. println ( p2. getAge ( ) ) ;
System. out. println ( p2. getAge ( ) . getAge ( ) ) ;
}
}
Age也实现Cloneable接口,但是上述验证代码Age还是一样的内存地址
public class Age implements Cloneable {
private int age;
public int getAge ( ) {
return age;
}
public void setAge ( int age ) {
this . age = age;
}
@Override
protected Object clone ( ) throws CloneNotSupportedException {
return super . clone ( ) ;
}
}
@Override
protected Object clone ( ) throws CloneNotSupportedException {
Person person = ( Person) super . clone ( ) ;
person. age = ( Age) age. clone ( ) ;
return person;
}
验证结果如下:两个age已经是不同的内存地址了,这里就是深拷贝
public class Test {
public static void main ( String[ ] args) throws CloneNotSupportedException {
Person p1 = new Person ( ) ;
p1. setName ( "p1" ) ;
Age age = new Age ( ) ;
age. setAge ( 10 ) ;
p1. setAge ( age) ;
Person p2 = ( Person) p1. clone ( ) ;
System. out. println ( p1) ;
System. out. println ( p2) ;
p1. setName ( "xxx" ) ;
p1. getAge ( ) . setAge ( 20 ) ;
System. out. println ( p1. getName ( ) ) ;
System. out. println ( p1. getAge ( ) ) ;
System. out. println ( p1. getAge ( ) . getAge ( ) ) ;
System. out. println ( p2. getName ( ) ) ;
System. out. println ( p2. getAge ( ) ) ;
System. out. println ( p2. getAge ( ) . getAge ( ) ) ;
}
}
2-4、深拷贝 / 浅拷贝
浅拷贝: 指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象。 深拷贝 :深拷贝不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象。 上面的例子2-2里面,其实就是属于浅拷贝,对于clone方法来说,如果不对clone方法进行改造,那么默认的使用,都是浅拷贝。 上面的例子2-3里面,其实就是属于深拷贝
2、源码翻译
package java.lang;
/**
* A class implements the <code>Cloneable</code> interface to
* indicate to the {@link Object#clone()} method that it
* is legal for that method to make a
* field-for-field copy of instances of that class.
* 一个类实现Cloneable接口,以指示Object.clone()方法,该方法对于该类的实例进行现场复制是合法的。
* <p>
* Invoking Object's clone method on an instance that does not implement the
* <code>Cloneable</code> interface results in the exception
* <code>CloneNotSupportedException</code> being thrown.
* 在不实现Cloneable接口的实例上调用对象的克隆方法导致抛出异常CloneNotSupportedException 。
* <p>
* By convention, classes that implement this interface should override
* <tt>Object.clone</tt> (which is protected) with a public method.
* See {@link Object#clone()} for details on overriding this
* method.
* 按照惯例,实现此接口的类应使用公共方法覆盖Object.clone (受保护)。 有关覆盖此方法的详细信息,请参阅Object.clone() 。
* <p>
* Note that this interface does <i>not</i> contain the <tt>clone</tt> method.
* Therefore, it is not possible to clone an object merely by virtue of the
* fact that it implements this interface. Even if the clone method is invoked
* reflectively, there is no guarantee that it will succeed.
* 注意,此接口不包含clone方法。 因此,只能通过实现该接口的事实来克隆对象是不可能的。
* 即使克隆方法被反射地调用,也不能保证它成功。
*
* @author unascribed
* @see CloneNotSupportedException
* @see Object#clone()
* @since JDK1.0
*/
public interface Cloneable {
}