深拷贝和浅拷贝
cloneable
clone() 是 Object 的 protected ⽅法,⼀个类不显式去重写 clone(),其它类就不能直接去调⽤该类实例的 clone() ⽅法。
public class CloneExample {
private int a;
private int b; }
CloneExample e1 = new CloneExample();
// CloneExample e2 = e1.clone(); // 'clone()' has protected access in'java.lang.Object'
重写 clone() 得到以下实现:
public class CloneExample {
private int a;
private int b;
@Override
public CloneExample clone() throws CloneNotSupportedException {
return (CloneExample)super.clone();
}
}
CloneExample e1 = new CloneExample();
try {
CloneExample e2 = e1.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
java.lang.CloneNotSupportedException: CloneExample
以上抛出了 CloneNotSupportedException,这是因为 CloneExample 没有实现 Cloneable 接⼝。应该注意的是,clone() ⽅法并不是 Cloneable 接⼝的⽅法,⽽是 Object 的⼀个 protected ⽅法。 Cloneable 接⼝只是规定,如果⼀个类没有实现 Cloneable 接⼝⼜调⽤了 clone() ⽅法,就会抛出 CloneNotSupportedException。
public class CloneExample implements Cloneable {
private int a;
private int b;
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
基本类型和引用类型在内存上存储的区别
- 定义局部变量 age,由于age是局部变量,所以在栈中申请内存空间,起名为age,又由于给age赋的值250是基本类型,所以,值直接存储在栈中。
- 定义局部变量arr,由于arr是局部变量,所以在栈中申请空间,但是arr的内存中存储的是什么?由于给arr赋的值不是基本类型,而是引用类型(new出来的),所以,先在堆中申请空间存放数据 12,23,34,。再把堆区的地址赋给arr。
浅拷⻉
浅拷⻉:就是把栈中的数据拷贝一份,即拷⻉对象和原始对象的引⽤类型引⽤同⼀个对象。 基本数据类型各自有一份。
public class ShallowCloneExample implements Cloneable {
private int[] arr;
public ShallowCloneExample() {
arr = new int[10];
for (int i = 0; i < arr.length; i++) {
arr[i] = i;
}
}
public void set(int index, int value) {
arr[index] = value;
}
public int get(int index) {
return arr[index];
}
@Override//浅拷⻉
protected ShallowCloneExample clone() throws CloneNotSupportedException
{
return (ShallowCloneExample) super.clone();
}
}
ShallowCloneExample e1 = new ShallowCloneExample();
ShallowCloneExample e2 = null;
try {
e2 = e1.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
e1.set(2, 222);
System.out.println(e2.get(2)); // 222
- 深拷⻉
深拷⻉:拷⻉对象和原始对象的引⽤类型引⽤不同对象。即再浅拷贝的基础上,将所有非基本数据类型的属性,手动拷贝一份
public class DeepCloneExample implements Cloneable {
private int[] arr;
public DeepCloneExample() {
arr = new int[10];
for (int i = 0; i < arr.length; i++) {
arr[i] = i;
}
}
public void set(int index, int value) {
arr[index] = value;
}
public int get(int index) {
return arr[index];
}
@Override//深拷贝,将所有非基本数据类型的属性,手动拷贝一份。即int[] arr
protected DeepCloneExample clone() throws CloneNotSupportedException {
DeepCloneExample result = (DeepCloneExample) super.clone();
result.arr = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
result.arr[i] = arr[i];
}
return result;
}
}
DeepCloneExample e1 = new DeepCloneExample();
DeepCloneExample e2 = null;
try {
e2 = e1.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
e1.set(2, 222);
System.out.println(e2.get(2)); // 2
clone()的替代⽅案
使⽤ clone() ⽅法来拷⻉⼀个对象即复杂⼜有⻛险,它会抛出异常,并且还需要类型转换。Effective Java 书上讲到,最好不要使⽤ clone(),可以使⽤拷⻉构造函数或者拷⻉⼯⼚来拷⻉⼀个对象。 即通过传入一个实例对象,手动拷贝它
public class CloneConstructorExample {
private int[] arr;
public CloneConstructorExample() {
arr = new int[10];
for (int i = 0; i < arr.length; i++) {
arr[i] = i;
}
}
//拷贝构造函数, 一个实例对象,手动拷贝它
public CloneConstructorExample(CloneConstructorExample original) {
arr = new int[original.arr.length];
for (int i = 0; i < original.arr.length; i++) {
arr[i] = original.arr[i];
}
}
public void set(int index, int value) {
arr[index] = value;
}
public int get(int index) {
return arr[index];
}
}
CloneConstructorExample e1 = new CloneConstructorExample();
CloneConstructorExample e2 = new CloneConstructorExample(e1);
e1.set(2, 222);
System.out.println(e2.get(2)); // 2