理解java中的浅复制

堕落了一段时间,感觉最近想问题没有以前那样浆糊了,是放松的原因还是看编程思想的原因,哈哈,希望两个都有吧。

那次和董小龙讨论过了深复制和浅复制的问题,结果不了了之,没有出来个结果,今天突然想起来,就又瞅了瞅,想了想。不知道现在理解的对不对,反正我就先把自己的想法写出来,有什么不对的地方,希望能得到各位的批评指正。谢谢。


今天,我们只搞清楚浅复制,我觉得,想通了浅复制,深复制就好理解了。


第一、看简单的对象引用赋值:


为什么首先提出简单的引用这种方式,主要是因为在网上看到一些文章写的像这种String str1 = str2;的简单方式就是浅复制,其实不是的,我们先看一下都非常熟悉的简单对象引用赋值:

Code:
  1. import java.lang.*;  
  2. class A{  
  3. int score;  
  4. public A(int score){  
  5. this.score = score;  
  6. }  
  7. }  
  8. class Student implements Cloneable{  
  9. public Object clone() throws CloneNotSupportedException{  
  10. return super.clone();  
  11.   
  12. }  
  13. A a1 = new A(91);  
  14. A a2 = new A(92);  
  15. int age;  
  16. public Student(int age){  
  17. this.age = age;  
  18. }  
  19. }  
  20. public class Test{  
  21. public Test(){  
  22. }  
  23. public static void main(String args[]) throws CloneNotSupportedException{  
  24.   
  25. //简单的引用  
  26. Student s1 = new Student(10);  
  27. Student s2 = new Student(11);  
  28. s2 = s1;  
  29.   
  30. //简单的引用改变内容  
  31. s1.a1.score=50;  
  32. s1.age = 123;  
  33. //简单引用后的结果  
  34. System.out.println(s1.age);  
  35. System.out.println(s1.a1.score);  
  36.   
  37. System.out.println(s2.age);  
  38. System.out.println(s2.a1.score);  
  39.     //不是地址,是内容 
  40. System.out.println(s2);  
  41. System.out.println(s1);  
  42. System.out.println(s1.equals(s2));  
  43. System.out.println("//");  
  44. }  
  45. }  

 

运行结果如下:

从输出的结果可以看出,这种赋值,完全是引用的赋值,不管是对象的基本类型变量还是非基本类型变量不分,都是共享的一个。

第二、浅克隆(浅复制)是什么样的呢?


1、定义


浅克隆或者说浅复制,到底是怎么定义的?

浅复制(浅克隆): 被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。

深复制:被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量,那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。其实,说白了,深复制就是把要复制的对象所引用的对象都复制了一遍。

估计乍一看,看不懂什么个意思,不过,看着下面的代码和运行的结果,估计就理解的差不多了。

看代码前,还有一些准备:

2、要找一个API中实现浅复制的类。


大家应该都知道,Object基类有clone()方法,而这里的clone()所实现的就是浅克隆,下面,我们先慢慢探究着去认识这个方法的用法。

Code:
  1. //编译出错  
  2. import java.lang.*;  
  3.   
  4. class Student{  
  5.   
  6. int age;  
  7. public Student(int age){  
  8. this.age = age;  
  9. }  
  10. }  
  11. public class Test{  
  12. public Test(){  
  13. }  
  14. public static void main(String args[]){  
  15. Student stu1 = new Student(12);  
  16. stu1.clone();  
  17. }  
  18. }  

以上代码,编译错误,查API才知道,是因为这个clone()方法定义的是protected的,而且原来定义的方法要抛出异常?看看下面的代码:

Code:
  1. //依旧编译错误  
  2. import java.lang.*;  
  3.   
  4. class Student{  
  5. public Object clone() throws CloneNotSupportedException{  
  6. return super.clone();  
  7. }  
  8. int age;  
  9. public Student(int age){  
  10. this.age = age;  
  11. }  
  12. }  
  13. public class Test{  
  14. public Test(){  
  15. }  
  16. public static void main(String args[]) throws CloneNotSupportedException{  
  17. Student stu1 = new Student(12);  
  18. stu1.clone();  
  19. }  
  20. }  

将方法覆盖并改成public的并抛出异常也是错误的。原因在哪?

注意:

首先看一下Object中clone的定义:
protected Object clone()
                throws CloneNotSupportedException

CloneNotSupportedException异常:当调用 Object 类中的 clone 方法复制对象,但该对象的类无法实现 Cloneable 接口时,抛出该异常。

而Object类自己并没有实现Cloneable接口,所以,在Object类的对象上调用clone()方法会导致一个运行时异常。


修改后:

