1. 程式人生 > >初始JAVA中淺拷貝和深拷貝

初始JAVA中淺拷貝和深拷貝

exc 深度復制 tst 圖片 們的 over 地方 per 發生

1. 簡單變量的復制

public static void main(String[] args) {
        int a = 5;
        int b = a;
        System.out.println(a);
        System.out.println(b);
    }

八種基本數據類型的(int,boolean,char,byte,short,float,double.long)都適用於這種情況。

2. 對象的復制

對象的復制並沒有基本類型變量的復制這麽簡單,下面看一個例子

2.1 準備一個Student類

package
com.jepson.java; /** * @author jepson * @create 2018-09-21 23:20 */ public class Student { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }

2.2 測試Object的測試

package com.jepson.java;

/** * @author jepson * @create 2018-09-21 23:21 */ public class TestObjectCopy { public static void main(String[] args) { Student stu1 = new Student(); stu1.setName("jepson"); Student stu2 = stu1; System.out.println("student1:"+stu1.getName()); System.out.println(
"student2:"+stu2.getName()); } }

2.3 運行結果

student1:jepson
student2:jepson

2.4 總結

這裏我們自定義了一個學生類,該類只有一個name字段。

我們新建了一個學生實例,然後將該值賦值給stu2實例。(Student stu2 = stu1;)

再看看打印結果,作為一個新手,拍了拍胸腹,對象復制不過如此,

難道真的是這樣嗎?

2.5 舉一反三

我們試著改變stu2實例的name字段,再打印結果看看

package com.jepson.java;

/**
 * @author jepson
 * @create 2018-09-21 23:21
 */
public class TestObjectCopy {
    public static void main(String[] args) {
        Student stu1 = new Student();
        stu1.setName("jepson");
        Student stu2 = stu1;
        stu2.setName("tom");
        System.out.println("student1:"+stu1.getName());
        System.out.println("student2:"+stu2.getName());
    }
}

打印結果

student1:tom
student2:tom

這就怪了,為什麽改變學生2的學號,學生1的學號也發生了變化呢?

原因出在(stu2 = stu1) 這一句。該語句的作用是將stu1的引用賦值給stu2,

這樣,stu1和stu2指向內存堆中同一個對象。如圖:

技術分享圖片

那麽,怎樣才能達到復制一個對象呢?

是否記得萬類之王Object。它有11個方法,有兩個protected的方法,其中一個為clone方法。

該方法的簽名是:

protected native Object clone() throws CloneNotSupportedException;

因為每個類直接或間接的父類都是Object,因此它們都含有clone()方法,但是因為該方法是protected,所以都不能在類外進行訪問。

要想對一個對象進行復制,就需要對clone方法覆蓋。

3 對象的淺拷貝(淺復制)

1. 被復制的類需要實現Clonenable接口(不實現的話在調用clone方法會拋出CloneNotSupportedException異常) 該接口為標記接口(不含任何方法)

2. 覆蓋clone()方法,訪問修飾符設為public。方法中調用super.clone()方法得到需要的復制對象,(native為本地方法)

3.1 修改Student類

1.實現Cloneable接口

2.重寫clone方法

3.訪問修飾符改為public

package com.jepson.java;

/**
 * @author jepson
 * @create 2018-09-21 23:20
 */
public class Student implements Cloneable{

    @Override
    public Object clone() {
        Student stu = null;
        try{
            stu = (Student)super.clone();
        }catch(CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return stu;
    }

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

3.2 修改測試類

1.調用clone方法。

package com.jepson.java;

/**
 * @author jepson
 * @create 2018-09-21 23:21
 */
public class TestObjectCopy {
    public static void main(String[] args) {
        Student stu1 = new Student();
        stu1.setName("jepson");
        Student stu2 = (Student)stu1.clone();
        stu2.setName("淺拷貝-tom");
        System.out.println("student1:"+stu1.getName());
        System.out.println("student2:"+stu2.getName());
    }
}

打印結果

技術分享圖片

如果你還不相信這兩個對象不是同一個對象,那麽可以測試一下

package com.jepson.java;

/**
 * @author jepson
 * @create 2018-09-21 23:21
 */
public class TestObjectCopy {
    public static void main(String[] args) {
        Student stu1 = new Student();
        stu1.setName("jepson");
        Student stu2 = (Student)stu1.clone();
        stu2.setName("淺拷貝-tom");
        System.out.println("student1:"+stu1.getName());
        System.out.println("student2:"+stu2.getName());
        System.out.println(stu1==stu2);
    }
}

打印結果

技術分享圖片

4 對象的深拷貝(深復制)

我們在學生類裏再加一個Address類。

Address類如下:

package com.jepson.java;

/**
 * @author jepson
 * @create 2018-09-21 23:57
 */
public class Address {
    private  String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Address{" +
                "address=‘" + address + ‘\‘‘ +
                ‘}‘;
    }
}

Student類如下:

package com.jepson.java;

/**
 * @author jepson
 * @create 2018-09-21 23:20
 */
public class Student implements Cloneable{

