首页 > 代码库 > 对象复制 - 浅拷贝与深拷贝
对象复制 - 浅拷贝与深拷贝
前言
在前面的文章中,提到过Java之间如果发生了对象赋值,那么其意义是赋值的两个对象都指向同一片内存区域。
那么,如果我希望得到的是一份新的副本 - 即可以随意更改而不影响原始对象呢?
那就涉及到本文要探讨的话题 - 对象的浅拷贝与深拷贝。
浅拷贝
若对象之间发生浅拷贝,那么首先肯定的是会创建一个新的对象副本(这就不同与对象间的直接赋值).
然后所有域进行简单的直接复制 - 非对象域直接拷贝过来,对象域则拷贝此对象的地址(因为对象在java中的本质就是一个 "指针")。
如何进行浅拷贝
1. 为目标类继承Cloneable接口。
2. 使用public访问修辞符重新定义clone方法且抛出CloneNotSupportedException异常。
3. 在clone方法中调用父类的clone方法并将结果强制转型为当前类类型后返回。
深拷贝
看完了浅拷贝,你很有可能对其处理对象域的操作不满意。因为对象如果直接拷贝地址过来,则副本中该对象域的改变势必也会影响到原始对象中的对象域。
相对于浅拷贝,深拷贝是指用户自定义对象域的拷贝方式。
如何进行深拷贝
1. 为目标类继承Cloneable接口。
2. 使用public访问修辞符重新定义clone方法且抛出CloneNotSupportedException异常。
3. 在clone方法中调用父类的clone方法并将结果强制转型为当前类类型。
4. 对3中处理得到的结果以自定义的方式拷贝对象域部分。
代码示例 - 直接赋值
测试类:
package test;// 测试类Apublic class A { public // 打印对象内的整型变量值 void showValueInt() { System.out.println(valueInt); } // 打印对象内的字符串变量值 void showValueStr() { System.out.println(valueStr); } // 设置对象内的整型变量值 boolean setValue (int valueInt) { this.valueInt = valueInt; return true; } // 设置对象内的字符串变量值 boolean setValue (String valueStr) { this.valueStr = valueStr; return true; } private // 值1 int valueInt; // 值2 String valueStr;}
测试代码:
1 package test; 2 3 public class Java7Learn { 4 5 public static void main(String[] args){ 6 7 // 创建一个对象a1,并让它直接赋值给a2。 8 A a1 = new A(); 9 A a2 = a1;10 11 // 设置a1的两个变量值并打印出来12 a1.setValue(1);13 a1.setValue("this is a1");14 a1.showValueInt();15 a1.showValueStr();16 17 // 设置a2的两个变量值并打印出来18 a2.setValue(2);19 a2.setValue("this is a2");20 a2.showValueInt();21 a2.showValueStr();22 23 // 显示结果发现 - a1的对象值都变成a2的了。24 a1.showValueInt();25 a1.showValueStr();26 } 27 }
运行结果
代码示例 - 浅拷贝
首先按照前文所讲的浅拷贝的方法修改测试类:
1 package test; 2 3 // 测试类A 4 public class A implements Cloneable { 5 public 6 // 实现clone浅拷贝方法 7 A clone() throws CloneNotSupportedException 8 { 9 return (A)super.clone();10 }11 // 打印对象内的整型变量值12 void showValueInt() {13 System.out.println(valueInt);14 }15 // 打印对象内的字符串变量值16 void showValueStr() {17 System.out.println(valueStr);18 }19 // 设置对象内的整型变量值20 boolean setValue (int valueInt) {21 this.valueInt = valueInt;22 return true;23 }24 // 设置对象内的字符串变量值25 boolean setValue (String valueStr) {26 this.valueStr = valueStr;27 return true;28 }29 private30 // 值131 int valueInt;32 // 值233 String valueStr;34 }
测试代码:
package test;public class Java7Learn { public static void main(String[] args) throws CloneNotSupportedException{ // 创建一个对象a1,并将它克隆到a2。 A a1 = new A(); A a2 = a1.clone(); // 设置a1的两个变量值并打印出来 a1.setValue(1); a1.setValue("this is a1"); a1.showValueInt(); a1.showValueStr(); // 设置a2的两个变量值并打印出来 a2.setValue(2); a2.setValue("this is a2"); a2.showValueInt(); a2.showValueStr(); // 显示结果发现 - a1的整型对象值没变,但字符串型的变了。 a1.showValueInt(); a1.showValueStr(); } }
运行结果
发现,a1的整型对象值没变,但字符串型的变了。这是和预期相一致的。
代码示例 - 深拷贝
首先按照前文所讲的深拷贝的方法修改测试类:
1 package test; 2 3 // 测试类A 4 public class A implements Cloneable { 5 public 6 // 实现clone深拷贝方法 7 A clone() throws CloneNotSupportedException 8 { 9 A cloned = (A)super.clone();10 // 自定义拷贝方式。11 // 一般定义的方式都是调用成员自己的clone函数。但测试的字符串默认赋值会深拷贝,就没有在这里继续clone了。12 cloned.valueStr = valueStr;13 return cloned;14 }15 // 打印对象内的整型变量值16 void showValueInt() {17 System.out.println(valueInt);18 }19 // 打印对象内的字符串变量值20 void showValueStr() {21 System.out.println(valueStr);22 }23 // 设置对象内的整型变量值24 boolean setValue (int valueInt) {25 this.valueInt = valueInt;26 return true;27 }28 // 设置对象内的字符串变量值29 boolean setValue (String valueStr) {30 this.valueStr = valueStr;31 return true;32 }33 private34 // 值135 int valueInt;36 // 值237 String valueStr;38 }
测试代码:
1 package test; 2 3 public class Java7Learn { 4 5 public static void main(String[] args) throws CloneNotSupportedException{ 6 7 // 创建一个对象a1,并将它克隆到a2。 8 A a1 = new A(); 9 A a2 = a1.clone();10 11 // 设置a1的两个变量值并打印出来12 a1.setValue(1);13 a1.setValue("this is a1");14 a1.showValueInt();15 a1.showValueStr();16 17 // 设置a2的两个变量值并打印出来18 a2.setValue(2);19 a2.setValue("this is a2");20 a2.showValueInt();21 a2.showValueStr();22 23 // 显示结果发现 - a1的所有对象值都没被影响。24 a1.showValueInt();25 a1.showValueStr();26 } 27 }
运行结果
a2所做的操作完全没有影响a1的域。
小结
1. 要清晰区分对象直接赋值,浅拷贝,深拷贝这三者的底层机制。
2. clone的方式确实有点 "笨重",但必须严格的遵守才能写出高质量的代码。
对象复制 - 浅拷贝与深拷贝