Code:
  1. import java.lang.*;  
  2.   
  3. class Student implements Cloneable{  
  4. public Object clone() throws CloneNotSupportedException{  
  5. return super.clone();  
  6. }  
  7. int age;  
  8. public Student(int age){  
  9. this.age = age;  
  10. }  
  11. }  
  12. public class Test{  
  13. public Test(){  
  14. }  
  15. public static void main(String args[]) throws CloneNotSupportedException{  
  16. Student stu1 = new Student(12);  
  17. stu1.clone();  
  18. }  
  19. }  

这样就可以编译和运行了。


3、对浅复制的理解(以及与简单引用的区别)


好了,说明了怎样去写出一个浅复制,也明白了简单的引用赋值的原理,那么,看看这两者的区别吧:

Code:
  1. import java.lang.*;  
  2. class A{  
  3. int score;  
  4. public A(int score){  
  5. this.score = score;  
  6. }  
  7. }  
  8. class Student implements Cloneable{  
  9. public Object clone() throws CloneNotSupportedException{  
  10. return super.clone();  
  11.   
  12. }  
  13. A a1 = new A(91);  
  14. A a2 = new A(92);  
  15. int age;  
  16. public Student(int age){  
  17. this.age = age;  
  18. }  
  19. }  
  20. public class Test{  
  21. public Test(){  
  22. }  
  23. public static void main(String args[]) throws CloneNotSupportedException{  
  24. //浅克隆  
  25. Student stu1 = new Student(12);  
  26. Student stu2 = new Student(110);  
  27. stu2 = (Student)stu1.clone();  
  28.   
  29. //简单的引用  
  30. Student s1 = new Student(10);  
  31. Student s2 = new Student(11);  
  32. s2 = s1;  
  33.   
  34. //改变内容  
  35.   
  36.   //浅克隆改变内容  
  37. stu1.a1.score = 100;  
  38. stu1.age = 10;  
  39.   
  40.   
  41.     //简单的引用改变内容  
  42. s1.a1.score=50;  
  43. s1.age = 123;  
  44.   
  45. //输出结果  
  46.     //简单引用后的结果  
  47. System.out.println(s1.age);  
  48. System.out.println(s1.a1.score);  
  49.   
  50. System.out.println(s2.age);  
  51. System.out.println(s2.a1.score);  
  52.     //不是地址,是内容 
  53. System.out.println(s2);  
  54. System.out.println(s1);  
  55. System.out.println(s1.equals(s2));  
  56. System.out.println("");  
  57.     //浅克隆后的结果  
  58. System.out.println("stu1.age:"+stu1.age);  
  59. System.out.println("stu1.a1.score:"+stu1.a1.score);  
  60.   
  61. System.out.println("stu2.age:"+stu2.age);  
  62. System.out.println("stu2.a1.score:"+stu2.a1.score);  
  63.     //地址是否相等  
  64. System.out.println(stu2.equals(stu1));  
  65. }  
  66. }  

运行结果:

从运行的结果可以看出:

(1) 两个变量不是同一个内容,不是仅仅的引用赋值问题(因为第二个的equals测试出来的两个实例变量的地址不同,说明,确实复制了);

(2) 直接属于本对象的引用被复制,如age,直接决定了在改变其中一个时,另一个不会发生变化;

(3) 不直接属于本对象的变量,如a1,会仅仅复制引用,这也直接决定了改变其中一个时,另一个会发生变化。

那么,下面的代码可能让我们感到困惑:

Code:
  1. import java.util.*;  
  2. class A{  
  3. String s = new String();  
  4. int i = 0;  
  5. public A(){  
  6. System.out.println("class A");  
  7. }  
  8. }  
  9. public class pipi {  
  10.   
  11. public pipi() {  
  12. }  
  13. public static void main(String[] args) {  
  14. A[] a = new A[1];  
  15. A[] b = new A[1];  
  16. a[0] = new A();  
  17. b[0] = new A();  
  18. a[0].s = "nihao";  
  19. System.arraycopy(a,0,b,0,a.length);  
  20. System.out.println(b[0].s);  
  21. a[0].s = "wohao";  
  22. a[0].i = 2;  
  23. System.out.println(b[0].s);  
  24. System.out.println(a[0].i);  
  25. System.out.println(b[0].i);  
  26. }  
  27. }  

要说明的一点是:System.arraycopy是浅复制的

但是从输出结果中可以看到,i的值也变了,为什么呢?这并不是浅复制的原因,而是应该这样理解:System.arraycopy是复制数组,复制的内容是数组的引用,而不是数组里面所放置的对象的引用,所以,i的值还是会变的。

好的吧,今天考虑的问题就是这些了。playplay啦~

评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值