    private String name;

    private  Address address;

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }



    @Override
    public Object clone() {
        Student stu = null;
        try{
            stu = (Student)super.clone();
        }catch(CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return stu;
    }

}

測試類

package com.jepson.java;

/**
 * @author jepson
 * @create 2018-09-21 23:21
 */
public class TestObjectCopy {
    public static void main(String[] args) {
        Address address = new Address();
        address.setAddress("成都市");

        Student stu1 = new Student();
        stu1.setName("jepson");
        stu1.setAddress(address);


        Student stu2 = (Student)stu1.clone();


        stu2.setName("深拷貝-tom");
        System.out.println("student1:"+stu1.getName()+"::::"+stu1.getAddress());
        System.out.println("student2:"+stu2.getName()+"::::"+stu2.getAddress());
        System.out.println(stu1==stu2);
    }
}

打印結果

技術分享圖片

乍一看沒什麽問題,真的是這樣嗎?

我們在main方法中試著改變addr實例的地址。

package com.jepson.java;

/**
 * @author jepson
 * @create 2018-09-21 23:21
 */
public class TestObjectCopy {
    public static void main(String[] args) {
        Address address = new Address();
        address.setAddress("成都市");
        Student stu1 = new Student();
        stu1.setName("jepson");
        stu1.setAddress(address);


        Student stu2 = (Student)stu1.clone();

        stu2.setName("深拷貝-tom");
        System.out.println("student1:"+stu1.getName()+"::::"+stu1.getAddress());
        System.out.println("student2:"+stu2.getName()+"::::"+stu2.getAddress());

        address.setAddress("重慶區");
        stu2.setName("深拷貝-tom");
        System.out.println("student1:"+stu1.getName()+"::::"+stu1.getAddress());
        System.out.println("student2:"+stu2.getName()+"::::"+stu2.getAddress());

        System.out.println(stu1==stu2);
    }
}

打印結果

技術分享圖片

這就奇怪了,怎麽兩個學生的地址都改變了?

原因是淺復制只是復制了addr變量的引用,並沒有真正的開辟另一塊空間,將值復制後再將引用返回給新對象。

所以,為了達到真正的復制對象,而不是純粹引用復制。我們需要將Address類可復制化,並且修改clone方法,完整代碼如下:

1.修改Address類

1.實現Cloneable接口

2.重寫clone方法

package com.jepson.java;

/**
 * @author jepson
 * @create 2018-09-21 23:57
 */
public class Address implements Cloneable{
    private  String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Address{" +
                "address=‘" + address + ‘\‘‘ +
                ‘}‘;
    }

    @Override
    protected Object clone() {
        Address addr = null;
        try{
            addr = (Address)super.clone();
        }catch(CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return addr;
    }
}

2. 修改Student類

clone方法中加入深度復制

package com.jepson.java;

/**
 * @author jepson
 * @create 2018-09-21 23:20
 */
public class Student implements Cloneable{

    private String name;

    private  Address address;

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }



    @Override
    public Object clone() {
        Student stu = null;
        try{
            stu = (Student)super.clone();  // 淺復制
        }catch(CloneNotSupportedException e) {
            e.printStackTrace();
        }
        stu.address = (Address)address.clone();   //深度復制
        return stu;
    }

}

3. 測試類

package com.jepson.java;

/**
 * @author jepson
 * @create 2018-09-21 23:21
 */
public class TestObjectCopy {
    public static void main(String[] args) {
        Address address = new Address();
        address.setAddress("成都市");
        Student stu1 = new Student();
        stu1.setName("jepson");
        stu1.setAddress(address);


        Student stu2 = (Student)stu1.clone();

        stu2.setName("深拷貝-tom");
        System.out.println("student1:"+stu1.getName()+"::::"+stu1.getAddress());
        System.out.println("student2:"+stu2.getName()+"::::"+stu2.getAddress());

        address.setAddress("重慶區");
        stu2.setName("深拷貝-tom");
        System.out.println("student1:"+stu1.getName()+"::::"+stu1.getAddress());
        System.out.println("student2:"+stu2.getName()+"::::"+stu2.getAddress());

        System.out.println(stu1==stu2);
    }
}

技術分享圖片

這樣結果就符合我們的想法了。

總結:淺拷貝是指在拷貝對象時,對於基本數據類型的變量會重新復制一份,而對於引用類型的變量只是對引用進行拷貝,

沒有對引用指向的對象進行拷貝。

而深拷貝是指在拷貝對象時,同時會對引用指向的對象進行拷貝。

區別就在於是否對 對象中的引用變量所指向的對象進行拷貝。

初始JAVA中淺拷貝和深拷貝