JAVA中對象的克隆及深拷貝和淺拷貝


使用場景:

在日常的編程過程 中,經常會遇到,有一個對象OA,在某一時間點OA中已經包含了一些有效值 ,此時可能會需一個和OA完全相對的新對象OB,並且要在后面的操作中對OB的任何改動都不會影響到OA的值,也就是OA與Ob是需要完全兩個獨立的對象。

但OB的初始值是由對象OA確定的。在JAVA語言中,用普通的賦值語句是滿足不了需求的。使用對象的clone()方法是實現克隆的最簡單、也是最高效的手段。

Java的所有類都默認繼承java.lang.Object類,在java.lang.Object類中有一個方法clone()。JDK API的說明文檔解釋這個方法將返回Object對象的一個拷貝。要說明的有兩點:一是拷貝對象返回的是一個新對象,而不是一個引用。二是拷貝對象與用 new操作符返回的新對象的區別就是這個拷貝已經包含了一些原來對象的信息,而不是對象的初始信息。 實現克隆可以用深拷貝和淺拷貝來實現。

深拷貝和淺拷貝的基本概念的理解:

淺拷貝是指拷貝對象時僅僅拷貝對象本身(包括對象中的基本變量),而不拷貝對象包含的引用指向的對象,被復制對象的所有變量都含有與原來的對象相同的值,而所有的對其他對象的引用仍然指向原來的對象

深拷貝不僅拷貝對象本身,而且拷貝對象包含的引用指向的所有對象 

通過實例看深拷貝和淺拷貝的實現和不同:

淺拷貝

class AddressNew implements Cloneable {
    private String add;

    public String getAdd() {
        return add;
    }

    public void setAdd(String add) {
        this.add = add;
    }

    public Object clone() throws CloneNotSupportedException{
       return super.clone();
    }

}

public class StudentNew implements Cloneable{
    private int number;
    private String name;

    public String getName() {
        return name;
    }

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

    private AddressNew addr;

    public AddressNew getAddr() {
        return addr;
    }

    public void setAddr(AddressNew addr) {
        this.addr = addr;
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }


    public Object clone() throws CloneNotSupportedException {
       return  super.clone();
    }
}


