java源码分析-Object类clone方法
先看一下源码:
/**
* Creates and returns a copy of this object. The precise meaning
* of "copy" may depend on the class of the object. The general
* intent is that, for any object {@code x}, the expression:
* <blockquote>
* <pre>
* x.clone() != x</pre></blockquote>
* will be true, and that the expression:
* <blockquote>
* <pre>
* x.clone().getClass() == x.getClass()</pre></blockquote>
* will be {@code true}, but these are not absolute requirements.
* While it is typically the case that:
* <blockquote>
* <pre>
* x.clone().equals(x)</pre></blockquote>
* will be {@code true}, this is not an absolute requirement.
* <p>
* By convention, the returned object should be obtained by calling
* {@code super.clone}. If a class and all of its superclasses (except
* {@code Object}) obey this convention, it will be the case that
* {@code x.clone().getClass() == x.getClass()}.
* <p>
* By convention, the object returned by this method should be independent
* of this object (which is being cloned). To achieve this independence,
* it may be necessary to modify one or more fields of the object returned
* by {@code super.clone} before returning it. Typically, this means
* copying any mutable objects that comprise the internal "deep structure"
* of the object being cloned and replacing the references to these
* objects with references to the copies. If a class contains only
* primitive fields or references to immutable objects, then it is usually
* the case that no fields in the object returned by {@code super.clone}
* need to be modified.
* <p>
* The method {@code clone} for class {@code Object} performs a
* specific cloning operation. First, if the class of this object does
* not implement the interface {@code Cloneable}, then a
* {@code CloneNotSupportedException} is thrown. Note that all arrays
* are considered to implement the interface {@code Cloneable} and that
* the return type of the {@code clone} method of an array type {@code T[]}
* is {@code T[]} where T is any reference or primitive type.
* Otherwise, this method creates a new instance of the class of this
* object and initializes all its fields with exactly the contents of
* the corresponding fields of this object, as if by assignment; the
* contents of the fields are not themselves cloned. Thus, this method
* performs a "shallow copy" of this object, not a "deep copy" operation.
* <p>
* The class {@code Object} does not itself implement the interface
* {@code Cloneable}, so calling the {@code clone} method on an object
* whose class is {@code Object} will result in throwing an
* exception at run time.
*
* @return a clone of this instance.
* @throws CloneNotSupportedException if the object's class does not
* support the {@code Cloneable} interface. Subclasses
* that override the {@code clone} method can also
* throw this exception to indicate that an instance cannot
* be cloned.
* @see java.lang.Cloneable
*/
protected native Object clone() throws CloneNotSupportedException;
通过源码和注释我们注意到以下几点:
1、Object类中的clone()方法是一个native方法,即本地方法;
2、Object类中的clone()方法被protected修饰,被子累重写后该方法的属性变为public;
3、Object类中的clone()方法返回一个Object对象,也就是说我们必须进行强制类型转换才能得到我们需要的类型。
那么clone方法到底是什么功能呢?
其实简单说,clone方法就是复制一个对象并返回。那么这个赋值的对象与元对象有什么关联呢?在搞清楚clone方法的原理之前,我们先来了解一下两个概念:深拷贝和浅拷贝。
深拷贝和浅拷贝
深拷贝
被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不是原有的那些被引用的对象。换言之,深层复制要复制的对象引用的对象都复制一遍。
我们用JVM内存图理解一下:
我们定义一个Person类,它含有两个成员变量age,name:
class Person {
private int age;
private String name;
public Person() {
}
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Person类中含有两个成员变量,一个是基本类型age,根据定义基本类型的拷贝直接进行值拷贝,而name是引用类型String,进行也需要赋值新的对象,就相当于新new String(“zhangsan”)这个对象。所以当我们进行深拷贝时,实际内存情况如下图:
浅拷贝
被复制的对象的所有成员属性都有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅层复制仅仅复制所考虑的对象,而不复制它所引用的对象。
进行浅拷贝时,基本类型直接对值进行拷贝,而对引用类型进行拷贝是实际是只进行了引用的复制,即不同的引用指向相同的对象,内存分布如下:
clone方法是深拷贝or浅拷贝
我们用实例来验证:
package test.java.lang;
/**
* 分析Object类的clone方法
*/
public class ObjectCloneDemo {
public static void main(String[] args) throws CloneNotSupportedException {
PersonDemo p1 = new PersonDemo("张三",18);
PersonDemo p2 = (PersonDemo) p1.clone();
System.out.println(p1 == p2);
System.out.println(p1.getName() == p2.getName());
}
}
class PersonDemo implements Cloneable {
private String name;
private int age;
public PersonDemo() {
}
public PersonDemo(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
打印结果:
p1.getName() == p2.getName()返回true,说明这是一个浅拷贝啊,只是复制了引用。对于PersonDemo类中有int基本类型成员,直接将一个4字节的整数值拷贝过来就行。在clone方法调用时,会复制PersonDemo对象,用于拷贝基本数据,对于String类型的name字段来说,它实际指向一个String对象,那么复制的时候有两种方式,一种直接复制引用指向相同的对象;另一种是赋值一个新对象,复制引用指向这个新对象;很明显Object类的clone方法采用了第一种方式,即浅拷贝的方式。
重写clone方法实现深拷贝
我们知道了clone方法是浅拷贝方式,那么有没有办法实现深拷贝呢?
是有的, 我们需要实现Clonable接口,覆盖并实现clone方法,除了调用父类中的clone方法得到新的对象, 还要将该类中的引用变量也clone出来。如果只是用Object中默认的clone方法,是浅拷贝的,再次以下面的代码验证:
package test.java.lang;
/**
* 分析Object类的clone方法
*/
public class ObjectCloneDemo2 {
public static void main(String[] args) throws CloneNotSupportedException {
BodyDemo body = new BodyDemo();
PersonDemo2 p1 = new PersonDemo2(body, 18);
PersonDemo2 p2 = (PersonDemo2) p1.clone();
System.out.println(p1 == p2);
System.out.println(p1.getBody() == p2.getBody());
}
}
class PersonDemo2 implements Cloneable {
private BodyDemo body;
private int age;
public PersonDemo2() {
}
public PersonDemo2(BodyDemo body, int age) {
this.body = body;
this.age = age;
}
public BodyDemo getBody() {
return body;
}
public void setBody(BodyDemo body) {
this.body = body;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class BodyDemo{
}
这段代码和之前几乎没有什么区别,我们也能知道结果,由于clone是浅拷贝,那么结果是false、true;
结果:
那么要实现深拷贝,对于引用类型的成员变量就需要该类型实现实现Clonable接口,并覆盖并实现clone方法;
实现Cloneable接口
首先,看一下源码:
1 public interface Cloneable {
2 }
Cloneable接口是一个空接口仅仅是一个标志,而且这个标志也仅仅是针对 Object类中 clone()方法的,如果 clone 类没有实现 Cloneable 接口,并调用了 Object 的 clone() 方法(也就是调用了 super.Clone() 方法),那么Object 的 clone() 方法就会抛出 CloneNotSupportedException 异常。
我们修改一下代码:
package test.java.lang;
/**
* 分析Object类的clone方法
*/
public class ObjectCloneDemo2 {
public static void main(String[] args) throws CloneNotSupportedException {
BodyDemo body = new BodyDemo();
PersonDemo2 p1 = new PersonDemo2(body, 18);
PersonDemo2 p2 = (PersonDemo2) p1.clone();
System.out.println(p1 == p2);
System.out.println(p1.getBody() == p2.getBody());
}
}
class PersonDemo2 implements Cloneable {
private BodyDemo body;
private int age;
public PersonDemo2() {
}
public PersonDemo2(BodyDemo body, int age) {
this.body = body;
this.age = age;
}
public BodyDemo getBody() {
return body;
}
public void setBody(BodyDemo body) {
this.body = body;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public Object clone() throws CloneNotSupportedException {
PersonDemo2 personDemo2 = (PersonDemo2) super.clone();
personDemo2.body = (BodyDemo) body.clone();//需要调用body的clone方法
return personDemo2;
}
}
class BodyDemo implements Cloneable{
@Override
protected Object clone() throws CloneNotSupportedException {//重写clone方法
return super.clone();
}
}
执行程序:
这样我们就是先了深拷贝了,也就是复制了一个全新的personDemo2对象了。内部的引用类型Body也是一个新对象,而不是简单的引用复制了。
这里在多问一句,如果成员变量BodyDemo中还有其他引用类型作为成员变量时,这个时候如何实现深拷贝。答案还是相同的:需要将BodyDemo对象中的引用类型也实现Cloneable接口,同时重写clone方法。
这个过程就像套娃一样,只要某个对象的成员变量是引用类型,要实现完全的深拷贝,就要将每一层依赖的引用类型都要事先实现Cloneable接口,同时重写clone方法。
例如:
package test.java.lang;
/**
* 分析Object类的clone方法
*/
public class ObjectCloneDemo2 {
public static void main(String[] args) throws CloneNotSupportedException {
HeadDemo headDemo = new HeadDemo();
BodyDemo body = new BodyDemo(headDemo);
PersonDemo2 p1 = new PersonDemo2(body, 18);
PersonDemo2 p2 = (PersonDemo2) p1.clone();
System.out.println(p1 == p2);
System.out.println(p1.getBody() == p2.getBody());
System.out.println(p1.getBody().getHeadDemo() == p2.getBody().getHeadDemo());
}
}
class PersonDemo2 implements Cloneable {
private BodyDemo body;
private int age;
public PersonDemo2() {
}
public PersonDemo2(BodyDemo body, int age) {
this.body = body;
this.age = age;
}
public BodyDemo getBody() {
return body;
}
public void setBody(BodyDemo body) {
this.body = body;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public Object clone() throws CloneNotSupportedException {
PersonDemo2 personDemo2 = (PersonDemo2) super.clone();
personDemo2.body = (BodyDemo) body.clone();
return personDemo2;
}
}
class BodyDemo implements Cloneable{
private HeadDemo headDemo;
public BodyDemo() {
}
public BodyDemo(HeadDemo headDemo) {
this.headDemo = headDemo;
}
public HeadDemo getHeadDemo() {
return headDemo;
}
public void setHeadDemo(HeadDemo headDemo) {
this.headDemo = headDemo;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class HeadDemo{
}
执行结果:
BodyDemo中依赖HeadDemo,但是HeadDemo没有实现Cloneable,和重现clone方法,那么clone时就不是深拷贝了,在HeadDemo这一层复制时,就是引用复制,并没有赋值新的HeadDemo对象。
要想实现完全深拷贝,就要按上面所说,HeadDemo也要实现实现Cloneable,和重现clone方法。
package test.java.lang;
/**
* 分析Object类的clone方法
*/
public class ObjectCloneDemo2 {
public static void main(String[] args) throws CloneNotSupportedException {
HeadDemo headDemo = new HeadDemo();
BodyDemo body = new BodyDemo(headDemo);
PersonDemo2 p1 = new PersonDemo2(body, 18);
PersonDemo2 p2 = (PersonDemo2) p1.clone();
System.out.println(p1 == p2);
System.out.println(p1.getBody() == p2.getBody());
System.out.println(p1.getBody().getHeadDemo() == p2.getBody().getHeadDemo());
}
}
class PersonDemo2 implements Cloneable {
private BodyDemo body;
private int age;
public PersonDemo2() {
}
public PersonDemo2(BodyDemo body, int age) {
this.body = body;
this.age = age;
}
public BodyDemo getBody() {
return body;
}
public void setBody(BodyDemo body) {
this.body = body;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public Object clone() throws CloneNotSupportedException {
PersonDemo2 personDemo2 = (PersonDemo2) super.clone();
personDemo2.body = (BodyDemo) body.clone();
return personDemo2;
}
}
class BodyDemo implements Cloneable{
private HeadDemo headDemo;
public BodyDemo() {
}
public BodyDemo(HeadDemo headDemo) {
this.headDemo = headDemo;
}
public HeadDemo getHeadDemo() {
return headDemo;
}
public void setHeadDemo(HeadDemo headDemo) {
this.headDemo = headDemo;
}
@Override
protected Object clone() throws CloneNotSupportedException {
BodyDemo bodyDemo = (BodyDemo) super.clone();
bodyDemo.headDemo = (HeadDemo) this.headDemo.clone();
return bodyDemo;
}
}
class HeadDemo implements Cloneable{
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
执行结果:
这样就实现了真正的深拷贝了。内存分布如下图: