1. 程式人生 > >深克隆(拷貝)與淺克隆(拷貝)

深克隆(拷貝)與淺克隆(拷貝)

串行化 deepcopy 裝飾者模式 依然 ring 對他 引用 puts void

一、什麽是克隆錢(拷貝)

    在實際編程過程中,我們常常遇到這種情況:有一個對象A,在某一時刻A中已經包含了一些有效值,此時可能會需要一個和A完全相同的新對象B,並且此後對B做任何改動都不會影響到A中的值,也就是說,A與B是兩個獨立的對象,但是B的初始值是由A對象確定的。在java語言中,用簡單的賦值語言是不能滿足這種需求的。要滿足這種需求雖然有很多途徑,但實現clone()方法是其中最簡單也是最高效的手段。

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

二、深拷貝與淺拷貝

  淺拷貝是指拷貝對象時僅僅拷貝對象本身(包括對象中的基本變量),而不拷貝對象包含的引用指向的對象。深拷貝不僅拷貝對象本身,而且拷貝對象包含的引用指向的所有對象。舉例來說更加清楚:對象A1中包含對B1的引用,B1中包含對C1的引用。淺拷貝A1得到A2,A2 中依然包含對B1的引用,B1中依然包含對C1的引用。深拷貝則是對淺拷貝的遞歸,深拷貝A1得到A2,A2中包含對B2(B1的copy)的引用,B2 中包含對C2(C1的copy)的引用。若不對clone()方法進行改寫,則調用此方法得到的對象即為淺拷貝。

三、代碼實現

1.淺克隆

package clone;

