Java中关于拷贝的知识点
今天在刷leetcode题的时候,直接将一个对象赋值给另一个对象,然后对赋值后的对象进行操作,原对象也跟着发生了变化,因此,特地研究了一下关于Java中的拷贝。
从复制操作开始讲起
int a=10;
int b=a;
对于基本数据类型的复制,是将a的值复制一份,然后给b,此时,修改b的值,a的值并不会发生改变,这是值的复制。
ListNode a;
ListNode b;
b=a;
对于引用数据类型的复制,是将a的地址复制给b,所以此时b和a所指向的是同一个对象。
讲到这里,可能大家就会有点疑问了。为什么呢?
补充一下Java的内存机制
我们知道,JVM中分为栈区和堆区,对于基本数据类型,数据直接存放在栈区中;
对于引用数据类型,栈区中存放的是地址,而实际的值存放在堆区中。
了解了这个之后呢,我们就要接着讲一下引用拷贝和对象拷贝。
引用拷贝和对象拷贝
Teacher teacher = new Teacher("Taylor",26);
Teacher otherteacher = teacher;
System.out.println(teacher);
System.out.println(otherteacher);
输出结果
blog.Teacher@355da254
blog.Teacher@355da254
我们可以看到,二者的地址相同,所以两个变量指向的就是同一个对象,因此这就是引用拷贝,即
创建一个指向对象的引用变量的拷贝。
Teacher teacher = new Teacher("Swift",26);
Teacher otherteacher = (Teacher)teacher.clone();
System.out.println(teacher);
System.out.println(otherteacher);
输出结果
blog.Teacher@355da254
blog.Teacher@4dc63996
我们可以看到,二者的地址并不相同,因此是创建了新的对象,而不仅仅只是复制地址。这就是对象拷贝,即
创建对象本身的一个副本。
讲完了引用拷贝和对象拷贝,我们就要重点讲一下深拷贝和浅拷贝。
需要注意的是,深拷贝和浅拷贝都是对象拷贝。
浅拷贝和深拷贝
浅拷贝和深拷贝出现的背景:当一个引用数据类型中含有引用数据类型的属性。比如说最常见的自定义一个Person类,含有name和age两个属性,name是String类型,age是int类型。
由于age是基本数据类型, 那么对它的拷贝没有什么疑议,直接将一个4字节的整数值拷贝过来就行。但是name是String类型的, 它只是一个引用, 指向一个真正的String对象,那么对它的拷贝有两种方式: 直接将源对象中的name的引用值拷贝给新对象的name字段, 或者是根据原Person对象中的name指向的字符串对象创建一个新的相同的字符串对象,将这个新字符串对象的引用赋给新拷贝的Person对象的name字段。这两种拷贝方式分别叫做浅拷贝和深拷贝。
实现深拷贝
1.现在为了要在clone对象时进行深拷贝, 那么就要Clonable接口,覆盖并实现clone方法,除了调用父类中的clone方法得到新的对象, 还要将该类中的引用变量也clone出来。如果只是用Object中默认的clone方法,是浅拷贝的。
浅拷贝:
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
深拷贝:
@Override
protected Object clone() throws CloneNotSupportedException {
Body newBody = (Body) super.clone();
newBody.head = (Head) head.clone();
return newBody;
}
即将源对象引用的对象也要clone一份。
但如果引用的对象中又包含引用数据类型,则此时的深拷贝是不完全的深拷贝,要继续重写引用的对象中的clone方法。
即将源对象引用的对象也要clone一份。
但如果引用的对象中又包含引用数据类型,则此时的深拷贝是不完全的深拷贝,要继续重写引用的对象中的clone方法。