1. 为什么需要拷贝
举一个例子,一个Student类,初始化创建一个student01实例,如果想让初始化创建student02实例和student01实例相同,你可能会这样做:Student student02 = student01,直接赋值。这样做是有问题的,student01这个对象实例放在Java堆中,如果你直接赋值,Java堆中并没有创建一个新的实例对象student02,而是让student02变量直接指向Java堆中的student01实例,如果你修改student02 或 student01 中任何一个对象实例,就是修改了Java堆中的那个唯一实例,另外一个对象实例也会随之改变,接下来我们用程序来测试一下。
首先创建一个Student类,代码如下所示:
package clone;
public class Student {
private String name;
private int 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 String toString() {
return "MyCloneStudy{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
初始化创建一个student01实例,然后通过Student student02 = student01来初始化创建student02实例,修改其中一个,然后查看另外一个的值是否改变。测试代码如下所示:
package clone;
public class MyCloneStudy {
public static void main(String args[]) {
Student student01 = new Student();
student01.setName("syrdbt");
student01.setAge(21);
System.out.println(student01);
Student student02 = student01;
System.out.println(student02);
System.out.println("修改student01:");
student01.setName("Jack");
student01.setAge(200);
System.out.println(student01);
System.out.println(student02);
System.out.println("修改student02:");
student01.setName("Rose");
student01.setAge(18);
System.out.println(student01);
System.out.println(student02);
}
}
运行截图如下所示,显然发生了上述的错误:
2. 浅拷贝
让Studen类实现 Cloneable 接口,Cloneable 接口是一个标识接口,实现了这个接口之后才可以调用Object类的clone方法,然后在Student类中重写clone方法,即可实现浅拷贝。代码如下所示。
Student类实现 Cloneable 接口,重写clone方法。
package clone;
public class Student implements Cloneable{
private String name;
private int 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 String toString() {
return "MyCloneStudy{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
protected Object clone(){
Object object = null;
try {
object = super.clone();
} catch (CloneNotSupportedException c) {
c.printStackTrace();
}
return object;
}
}
测试代码如下所示 ,通过student02 = (Student) student01.clone()来实现浅拷贝。
package clone;
public class MyCloneStudy {
public static void main(String args[]) {
Student student01 = new Student();
student01.setName("syrdbt");
student01.setAge(21);
System.out.println(student01);
Student student02 = null;
student02 = (Student) student01.clone();
System.out.println(student02);
System.out.println("修改student01:");
student01.setName("Jack");
student01.setAge(200);
System.out.println(student01);
System.out.println(student02);
System.out.println("修改student02:");
student01.setName("Rose");
student01.setAge(18);
System.out.println(student01);
System.out.println(student02);
}
}
运行截图如下所示
3. 深拷贝
浅拷贝有问题吗?显然是有问题的,看下面这个例子,把name这个属性修改成一个类。
Name类代码如下所示:
package clone;
public class Name {
private String name;
public Name(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Student 类如下所示:
package clone;
public class Student implements Cloneable{
private Name name;
private int age;
public Name getName() {
return name;
}
public void setName(Name name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name=" + name.getName() +
", age=" + age +
'}';
}
@Override
protected Object clone(){
Object object = null;
try {
object = super.clone();
} catch (CloneNotSupportedException c) {
c.printStackTrace();
}
return object;
}
}
测试代码:
package clone;
public class MyCloneStudy {
public static void main(String args[]) {
Name name = new Name("syrdbt");
Student student01 = new Student();
student01.setName(name);
student01.setAge(21);
System.out.println(student01);
Student student02 = (Student) student01.clone();
System.out.println(student02);
System.out.println("修改Name为Jack:");
name.setName("Jack");
System.out.println(student01);
System.out.println(student02);
}
}
运行测试代码,你会发现浅拷贝的错误,修改完Name之后,Student01和Student02的name都改变了。
如何解决这个问题,在Student类中将Name成员变量也进行拷贝。
Name 类实现了浅拷贝:
package clone;
public class Name implements Cloneable{
private String name;
public Name(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected Object clone(){
Object o = null;
try {
o = super.clone();
} catch (CloneNotSupportedException c) {
c.printStackTrace();
}
return o;
}
}
Student 类实现了深拷贝:
package clone;
public class Student implements Cloneable{
private Name name;
private int age;
public Name getName() {
return name;
}
public void setName(Name name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name=" + name.getName() +
", age=" + age +
'}';
}
@Override
protected Object clone(){
Student student = null;
try {
student = (Student) super.clone();
} catch (CloneNotSupportedException c) {
c.printStackTrace();
}
student.name = (Name) name.clone();
return student;
}
}
测试代码,如下所示:
package clone;
public class MyCloneStudy {
public static void main(String args[]) {
Name name = new Name("syrdbt");
Student student01 = new Student();
student01.setName(name);
student01.setAge(21);
System.out.println(student01);
Student student02 = (Student) student01.clone();
System.out.println(student02);
System.out.println("修改Name为Jack:");
name.setName("Jack");
System.out.println(student01);
System.out.println(student02);
}
}
运行结果,显然解决了“修改完Name之后,Student01和Student02的name都改变”这个问题。
END。