import com.sun.star.i18n.Implementation; /** * 克隆測試:淺克隆,其實就是把被復制的這個對象的一些變量值拿過來 * @author coco.xu * */ public class CloneTest1 { public static void main(String[] args) throws Exception { Student student = new Student(); student.setAge(18); student.setName(
"coco"); Student student2 = (Student)student.clone(); //這個是調用下面的那個方法,然後把這個這個對象Clone到student System.out.println("Age:" + student2.getAge() + "&&Name:" + student2.getName()); System.out.println("---------------------"); //克隆後得到的是一個新的對象,所以重新寫的是student2這個對象的值 student2.setAge(23); System.out.println(student.getAge()); System.out.println(student2.getAge()); } } /** * 克隆的對象必須實現Cloneable這個接口,而且需要重寫clone方法 * @author coco.xu * */ class Student implements Cloneable { private int age; //定義為private說明這個成員變量只能被被當前類中訪問,如果外部需要獲得,那麽就只能通過getAge方法進行獲取 private String name; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public Object clone() throws CloneNotSupportedException { Object object = super.clone(); return object; } }

2.深克隆

package clone;

/**
 * 深克隆
 * @author coco.xu
 *
 */
public class CloneTest2
{
    public static void main(String[] args) throws Exception
    {
        Teacher teacher = new Teacher();
        teacher.setAge(30);
        teacher.setName("coco xu");
        
        Student2 student2 = new Student2();
        student2.setAge(10);
        student2.setName("zhangsan");
        student2.setTeacher(teacher);
        
        Student2 student3 = (Student2)student2.clone();
        //這裏是深復制,所以這時候Student2中的teacher就是teacher這個對象的一個復制,就和student3是student2的一個復制
        //所以下面teacher.setName只是對他原來的這個對象更改,但是復制的那個並沒有更改
        System.out.println(student3.getAge());
        System.out.println(student3.getName());
        System.out.println(student3.getTeacher().getAge());
        teacher.setName("teacher xixi");//不會又任何影響
        System.out.println(student3.getTeacher().getName());
    
    }
 
}
class Student2 implements Cloneable
{
    private int age;
    private String name;
    private Teacher teacher;
    public int getAge()
    {
        return age;
    }
    public void setAge(int age)
    {
        this.age = age;
    }
    public String getName()
    {
        return name;
    }
    public void setName(String name)
    {
        this.name = name;
    }
    public Teacher getTeacher()
    {
        return teacher;
    }
    public void setTeacher(Teacher teacher)
    {
        this.teacher = teacher;
    }
    @Override
    public Object clone() throws CloneNotSupportedException
    {
        //這一步返回的這個student2還只是一個淺克隆,
        Student2 student2 = (Student2)super.clone();
        //然後克隆的過程中獲得這個克隆的student2,然後調用這個getTeacher這個方方法得到這個Teacher對象。然後實現克隆。在設置到這個student2中的Teacher。
        //這樣實現了雙層克隆使得那個teacher對象也得到了復制。
        student2.setTeacher((Teacher)student2.getTeacher().clone());
        //雙層克隆使得那個teacher對象也得到了復制
        return student2;
    }
}
class Teacher implements Cloneable
{
    private int age;
    private String name;
    public int getAge()
    {
        return age;
    }
    public void setAge(int age)
    {
        this.age = age;
    }
    public String getName()
    {
        return name;
    }
    public void setName(String name)
    {
        this.name = name;
    }
    @Override
    public Object clone() throws CloneNotSupportedException
    {
        return super.clone();
    }
    
}

3.利用serializable實現深克隆(這個是利用Serializable,利用序列化的方式來實現深克隆(深克隆),在其中利用了Io流的方式將這個對象寫到IO流裏面,然後在從IO流裏面讀取,這樣就實現了一個克隆,然後實現序列化的這個會將引用的那個對象也一並進行深克隆,這樣就實現了這個機制,同時在IO裏面讀取數據的時候還使用了裝飾者模式)

package clone;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class CloneTest3 {
    public static void main(String[] args) throws Exception {
        Teacher3 teacher3 = new Teacher3();
        teacher3.setAge(23);
        teacher3.setName("coco");

        Student3 student3 = new Student3();
        student3.setAge(50);
        student3.setName("xixi");
        student3.setTeacher3(teacher3);

        Student3 ss = (Student3) student3.deepCopy();
        System.out.println(ss.getAge());
        System.out.println(ss.getName());

        System.out.println("---------------------");
        System.out.println(ss.getTeacher3().getAge());
        System.out.println(ss.getTeacher3().getName());

        System.out.println("-----------------------");

        ss.getTeacher3().setAge(34);
        ss.getTeacher3().setName("hhhhh");

        System.out.println(teacher3.getAge());
        System.out.println(teacher3.getName());
        // 雖然上面的已經改了,但是改的是那個復制對象後的那個裏面的,然後那個原來的那個裏面的並沒有改,下面驗證:::

        System.out.println("-----------------");

        System.out.println(ss.getTeacher3().getAge());
        System.out.println(ss.getTeacher3().getName());

    }

}

class Teacher3 implements Serializable {
    // 上面的那個警告可以直接消除,除了使用在設置中不顯示這個警告,還可以使用下面的這兩條語句中的任何一條語句
    // 這個serialVersionUID為了讓該類別Serializable向後兼容
    private static final long serialVersionUID = 8940196742313994740L;
    private int age;
    private String name;

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

class Student3 implements Serializable {
    private static final long serialVersionUID = 1L;
    private int age;
    private String name;
    private Teacher3 teacher3;

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

    public Teacher3 getTeacher3() {
        return teacher3;
    }

    public void setTeacher3(Teacher3 teacher3) {
        this.teacher3 = teacher3;
    }

    // 使得序列化student3的時候也會將teacher序列化
    public Object deepCopy() throws Exception {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);
        // 將當前這個對象寫到一個輸出流當中,,因為這個對象的類實現了Serializable這個接口,所以在這個類中
        // 有一個引用,這個引用如果實現了序列化,那麽這個也會寫到這個輸出流當中

        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return ois.readObject();
        // 這個就是將流中的東西讀出類,讀到一個對象流當中,這樣就可以返回這兩個對象的東西,實現深克隆
    }
}

要想序列化對象,必須先創建一個OutputStream,然後把它嵌入ObjectOutputStream。這時就能用writeObject()方法把對象寫入OutputStream。讀的時候需要把InputStream嵌到ObjectInputStream中,然後再調用readObject()方法。不過這樣讀出來的只是一個Object的reference,因此,在用之前,還要下轉型。對象序列化不僅能保存對象的副本,而且會跟著對象中的reference把它所引用的對象也保存起來,然後再繼續跟蹤那些對象的reference,以此類推。這種情形常被稱作”單個對象所聯結的‘對象網’ “。
但是串行化卻很耗時,在一些框架中,我們便可以感受到,它們往往將對象進行串行化後進行傳遞,耗時較多。

深克隆(拷貝)與淺克隆(拷貝)