谁家玉笛暗飞声
散入春风满洛城
往期回顾✨
内部类
目录✨
引用拷贝
介绍
总结
浅拷贝
介绍
浅拷贝的步骤
深拷贝
介绍
引用拷贝
介绍
引用拷贝就是我们常用的 “赋值” ,只是复制了原对象的引用,即两个对象指向同一块内存堆地址。修改其中的一个对象会影响到另一个对象
例子 ~
当我们想要复制一个对象时,最自然的操作就是将该对象直接赋值给另一个对象
如下图:Student s2 = s1
class Student{ private String name; private int age; public Student(String name, int age) { this.name = name; this.age = age; } public Student() { this.name = "张三"; this.age = 18; } public void StudentPrint(){ System.out.println("name:"+name+",age:"+age); } } class Test1 { public static void main(String[] args) { Student s1 = new Student("李四",20); Student s2 = s1; s1.StudentPrint(); s2.StudentPrint(); System.out.println("========"); System.out.println(s1); System.out.println(s2); } }
输出的结果为:
name:李四,age:20 name:李四,age:20 ======== Student@3d494fbf Student@3d494fbf
根据打印结果我们可以知道:引用拷贝会生成一个新的对象引用地址,但是两个最终指向依然是同一个对象
总结
引用拷贝的优点在于能够快速的实现 “拷贝” (一个变量拥有另一个变量的所有属性)。缺点也很明显,由于所指向的是同一个对象,所以任意一个变量操作了对象的属性,都会影响另一个变量
思考
如何创建一个对象,将目标对象的内容复制过来而不是直接拷贝引用呢?
--- 这里就要介绍我们的深浅拷贝了
浅拷贝
介绍
浅拷贝会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝
对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。因为是两份不同的数据,所以对其中一个对象的该成员变量值进行修改,不会影响另一个对象拷贝得到的数据 对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。
浅拷贝的步骤
Object 中已经有 clone() 克隆方法因为是 native 方法,底层已经实现了拷贝对象的逻辑,只要重写即可 |
注意一定要标注 Cloneable接口,说明这个类对象是可以被拷贝的 |
具体案例如下:
class Money{
public double money;
public Money() {
this.money = 13.14;
}
public Money(double money) {
this.money = money;
}
void MoneyPrint(){
System.out.println(" Money: "+money);
}
}
class Student implements Cloneable{
public String name;
public int age;
public Money m = new Money();
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
protected Object clone(){
try {
return super.clone();
}catch (CloneNotSupportedException e){
throw new AssertionError();
}
}
public void PrintStudent(){
System.out.print("Student name: " + name+" age: " + age);
m.MoneyPrint();
}
}
class Test{
public static void main(String[] args) {
Student student = new Student("张三",18);
student.PrintStudent();
Student student1 = (Student) student.clone();
student1.PrintStudent();
student1.m.money = 3.14;
student1.name = "李四";
student1.age = 20;
System.out.println("=============");
student1.PrintStudent();
student.PrintStudent();
System.out.println("=============");
System.out.println(student.m);
System.out.println(student1.m);
}
}
这里再提一下写代码过程中的注意事项:
<1> 因为使用的是 Object 中的 clone(),编译器调用的时候会抛出异常,这里一定要写异常捕捉的·代码否则会报错
@Override
protected Object clone(){
try {
return super.clone();
}catch (CloneNotSupportedException e){
throw new AssertionError();
}
}
<2> 因为我们调用的是 Object 的克隆,所以类型是 Object,这里就会出现类型不匹配的问题,只需向下转型即可
Student student1 = (Student) student.clone();
输出结果:
Student name: 张三 age: 18 Money: 13.14
Student name: 张三 age: 18 Money: 13.14
=============
Student name: 李四 age: 20 Money: 3.14
Student name: 张三 age: 18 Money: 3.14
=============
com.thz.Money@70177ecd
com.thz.Money@70177ecd
通过以上结果以上结论的正确性:
我们的基础类型是两个不同的对象,而引用类型是共用一个对象(两个Money的地址相同)
思考
那么如何让浅拷贝进行的更彻底呢?-- 将共用的引用空间也进行拷贝,让两个对象互不干扰
深拷贝
介绍
深拷贝是一种完全拷贝,无论是基本类型还是引用类型都会完完全全的拷贝一份,在内存中生成一个新的对象。这样拷贝对象和被拷贝对象没有任何关系,互不影响
那么深拷贝该如何实现呢?
-- 我们可以在浅拷贝的基础上,对其内部的引用对象进行拷贝即可
深拷贝与通过重写 clone 方法实现浅拷贝的基本思路一样,只需要为对象图的每一层的每一个对象都实现 Cloneable 接口并重写 clone 方法,最后在最顶层的类的重写的 clone 方法中调用所有的 clone 方法即可实现深拷贝。简单的说就是:每一层的每个对象都进行浅拷贝就是深拷贝 |
案例如下:
class Money implements Cloneable {
public double money;
public Money() {
this.money = 13.14;
}
public Money(double money) {
this.money = money;
}
void MoneyPrint(){
System.out.println(" Money: "+money);
}
@Override
protected Object clone(){
try {
return super.clone();
}catch (CloneNotSupportedException e){
throw new AssertionError();
}
}
}
class Student implements Cloneable{
public String name;
public int age;
public Money m = new Money();
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
protected Object clone(){
try {
Student tmp = (Student)super.clone();
tmp.m = (Money)m.clone();
return tmp;
}catch (CloneNotSupportedException e){
throw new AssertionError();
}
}
public void PrintStudent(){
System.out.print("Student name: " + name+" age: " + age);
m.MoneyPrint();
}
}
class Test{
public static void main(String[] args) {
Student student = new Student("张三",18);
student.PrintStudent();
Student student1 = (Student) student.clone();
student1.PrintStudent();
student1.m.money = 3.14;
student1.name = "李四";
student1.age = 20;
System.out.println("=============");
student1.PrintStudent();
student.PrintStudent();
System.out.println("=============");
System.out.println(student.m);
System.out.println(student1.m);
}
}
输出结果
Student name: 张三 age: 18 Money: 13.14
Student name: 张三 age: 18 Money: 13.14
=============
Student name: 李四 age: 20 Money: 3.14
Student name: 张三 age: 18 Money: 13.14
=============
com.thz.Money@70177ecd
com.thz.Money@1e80bfe8
通过以上案例可以知道:
我们在浅拷贝的基础上重写了引用对象 Moeny 的克隆方法,并且在 Student 的拷贝中调用了这个方法。此时两个对象的 Money 的地址就不同了,所以当一个类中所有每个成员都实现克隆,并且在最顶层的 clone 中调用,那么就是深拷贝 ~