0x00 浅拷贝和深拷贝
浅拷贝会创建一个新对象,然后将当前对象的非静态字段复制到该新对象,如果字段是值类型的,那么对该字段执行复制**。如果该字段是引用类型的话,则复制引用但不复制引用的对象。因此,原始对象及其副本引用同一个对象**。看如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| public class Main {
static class Test { private ArrayList<String> strings = new ArrayList<>();
public void addString(String s) { strings.add(s); }
@Override public String toString() { StringBuilder stringBuilder = new StringBuilder(); for (String s : strings) { stringBuilder.append(s).append(" "); } return stringBuilder.toString(); } }
public static void main(String[] args) { Test t1 = new Test(); t1.addString("Hello"); System.out.println(t1); Test t2 = t1; t2.addString("World!"); System.out.println(t1); } }
|
此代码的输出值为:
因为代码Test t2 = t1;
为浅拷贝,对象t2
中的strings
为引用类型变量,在拷贝的过程中,仅仅是把t1
中的那个strings
的地址赋值给t2
中的引用类型变量strings
,其仍等于对象t1
中的那个strings
所指向的内存地址值。所以当我们使用t2.addString("World!");
时,仍然相当于给t1
中的strings
添加元素。所以第二行的输出为Hello World!
。
深拷贝则恰恰相反,深拷贝会创建一个新对象,然后将当前对象的非静态字段复制到该新对象,无论该字段是值类型的还是引用类型,都复制独立的一份。当你修改其中一个对象的任何内容时,都不会影响另一个对象的内容。
0x01 深拷贝的实现
深拷贝的实现需要类实现Cloneable
接口,而且需要重写其clone
方法,以手动实现深拷贝。当拷贝的时候,需要调用clone
方法进行深拷贝,如果仍然使用=
赋值,那么结果仍为浅拷贝。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| public class Main {
static class Test implements Cloneable { private ArrayList<String> strings = new ArrayList<>();
public void addString(String s) { strings.add(s); }
@Override protected Object clone() throws CloneNotSupportedException { Test test = (Test) super.clone(); test.strings = new ArrayList<>(); for (String s : strings) { test.addString(s); } return test; }
@Override public String toString() { StringBuilder stringBuilder = new StringBuilder(); for (String s : strings) { stringBuilder.append(s).append(" "); } return stringBuilder.toString(); } }
public static void main(String[] args) throws CloneNotSupportedException { Test t1 = new Test(); t1.addString("Hello"); System.out.println(t1); Test t2 = (Test) t1.clone(); t2.addString("World!"); System.out.println(t1); } }
|
此代码输出即为:
因为t2
和t1
中的strings
分别为指向两块不同内存区域的引用类型变量。当然我们也可以通过对象的序列化来实现深拷贝。