先看看object对象中的源代码:
/**
* 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;
从注释内容中我们可以得到以下几点信息:
- 这个方法能创建并返回对象的拷贝。
- 返回的拷贝对象包含原有对象拥有的属性值。
- 拷贝分为浅拷贝和深拷贝。
- 如果这个类没有实现Cloneable接口,那么对象在调用clone()方法时将报异常。
然而我们在什么情况下使用这个方法呢?试想这样一种情况:我们创建了一个对象,这个对象中有很多属性,我们已经为这些属性赋了值,当我们想创建一个新的对象时我们就可以使用这个方法了。当然你可能会说为什么不直接new一个新的对象呢,但是别忘了在new新的对象时,我们的新对象中的属性是初始值,在使用clone方法复制这个对象时,我们获得的对象可以拥有原有对象的属性值。
先写几个类试一下这个功能:
1.Student类:
public class Student implements Cloneable {
private String name;
private int age;
private Teacher teacher;
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;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
@Override
protected Student clone() throws CloneNotSupportedException {
Student stu = (Student) super.clone();
return stu;
}
}
2.Teacher类:
public class Teacher {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
3.测试类:
@Test
public void test_shallowCopy() throws CloneNotSupportedException {
Teacher t1 = new Teacher();
t1.setName("t1");
Student student = new Student();
student.setName("s1");
student.setAge(10);
student.setTeacher(t1);
Student clone = student.clone();
System.out.println("原来对象的地址:" + student);
System.out.println("拷贝对象的地址:" + clone);
System.out.println("原来对象的教师地址:" + student.getTeacher());
System.out.println("拷贝对象的教师地址:" + student.getTeacher());
//测试对字符串修改是否影响原来对象,不同则不影响
clone.setName("s2");
System.out.println("原来对象的名字:" + student.getName());
System.out.println("拷贝对象的名字:" + clone.getName());
//测试对基本类型修改是否影响原来对象,不同则不影响
clone.setAge(20);
System.out.println("原来对象的年龄:" + student.getAge());
System.out.println("拷贝对象的年龄:" + clone.getAge());
//测试对应用类型修改是否影响原来对象,不同则不影响
clone.getTeacher().setName("t2");
System.out.println("原来对象的教师名字:" + student.getTeacher().getName());
System.out.println("拷贝对象的教师名字:" + clone.getTeacher().getName());
}
输出结果:
原来对象的地址:clone.Student@4ee285c6
拷贝对象的地址:clone.Student@621be5d1
原来对象的教师地址:clone.Teacher@573fd745
拷贝对象的教师地址:clone.Teacher@573fd745
原来对象的名字:s1
拷贝对象的名字:s2
原来对象的年龄:10
拷贝对象的年龄:20
原来对象的教师名字:t2
拷贝对象的教师名字:t2
在内存中的体现如下:
由上可知我们在内存中创建了一个新的对象,但是新的对象中的引用类型变量teacher却和原有对象指向同一个地址,这意味着我们在更改clone对象的teacher属性时,我们不可避免的要更改原有对象(即上图student对象)的teacher属性。(注:你可能会问为什么String类型变量name也指向同一个地址为什么,改变拷贝对象String的值原来对象的String的值未改变,这是因为String类型是final修饰的不可改变,只能创建一个地址,当改变拷贝对象的name是name不再指向s1,而是指向新地址s2了)上述过程被称为浅拷贝,可以对基本数据类型和直接复制的String变量进行拷贝,但是无法对引用数据类型变量进行拷贝。这往往是我们不希望看到的结果,这时候我们就不能使用默认的浅拷贝了,应该使用深拷贝。
那么如何使用深拷贝呢?深拷贝即让原来对象和拷贝的新对象中的引用类型指向不同的地址,各自拥有一块内存空间。所以在本例中我们只需修改Student类的clone方法即可,修改如下:
@Override
protected Student clone() throws CloneNotSupportedException {
Student stu = (Student) super.clone();
//添加修改内容
Teacher tea = new Teacher();
tea.setName(stu.getTeacher().getName());
stu.setTeacher(tea);
return stu;
}
运行结果如下:
原来对象的教师名字:t1
拷贝对象的教师名字:t2
总结:当我们编写的类只有基本类型和String类型时,可以只使用浅拷贝,当有引用类型时应该使用深拷贝。