public class JavaShallowCopy {
   public static void main(String [] args) throws Exception{

       AddressNew addr = new AddressNew();
       addr.setAdd("杭州市");
       StudentNew stu1 = new StudentNew();
       stu1.setNumber(123);
       stu1.setName("s1");
       stu1.setAddr(addr);

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

       System.out.println("學生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
       System.out.println("學生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());
       System.out.println(stu1);
       System.out.println(stu2);
       addr.setAdd("西湖區");
       //stu1.setNumber(20);
       //stu2.setName("s2");

       System.out.println("學生1:" + stu1.getName() + ",地址:" + stu1.getAddr().getAdd());
       System.out.println("學生2:" + stu2.getName() + ",地址:" + stu2.getAddr().getAdd());
   }
}

運行結果:

學生1:123,地址:杭州市
學生2:123,地址:杭州市
com.songidea.StudentNew@133314b
com.songidea.StudentNew@b1bc7ed
學生1:s1,地址:西湖區
學生2:s1,地址:西湖區

從運行結果來看,stu1,stu2的解是2個不同的對象了,但是在改變了,addr的對象的地址之后,stu1,stu2的2個對象的引用對象addr的值都改變了,也就是說stu1和stu2的addr對象引用的是同一個地址,這個不是我們想要的結果,在實際的開發工作中這一塊一定要特別注意,使用不當可能會使業務功能或數據造成錯誤或混亂,所以這個拷貝只是實現在淺拷貝,那么從我們需要的場景看需要實現深拷貝才能達到我們想要的結果,下面會通過實例來看深拷貝的2種不同的實現。

深拷貝:

class Address implements Cloneable {
    private String add;

    public String getAdd() {
        return add;
    }

    public void setAdd(String add) {
        this.add = add;
    }
    @Override
    public Object clone() {
        Address addr = null;
        try{
            addr = (Address)super.clone();
        }catch(CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return addr;
    }

}

public class Student implements Cloneable{
    private int number;
    private String name;

    public String getName() {
        return name;
    }

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

    private Address addr;

    public Address getAddr() {
        return addr;
    }

    public void setAddr(Address addr) {
        this.addr = addr;
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

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


public class javaDeepCopy {
    public static void main(String args[]) {
Address addr = new Address();
addr.setAdd("杭州市");
Student stu1 = new Student();
stu1.setNumber(123);
stu1.setName("s1");
stu1.setAddr(addr);

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

System.out.println("學生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
System.out.println("學生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());
System.out.println(stu1);
System.out.println(stu2);
addr.setAdd("西湖區");
stu1.setNumber(20);
System.out.println("學生1:" + stu1.getName() + ",地址:" + stu1.getAddr().getAdd());
System.out.println("學生2:" + stu2.getName() + ",地址:" + stu2.getAddr().getAdd());
 } }

運行結果:

學生1:123,地址:杭州市
學生2:123,地址:杭州市
com.songidea.Student@133314b
com.songidea.Student@b1bc7ed
學生1:s1,地址:西湖區
學生2:s1,地址:杭州市

從運行結果來看,stu1,stu2的解是2個不同的對象了,在改變了addr的對象的地址之后,stu1,stu2的2個對象的引用對象addr的值只有stu1r 改變了,也就是說stu1和stu2的addr對象引用的不是同一個地址,這個是我們想要的結果所以這個拷貝只是實現在淺拷貝,那么從我們需要的場景看,這種方式實現了深拷,達到了我們想要的結果,下面會通過實例來看深拷貝的另一種實現:通過序例化來實現深拷貝。

深拷貝序列化的實現:

class AddressSerial implements Serializable {
    private String add;

    public String getAdd() {
        return add;
    }

    public void setAdd(String add) {
        this.add = add;
    }

    
}

public class StudentSerial implements Serializable{
    private int number;
    private String name;

    public String getName() {
        return name;
    }

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

    private AddressSerial addr;

    public AddressSerial getAddr() {
        return addr;
    }

    public void setAddr(AddressSerial addr) {
        this.addr = addr;
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }


    public Object clone() throws CloneNotSupportedException {
        return  super.clone();
    }

    public  Object deepClone() throws IOException,OptionalDataException,ClassNotFoundException{
        ByteArrayOutputStream bo=new ByteArrayOutputStream();
        ObjectOutputStream oo=new ObjectOutputStream(bo);
        oo.writeObject(this);
        ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray());
        ObjectInputStream oi=new ObjectInputStream(bi);
        return oi.readObject();
    }
}



public class javaDeepCopySreial {
    public static void  main(String [] args) throws Exception{

        AddressSerial addr = new AddressSerial();
        addr.setAdd("杭州市");
        StudentSerial stu1 = new StudentSerial();
        stu1.setNumber(123);
        stu1.setName("s1");
        stu1.setAddr(addr);

        StudentSerial stu2 = (StudentSerial)stu1.deepClone();

        System.out.println("學生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
        System.out.println("學生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());
        System.out.println(stu1);
        System.out.println(stu2);
        addr.setAdd("西湖區");
        //stu1.setNumber(20);
        //stu2.setName("s2");

        System.out.println("學生1:" + stu1.getName() + ",地址:" + stu1.getAddr().getAdd());
        System.out.println("學生2:" + stu2.getName() + ",地址:" + stu2.getAddr().getAdd());
    }
}

運行結果:

學生1:123,地址:杭州市
學生2:123,地址:杭州市
com.songidea.StudentSerial@1e80bfe8
com.songidea.StudentSerial@5e9f23b4
學生1:s1,地址:西湖區
學生2:s1,地址:杭州市

從運行的結果看序例化也達到了深拷貝的場景。

參考和閱讀的幾偏深拷貝淺拷貝的文章:

https://www.cnblogs.com/null00/archive/2010/12/14/2065088.html

https://www.cnblogs.com/xuanxufeng/p/6558330.html

https://blog.csdn.net/baiye_xing/article/details/71788741

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM