堕落了一段时间,感觉最近想问题没有以前那样浆糊了,是放松的原因还是看编程思想的原因,哈哈,希望两个都有吧。
那次和董小龙讨论过了深复制和浅复制的问题,结果不了了之,没有出来个结果,今天突然想起来,就又瞅了瞅,想了想。不知道现在理解的对不对,反正我就先把自己的想法写出来,有什么不对的地方,希望能得到各位的批评指正。谢谢。
今天,我们只搞清楚浅复制,我觉得,想通了浅复制,深复制就好理解了。
第一、看简单的对象引用赋值:
为什么首先提出简单的引用这种方式,主要是因为在网上看到一些文章写的像这种String str1 = str2;的简单方式就是浅复制,其实不是的,我们先看一下都非常熟悉的简单对象引用赋值:
- import java.lang.*;
- class A{
- int score;
- public A(int score){
- this.score = score;
- }
- }
- class Student implements Cloneable{
- public Object clone() throws CloneNotSupportedException{
- return super.clone();
- }
- A a1 = new A(91);
- A a2 = new A(92);
- int age;
- public Student(int age){
- this.age = age;
- }
- }
- public class Test{
- public Test(){
- }
- public static void main(String args[]) throws CloneNotSupportedException{
- //简单的引用
- Student s1 = new Student(10);
- Student s2 = new Student(11);
- s2 = s1;
- //简单的引用改变内容
- s1.a1.score=50;
- s1.age = 123;
- //简单引用后的结果
- System.out.println(s1.age);
- System.out.println(s1.a1.score);
- System.out.println(s2.age);
- System.out.println(s2.a1.score);
- //不是地址,是内容
- System.out.println(s2);
- System.out.println(s1);
- System.out.println(s1.equals(s2));
- System.out.println("//");
- }
- }
运行结果如下:
从输出的结果可以看出,这种赋值,完全是引用的赋值,不管是对象的基本类型变量还是非基本类型变量不分,都是共享的一个。
第二、浅克隆(浅复制)是什么样的呢?
1、定义
浅克隆或者说浅复制,到底是怎么定义的?
浅复制(浅克隆): 被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。
深复制:被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量,那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。其实,说白了,深复制就是把要复制的对象所引用的对象都复制了一遍。
估计乍一看,看不懂什么个意思,不过,看着下面的代码和运行的结果,估计就理解的差不多了。
看代码前,还有一些准备:
2、要找一个API中实现浅复制的类。
大家应该都知道,Object基类有clone()方法,而这里的clone()所实现的就是浅克隆,下面,我们先慢慢探究着去认识这个方法的用法。
- //编译出错
- import java.lang.*;
- class Student{
- int age;
- public Student(int age){
- this.age = age;
- }
- }
- public class Test{
- public Test(){
- }
- public static void main(String args[]){
- Student stu1 = new Student(12);
- stu1.clone();
- }
- }
以上代码,编译错误,查API才知道,是因为这个clone()方法定义的是protected的,而且原来定义的方法要抛出异常?看看下面的代码:
- //依旧编译错误
- import java.lang.*;
- class Student{
- public Object clone() throws CloneNotSupportedException{
- return super.clone();
- }
- int age;
- public Student(int age){
- this.age = age;
- }
- }
- public class Test{
- public Test(){
- }
- public static void main(String args[]) throws CloneNotSupportedException{
- Student stu1 = new Student(12);
- stu1.clone();
- }
- }
将方法覆盖并改成public的并抛出异常也是错误的。原因在哪?
注意:
首先看一下Object中clone的定义:
protected Object clone()
throws CloneNotSupportedException
CloneNotSupportedException异常:当调用 Object 类中的 clone 方法复制对象,但该对象的类无法实现 Cloneable 接口时,抛出该异常。
而Object类自己并没有实现Cloneable接口,所以,在Object类的对象上调用clone()方法会导致一个运行时异常。
修改后:
- import java.lang.*;
- class Student implements Cloneable{
- public Object clone() throws CloneNotSupportedException{
- return super.clone();
- }
- int age;
- public Student(int age){
- this.age = age;
- }
- }
- public class Test{
- public Test(){
- }
- public static void main(String args[]) throws CloneNotSupportedException{
- Student stu1 = new Student(12);
- stu1.clone();
- }
- }
这样就可以编译和运行了。
3、对浅复制的理解(以及与简单引用的区别)
好了,说明了怎样去写出一个浅复制,也明白了简单的引用赋值的原理,那么,看看这两者的区别吧:
- import java.lang.*;
- class A{
- int score;
- public A(int score){
- this.score = score;
- }
- }
- class Student implements Cloneable{
- public Object clone() throws CloneNotSupportedException{
- return super.clone();
- }
- A a1 = new A(91);
- A a2 = new A(92);
- int age;
- public Student(int age){
- this.age = age;
- }
- }
- public class Test{
- public Test(){
- }
- public static void main(String args[]) throws CloneNotSupportedException{
- //浅克隆
- Student stu1 = new Student(12);
- Student stu2 = new Student(110);
- stu2 = (Student)stu1.clone();
- //简单的引用
- Student s1 = new Student(10);
- Student s2 = new Student(11);
- s2 = s1;
- //改变内容
- //浅克隆改变内容
- stu1.a1.score = 100;
- stu1.age = 10;
- //简单的引用改变内容
- s1.a1.score=50;
- s1.age = 123;
- //输出结果
- //简单引用后的结果
- System.out.println(s1.age);
- System.out.println(s1.a1.score);
- System.out.println(s2.age);
- System.out.println(s2.a1.score);
- //不是地址,是内容
- System.out.println(s2);
- System.out.println(s1);
- System.out.println(s1.equals(s2));
- System.out.println("");
- //浅克隆后的结果
- System.out.println("stu1.age:"+stu1.age);
- System.out.println("stu1.a1.score:"+stu1.a1.score);
- System.out.println("stu2.age:"+stu2.age);
- System.out.println("stu2.a1.score:"+stu2.a1.score);
- //地址是否相等
- System.out.println(stu2.equals(stu1));
- }
- }
运行结果:
从运行的结果可以看出:
(1) 两个变量不是同一个内容,不是仅仅的引用赋值问题(因为第二个的equals测试出来的两个实例变量的地址不同,说明,确实复制了);
(2) 直接属于本对象的引用被复制,如age,直接决定了在改变其中一个时,另一个不会发生变化;
(3) 不直接属于本对象的变量,如a1,会仅仅复制引用,这也直接决定了改变其中一个时,另一个会发生变化。
那么,下面的代码可能让我们感到困惑:
- import java.util.*;
- class A{
- String s = new String();
- int i = 0;
- public A(){
- System.out.println("class A");
- }
- }
- public class pipi {
- public pipi() {
- }
- public static void main(String[] args) {
- A[] a = new A[1];
- A[] b = new A[1];
- a[0] = new A();
- b[0] = new A();
- a[0].s = "nihao";
- System.arraycopy(a,0,b,0,a.length);
- System.out.println(b[0].s);
- a[0].s = "wohao";
- a[0].i = 2;
- System.out.println(b[0].s);
- System.out.println(a[0].i);
- System.out.println(b[0].i);
- }
- }
要说明的一点是:System.arraycopy是浅复制的。
但是从输出结果中可以看到,i的值也变了,为什么呢?这并不是浅复制的原因,而是应该这样理解:System.arraycopy是复制数组,复制的内容是数组的引用,而不是数组里面所放置的对象的引用,所以,i的值还是会变的。
好的吧,今天考虑的问题就是这些了。playplay啦~