java中对象的深拷贝和浅拷贝

根据对象成员变量的拷贝程度(基本数据类型、引用类型),可将拷贝分为浅拷贝和深拷贝。

一、浅拷贝

package javaKeyTechnology;

class PerSon{
    private String name;
    private int age;
    PerSon(String name,int age){
        this.name = name;
        this.age = age;
    }

    public void setName(String name){ //私有数据的更改器
        this.name = name;
    }

    public String getName(){ //私有数据的访问器
        return name;
    }

    public void setAge(int age){
        this.age = age;
    }

    public int getAge(){
        return age;
    }

    public String toString(){
        return "Person:["+this.hashCode()+" name:"+name+" age:"+age+"]";
    }

}
public class MyClone implements Cloneable{
    PerSon man;
    private int id;
    private String gender;

    public MyClone(String name,int age,String gender,int id){
        man = new PerSon(name,age);
        this.gender = gender;
        this.id = id;
    }
    public void setId(int id){
        this.id = id;
    }

    public int getId(){
        return id;
    }

    public void setGender(String gender){
        this.gender = gender;
    }

    public String getGender(){
        return gender;
    }
    public String toString(){
        return man.toString() + this.hashCode() + " gender:"+gender+" id:"+id;
    }

    //浅拷贝
    public MyClone clone(){
        try {
            return (MyClone)super.clone();
        } catch (CloneNotSupportedException e) {
            return null;
        }
    }

    public static void main(String[] args){
        MyClone student1 = new MyClone("LiLi",16,"felman",123456);
        MyClone student2 = student1.clone();
        MyClone student3 = student1;
        student2.man.setAge(20);//改变student的引用变量的内容
        student2.man.setName("Jason");
        student2.setId(888888);//改变student的基本类型变量的内容
        student2.setGender("man");
        System.out.println("浅拷贝");
        System.out.println("student1:        " + student1.toString());
        System.out.println("student1.clone   " + student2.toString());
        System.out.println("对象拷贝");
        System.out.println("student1.copy:   " + student1.toString());
    }
}

 

 

 可见,对于浅拷贝:

1.创建了新对象,和原对象的hashcode不同;

2.成员变量中的基本数据类型是是拷贝其值,新对象和原对象中任一对象的基本数据类型发生改变,对另一对象不产生影响。所以原对象的id没有改变。

3.成员变量中的不可变对象的引用类型(如String),虽然是拷贝其地址,由于其引用的对象不可变,改变新对象和原对象任一的该变量(引用),只是让它引用了新的不可变对象,另外一个对象的该变量所引用的对象不变。所以原对象的gender没有改变。

4.成员变量中的可变对象的引用类型是是拷贝地址。新对象和原对象中该变量指向同一对象,所以person的hashCode相同;任一对象的该引用的对象内容发生改变,对另一对象产生影响,所以原对象的person内容也改变了。

5.Object.clone()是浅拷贝,如果在实现了Cloneable接口后只调用Object.clone(),自然也只能是浅拷贝。

对于浅拷贝和对象拷贝的区别:

1.浅拷贝创建了新对象,两对象地址不同。

2.对象拷贝没有创建新对象,两个变量指向的是同一个对象,同一个地址。

 二、深拷贝

package javaKeyTechnology;

class PerSon implements Cloneable{
    private String name;
    private int age;
    PerSon(String name,int age){
        this.name = name;
        this.age = age;
    }

    public void setName(String name){ //私有数据的更改器
        this.name = name;
    }

    public String getName(){ //私有数据的访问器
        return name;
    }

    public void setAge(int age){
        this.age = age;
    }

    public int getAge(){
        return age;
    }

    public String toString(){
        return "Person:["+this.hashCode()+" name:"+name+" age:"+age+"]";
    }

    public PerSon clone(){
        try {
            return (PerSon)super.clone();
        } catch (CloneNotSupportedException e) {
            return null;
        }
    }


}
public class MyClone implements Cloneable{
    PerSon man;
    private int id;
    private String gender;

    public MyClone(String name,int age,String gender,int id){
        man = new PerSon(name,age);
        this.gender = gender;
        this.id = id;
    }
    public void setId(int id){
        this.id = id;
    }

    public int getId(){
        return id;
    }

    public void setGender(String gender){
        this.gender = gender;
    }

    public String getGender(){
        return gender;
    }
    public String toString(){
        return man.toString() + this.hashCode() + " gender:"+gender+" id:"+id;
    }

    //深拷贝
    public MyClone clone(){
        try {
            MyClone tmp = (MyClone)super.clone();
            tmp.man = man.clone(); //把现在对象的man克隆
            return tmp;
        } catch (CloneNotSupportedException e) {
            return null;
        }
    }

