什么是浅拷贝
浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。
直白描述:浅拷贝是指拷贝的对象和被拷贝后的对象有相同的内容,拷贝对象和被拷贝对象地址是不同的,对象中基本数据属性内容发生改变,不会影响另一个对象,但如果其中一个对象改变其引用属性的内容,另一个对象所对应的引用属性内容也会随着改变。
普通对象实现浅拷贝方式
1、对象实现 Cloneable 接口,复写clone方法
2、通过构造器注入对象赋值的方式克隆
public class Demo {
public static void main(String[] args) {
// clone 方式
cloneMethod();
// 构造器 方式
constructorMethod();
}
private static void constructorMethod() {
// 原始对象
Teacher teacher = new Teacher("JOJO", new Subject("物理"), 19);
System.out.println("原始对象: " + teacher);
// 拷贝对象
Teacher clonedTeacher = new Teacher(teacher);
System.out.println("拷贝对象: " + clonedTeacher);
// 原始对象和拷贝对象是否一样:
System.out.println("原始对象和拷贝对象是否一样: " + (teacher == clonedTeacher));
// 原始对象和拷贝对象的name属性是否一样
System.out.println("原始对象和拷贝对象的name属性是否一样: " + (teacher.getName() == clonedTeacher.getName()));
// 原始对象和拷贝对象的subj属性是否一样
System.out.println("原始对象和拷贝对象的subj属性是否一样: " + (teacher.getSubj() == clonedTeacher.getSubj()));
teacher.setName("JOJO阿");
teacher.getSubj().setName("音乐");
teacher.setAge(20);
System.out.println("更新后的原始对象: " + teacher);
System.out.println("更新原始对象后的克隆对象: " + clonedTeacher);
}
private static void cloneMethod() {
// 原始对象
Teacher teacher = new Teacher("JOJO", new Subject("物理"),19);
teacher.setAge(19);
System.out.println("原始对象: " + teacher);
// 拷贝对象
Teacher clonedTeacher = (Teacher) teacher.clone();
System.out.println("拷贝对象: " + clonedTeacher);
// 原始对象和拷贝对象是否一样:
System.out.println("原始对象和拷贝对象是否一样: " + (teacher == clonedTeacher));
// 原始对象和拷贝对象的name属性是否一样
System.out.println("原始对象和拷贝对象的name属性是否一样: " + (teacher.getName() == clonedTeacher.getName()));
// 原始对象和拷贝对象的subj属性是否一样
System.out.println("原始对象和拷贝对象的subj属性是否一样: " + (teacher.getSubj() == clonedTeacher.getSubj()));
teacher.setName("JOJO阿");
teacher.getSubj().setName("音乐");
teacher.setAge(20);
System.out.println("更新后的原始对象: " + teacher);
System.out.println("更新原始对象后的克隆对象: " + clonedTeacher);
}
public static class Teacher implements Cloneable {
// 对象引用
private Subject subj;
private String name;
private int age;
public Teacher(Teacher teacher) {
this.name = teacher.name;
this.subj = teacher.subj;
this.age = teacher.age;
}
public Teacher(String name, Subject subj, int age) {
this.name = name;
this.subj = subj;
this.age = age;
}
public Subject getSubj() {
return subj;
}
public void setSubj(Subject subj) {
this.subj = subj;
}
public String getName() {
return name;
}
public void setName(String s) {
name = s;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
/**
* 重写clone()方法
*
* @return
*/
public Object clone() {
//浅拷贝
try {
// 直接调用父类的clone()方法
return super.clone();
} catch (CloneNotSupportedException e) {
return null;
}
}
@Override
public String toString() {
return "Teacher{" + "subj=" + subj + ", name='" + name + '\'' + ", age=" + age + '}';
}
}
public static class Subject {
private String name;
public Subject(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "Subject{" + "name='" + name + '\'' + '}';
}
}
}
浅拷贝输出结果
原始对象: Teacher{subj=Subject{name='物理'}, name='JOJO', age=19}
拷贝对象: Teacher{subj=Subject{name='物理'}, name='JOJO', age=19}
原始对象和拷贝对象是否一样: false
原始对象和拷贝对象的name属性是否一样: true
原始对象和拷贝对象的subj属性是否一样: true
更新后的原始对象: Teacher{subj=Subject{name='音乐'}, name='JOJO阿', age=20}
更新原始对象后的克隆对象: Teacher{subj=Subject{name='音乐'}, name='JOJO', age=19}
什么是深拷贝
深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。
普通对象实现深拷贝方式
1、实现Cloneable接口,复写clone方法
/**
* 关键代码:
* 重写clone()方法
*
* @return
*/
public Object clone() {
// 深拷贝,创建拷贝类的一个新对象,这样就和原始对象相互独立
Teacher t = new Teacher(name, subj.getName(),age);
return t;
}
2、实现序列化,Java可通过实现Serializable接口。Android中也可以实现Parcelable接口来支持序列化。
基本步骤:
- 确保需要序列化的类都支持序列化
- 创建输入输出流
- 使用这个输入输出流来创建对象输入和对象输出流
- 将你想要拷贝的对象传递给对象输出流
- 从对象输入流中读取新的对象并且转换回你所发送的对象的类
集合的深浅拷贝
集合浅拷贝
- 默认List通过构造函数和 clone()方法进行拷贝都是浅拷贝
集合深拷贝
clone方式
- 注意:列表对象通过clone方法实现深拷贝和普通对象clone深拷贝不太一样
- 实现步骤如下:
- 1、类实现clone方法
- 2、类中引用属性也实现clone方法
- 3、并在类clone方法中给引用对象也进行clone赋值
- 4、创建一个新列表,把旧列表中元素通过clone方式添加进新列表
- 实现步骤如下:
// 主要代码1:给clone方法赋值
public Object clone() {
//浅拷贝
try {
Teacher teacher = (Teacher) super.clone();
// 关键代码,不给subj进行clone赋值,修改原始列表subject内容,拷贝列表对应subject也会发生变更
teacher.subj = (Subject) this.subj.clone();
return teacher;
} catch (CloneNotSupportedException e) {
return null;
}
}
// 主要代码2:创建新列表,添加clone元素,list是原始列表
List<Teacher> listTemp = new ArrayList<>();
listTemp.add((Teacher) list.get(0).clone());
listTemp.get(0).setAge(11);
listTemp.get(0).getSubj().setName("subjtemp");