java 中的深拷贝与浅拷贝
1 什么是浅拷贝和深拷贝
浅拷贝 : 浅拷贝又叫浅复制,将对象中的所有字段复制到新的对象(副本)中。其中,值类型字段(java中8中原始类型)的值被复制到副本中后,在副本中的修改不会影响到源对象对应的值。而引用类型的字段被复制到副本中的还是引用类型的引用,而不是引用的对象,在副本中对引用类型的字段值做修改会影响到源对象本身。
深拷贝 : 将对象中的所有字段复制到新的对象中。不过,无论是对象的值类型字段,还是引用类型字段,都会被重新创建并赋值,对于副本的修改,不会影响到源对象本身。深拷贝简单归纳就是对象内部引用的对象均复制。
2 使用 = 进行复制操作
package cn.zs.copydemo;
public class Student {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
测试
package cn.zs.copydemo;
public class Test {
public static void main(String[] args) {
Student s1 = new Student();
s1.setAge(10);
s1.setName("aaa");
Student s2 = s1;
s2.setAge(20);
// s1.setAge(20);
System.out.println("name:"+s1.getName()+" age:"+s1.getAge());
System.out.println("name:"+s2.getName()+" age:"+s2.getAge());
}
}
- 在不添加 s2.setAge(20); s1.setAge(20);是的输出结果是
s1 ------->name:aaa age:10
s2 -------->name:aaa age:10
- 添加s2.setAge(20)或s1.setAge(20)后输出结果为
s1 ------->name:aaa age:20
s2 -------->name:aaa age:20
= 操作是是一种浅复制,副本或者主体的修改都会相互影响
3. 使用clone方法
3.1 基本类型
package cn.zs.copydemo;
public class Student implements Cloneable {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public Object clone() {
Student student = null;
try {
student = (Student) super.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return student;
}
}
测试
package cn.zs.copydemo;
public class Test {
public static void main(String[] args) {
Student s1 = new Student();
s1.setAge(10);
s1.setName("aaa");
Student s2 = (Student)s1.clone();
// s2.setAge(20);
s1.setAge(20);
System.out.println("s1 ------->name:"+s1.getName()+" age:"+s1.getAge());
System.out.println("s2 -------->name:"+s2.getName()+" age:"+s2.getAge());
}
}
- clone 是一种深拷贝,副本与主体的修改互不影响
- clone 的这种深拷贝特性适用于基本类型,数组和自定义类型只是一种引用
3.2 自定义类型
Address对象
package cn.zs.copydemo;
public class Address {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
Student对象
package cn.zs.copydemo;
public class Student {
private Address address;
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
测试
package cn.zs.copydemo;
public class Test {
public static void main(String[] args) {
Address address = new Address();
address.setAddress("ccccccc");
Student s1 = new Student();
s1.setAge(10);
s1.setName("aaa");
s1.setAddress(address);
Student s2 = (Student)s1.clone();
address.setAddress("ddd");
System.out.println("s1 ------->name:"+s1.getName()+" age:"+s1.getAge()+" address:"+s1.getAddress().getAddress());
System.out.println("s2 -------->name:"+s2.getName()+" age:"+s2.getAge()+" address:"+s2.getAddress().getAddress());
}
}
- addressde 修改会将副本和主体都修改
那如何避免这种情况?
- 为Address添加clone方法
@Override
protected Object clone() {
Address address = null;
try {
address = (Address) super.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return address;
}
- 修改Student的clone方法
@Override
public Object clone() {
Student student = null;
try {
student = (Student) super.clone();
student.address =(Address) address.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return student;
}
3. 拷贝构造函数
将Student的构造函数修改为
public Student() {
}
public Student(Student s) {
this.age = s.age;
this.name = s.name;
}
测试
package cn.zs.copydemo;
public class Test {
public static void main(String[] args) {
Student s1 = new Student();
s1.setAge(20);
s1.setName("ssss");
Student s2 = new Student(s1);
// s1.setAge(50);
s2.setAge(50);
System.out.println("s1 ------->name:"+s1.getName()+" age:"+s1.getAge());
System.out.println("s2 -------->name:"+s2.getName()+" age:"+s2.getAge());
}
}
- 副本和主体之间的修改互不影响