1.什么是深拷贝与浅拷贝
何为深拷贝?完全复制一个新的对象,需要申请新的内存空间;
浅拷贝,就是仅仅将引用赋值。
int a = 3;
int b = a;
a=4;
这是深拷贝,改变a的值不会影响到b;
Object x = new Object();
Object y = x;
浅拷贝,改变x的内容,相当于与改变b的内容,反之亦然。
2.决定深浅拷贝的原因
Java中实现拷贝的方式有三种,运算符“=”,clone()以及拷贝构造函数。上诉例子同样用 “=” 去赋值操作,而且Java中又不支持运算符的重载,为何一个为深,一个为浅?
我们先来看一下java的内存管理
Java的内存分为两种,一种是栈,一种是堆
栈的空间较小,读写速度较快,生命周期固定,超出作用域就会释放掉,栈中除了保存基本类型(int string float double...)以外,还会保存对象的引用。
堆的空间较大,读写相对较慢,一旦申请了空间,只能等待GC回收,堆中保存的是new的对象。
int a = 3;在栈中分配一个名为a的引用,然后去寻找是否有为常量3的地址,如果存在将a指向3的地址,否则开辟一个新的空间保存3,并将地址赋给a;
int b= a;将a的值赋值给b,也就是3的地址;
a = 4; 寻找是否有为常量4的地址,有的话,将a指向4的地址,否则开辟新空间
可见,对于基本类型,利用 = 给变量赋值时,会将变量指向新的地址,所以a重新赋值时,a,b指向的地址已经不同,所以a,b互不干扰,也就是深拷贝
Object x = new Object();对于Object x,对象引用,会存储在栈中,new Object()在堆中申请空间;之后将其首地址返回给对象的引用x
Object y = x;将x赋值给y,也就是new Object()的首地址
当x对其成员变量操作时,所在的内存空间仍然是建立Object时的对象空间,而y指向的也就是那个地址,一变俱变,也就是浅拷贝。
对于拷贝构造函数,我们来看一段Arraylist的源代码:
public ArrayList(Collection<? extends E> collection) {
Object[] a = collection.toArray();
if (a.getClass() != Object[].class) {
Object[] newArray = new Object[a.length];
System.arraycopy(a, 0, newArray, 0, a.length);
a = newArray;
}
array = a;
size = a.length;
}
a先指向被拷贝的对象collection的地址,然后newArray在堆中申请了新的空间,最后a指向了newArray
可见对于Arraylist的拷贝构造函数,是会在堆中重新申请新的空间,也就是深拷贝。
当然你也可以将你自己的拷贝构造函数简单的定义为一个引用的赋值,也就是浅拷贝。
对于clone而言,类似于拷贝构造函数,它取决于你去实现这个clone()方法时,是简单的引用赋值还是new新的object
可见,对于基本类型,都是深拷贝,这由栈处理变量的机制有关;
对于自定义的对象,由于引用和对象空间分配在不同的存储中,取决于你在拷贝时是否选择在堆中去申请新的对象空间。
Note:
对于String,Integer这种,如果直接赋值,String str = "qwqw" 遵循基本类型,如果利用new去创建,遵循自定义对象。