    public static void main(String[] args){
        MyClone student1 = new MyClone("LiLi",16,"felman",123456);
        MyClone student2 = student1.clone();
        student2.man.setAge(20);//改变student的引用变量的内容
        student2.man.setName("Jason");
        student2.setId(888888);//改变student的基本类型变量的内容
        student2.setGender("man");
        System.out.println("深拷贝");
        System.out.println("student1:        " + student1.toString());
        System.out.println("student1.clone   " + student2.toString());

    }
}

 

 原对象和拷贝后创建的新对象中任一对象的任意成员变量(基本数据类型、不可变对象的引用、可变对象的引用)发生改变,都不影响另一个对象的成员变量。

深拷贝比浅拷贝多做的工作是:让新对象的引用变量 = 该引用所指对象的深拷贝

如果对象有多层对象(成员变量是引用),每层对象所属的类都要实现Cloneable接口并重写clone()。如果类的超类是Object,直接重写clone()。

 三、关于以上代码中的异常处理

如果一个类没有implement Cloneable接口,该类的对象在调用Object.clone()时,就会抛出CloneNotSupportException(受查异常)。

注意并没有实现Cloneable接口的任何方法,clone()是从Object继承而来的。但是该接口会作为一个标记,允许克隆。

有两种异常处理的方法:

1.在该方法中捕获异常(一、二中的代码采用该方式)

2.声明异常,由该方法的高层调用处理,如果第一层调用不处理,可以继续传递给更高层的调用。这里就用到这一点。

因为这是一个受查异常,所以最好利用throws声明异常,将异常抛给方法的调用者去处理:

 

  1 package javaKeyTechnology;
  2 
  3 //class PerSon implements Cloneable{ //正确写法
  4 class PerSon{    //制造一个异常
  5     private String name;
  6     private int age;
  7     PerSon(String name,int age){
  8         this.name = name;
  9         this.age = age;
 10     }
 11 
 12     public void setName(String name){ //私有数据的更改器
 13         this.name = name;
 14     }
 15 
 16     public String getName(){ //私有数据的访问器
 17         return name;
 18     }
 19 
 20     public void setAge(int age){
 21         this.age = age;
 22     }
 23 
 24     public int getAge(){
 25         return age;
 26     }
 27 
 28     public String toString(){
 29         return "Person:["+this.hashCode()+" name:"+name+" age:"+age+"]";
 30     }
 31 
 32     public PerSon clone() throws CloneNotSupportedException{
 33         return (PerSon)super.clone();
 34     }
 35 
 36 
 37 }
 38 public class MyClone implements Cloneable{
 39     PerSon man;
 40     private int id;
 41     private String gender;
 42 
 43     public MyClone(String name,int age,String gender,int id){
 44         man = new PerSon(name,age);
 45         this.gender = gender;
 46         this.id = id;
 47     }
 48     public void setId(int id){
 49         this.id = id;
 50     }
 51 
 52     public int getId(){
 53         return id;
 54     }
 55 
 56     public void setGender(String gender){
 57         this.gender = gender;
 58     }
 59 
 60     public String getGender(){
 61         return gender;
 62     }
 63     public String toString(){
 64         return man.toString() + this.hashCode() + " gender:"+gender+" id:"+id;
 65     }
 66 
 67     //异常处理方式一:
 68 //    public MyClone clone(){
 69 //        try {
 70 //            MyClone tmp = (MyClone)super.clone();
 71 //            tmp.man = man.clone(); //把现在对象的man克隆
 72 //            return tmp;
 73 //        } catch (CloneNotSupportedException e) {
 74 //            return null;
 75 //        }
 76 //    }

77 //异常处理方式二: 78 public MyClone clone() throws CloneNotSupportedException{ //一旦没有采用Cloneable接口,将抛出异常,由该方法的上层调用处理 79 MyClone tmp = (MyClone)super.clone(); 80 tmp.man = man.clone(); //把现在对象的man克隆,这里将抛出一个异常,但是先不处理继续传递给public MyClone clone()方法。 81 return tmp; 82 83 } 84 public static void main(String[] args){ 85 MyClone student1 = new MyClone("LiLi",16,"felman",123456); 86 try{ //需要调用声明异常的方法时,捕获异常 87 MyClone student2 = student1.clone(); 88 student2.man.setAge(20);//改变student的引用变量的内容 89 student2.man.setName("Jason"); 90 student2.setId(888888);//改变student的基本类型变量的内容 91 student2.setGender("man"); 92 System.out.println("深拷贝"); 93 System.out.println("student1: " + student1.toString()); 94 System.out.println("student1.clone " + student2.toString()); 95 }catch(CloneNotSupportedException e){ 96 System.out.println("error"); 97 e.printStackTrace(); 98 } 99 } 100 }

 

posted @ 2019-12-04 14:44  随温  阅读(419)  评论(0)    收藏  举报