java提高篇(六)-----使用序列化實現對象的拷貝

來源:互聯網
上載者:User

標籤:

我們知道在Java中存在這個介面Cloneable,實現該介面的類都會具備被拷貝的能力,同時拷貝是在記憶體中進行,在效能方面比我們直接通過new產生對象來的快,特別是在大對象的產生上,使得效能的提升非常明顯。然而我們知道拷貝分為深拷貝和淺拷貝之分,但是淺拷貝存在對象屬性拷貝不徹底問題。關於深拷貝、淺拷貝的請參考這裡:漸析java的淺拷貝和深拷貝

一、淺拷貝問題

我們先看如下代碼:

public class Person implements Cloneable{    /** 姓名 **/    private String name;        /** 電子郵件 **/    private Email email;    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public Email getEmail() {        return email;    }    public void setEmail(Email email) {        this.email = email;    }        public Person(String name,Email email){        this.name  = name;        this.email = email;    }        public Person(String name){        this.name = name;    }    protected Person clone() {        Person person = null;        try {            person = (Person) super.clone();        } catch (CloneNotSupportedException e) {            e.printStackTrace();        }                return person;    }}public class Client {    public static void main(String[] args) {        //寫封郵件        Email email = new Email("請參加會議","請與今天12:30到二會議室參加會議...");                Person person1 =  new Person("張三",email);                Person person2 =  person1.clone();        person2.setName("李四");        Person person3 =  person1.clone();        person3.setName("王五");                System.out.println(person1.getName() + "的郵件內容是:" + person1.getEmail().getContent());        System.out.println(person2.getName() + "的郵件內容是:" + person2.getEmail().getContent());        System.out.println(person3.getName() + "的郵件內容是:" + person3.getEmail().getContent());    }}--------------------Output:張三的郵件內容是:請與今天12:30到二會議室參加會議...李四的郵件內容是:請與今天12:30到二會議室參加會議...王五的郵件內容是:請與今天12:30到二會議室參加會議...

      在該應用程式中,首先定義一封郵件,然後將該郵件發給張三、李四、王五三個人,由於他們是使用相同的郵件,並且僅有名字不同,所以使用張三該對象類拷貝李四、王五對象然後更改下名字即可。程式一直到這裡都沒有錯,但是如果我們需要張三提前30分鐘到,即把郵件的內容修改下:

public class Client {    public static void main(String[] args) {        //寫封郵件        Email email = new Email("請參加會議","請與今天12:30到二會議室參加會議...");                Person person1 =  new Person("張三",email);                Person person2 =  person1.clone();        person2.setName("李四");        Person person3 =  person1.clone();        person3.setName("王五");                person1.getEmail().setContent("請與今天12:00到二會議室參加會議...");                System.out.println(person1.getName() + "的郵件內容是:" + person1.getEmail().getContent());        System.out.println(person2.getName() + "的郵件內容是:" + person2.getEmail().getContent());        System.out.println(person3.getName() + "的郵件內容是:" + person3.getEmail().getContent());    }}

      在這裡同樣是使用張三該對象實現對李四、王五拷貝,最後將張三的郵件內容改變為:請與今天12:00到二會議室參加會議...。但是結果是:

張三的郵件內容是:請與今天12:00到二會議室參加會議...李四的郵件內容是:請與今天12:00到二會議室參加會議...王五的郵件內容是:請與今天12:00到二會議室參加會議...

      這裡我們就疑惑了為什麼李四和王五的郵件內容也發送了改變呢?讓他們提前30分鐘到人家會有意見的!

      其實出現問題的關鍵就在於clone()方法上,我們知道該clone()方法是使用Object類的clone()方法,但是該方法存在一個缺陷,它並不會將對象的所有屬性全部拷貝過來,而是有選擇性的拷貝,基本規則如下:

      1、 基本類型

         如果變數是基本很類型,則拷貝其值,比如int、float等。

      2、 對象

         如果變數是一個執行個體對象,則拷貝其地址引用,也就是說此時新對象與原來對象是公用該執行個體變數。

      3、 String字串

         若變數為String字串,則拷貝其地址引用。但是在修改時,它會從字串池中重建一個新的字串,原有紫都城對象保持不變。

      基於上面上面的規則,我們很容易發現問題的所在,他們三者公用一個對象,張三修改了該郵件內容,則李四和王五也會修改,所以才會出現上面的情況。對於這種情況我們還是可以解決的,只需要在clone()方法裡面建立一個對象,然後張三引用該對象即可:

protected Person clone() {        Person person = null;        try {            person = (Person) super.clone();            person.setEmail(new Email(person.getEmail().getObject(),person.getEmail().getContent()));        } catch (CloneNotSupportedException e) {            e.printStackTrace();        }                return person;    }

      所以:淺拷貝只是Java提供的一種簡單的拷貝機制,不便於直接使用。

      對於上面的解決方案還是存在一個問題,若我們系統中存在大量的對象是通過拷貝產生的,如果我們每一個類都寫一個clone()方法,並將還需要進行深拷貝,建立大量的對象,這個工程是非常大的,這裡我們可以利用序列化來實現對象的拷貝。

二、利用序列化實現對象的拷貝

      如何利用序列化來完成對象的拷貝呢?在記憶體中通過位元組流的拷貝是比較容易實現的。把母對象寫入到一個位元組流中,再從位元組流中將其讀出來,這樣就可以建立一個新的對象了,並且該新對象與母對象之間並不存在引用共用的問題,真正實現對象的深拷貝。

public class CloneUtils {    @SuppressWarnings("unchecked")    public static <T extends Serializable> T clone(T obj){        T cloneObj = null;        try {            //寫入位元組流            ByteArrayOutputStream out = new ByteArrayOutputStream();            ObjectOutputStream obs = new ObjectOutputStream(out);            obs.writeObject(obj);            obs.close();                        //分配記憶體,寫入原始對象,產生新對象            ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray());            ObjectInputStream ois = new ObjectInputStream(ios);            //返回產生的新對象            cloneObj = (T) ois.readObject();            ois.close();        } catch (Exception e) {            e.printStackTrace();        }        return cloneObj;    }}

      使用該工具類的對象必須要實現Serializable介面,否則是沒有辦法實現複製的。

public class Person implements Serializable{    private static final long serialVersionUID = 2631590509760908280L;    ..................    //去除clone()方法}public class Email implements Serializable{    private static final long serialVersionUID = 1267293988171991494L;        ....................}

      所以使用該工具類的對象只要實現Serializable介面就可實現對象的複製,無須繼承Cloneable介面實現clone()方法。

public class Client {    public static void main(String[] args) {        //寫封郵件        Email email = new Email("請參加會議","請與今天12:30到二會議室參加會議...");                Person person1 =  new Person("張三",email);                Person person2 =  CloneUtils.clone(person1);        person2.setName("李四");        Person person3 =  CloneUtils.clone(person1);        person3.setName("王五");        person1.getEmail().setContent("請與今天12:00到二會議室參加會議...");                System.out.println(person1.getName() + "的郵件內容是:" + person1.getEmail().getContent());        System.out.println(person2.getName() + "的郵件內容是:" + person2.getEmail().getContent());        System.out.println(person3.getName() + "的郵件內容是:" + person3.getEmail().getContent());    }}-------------------Output:張三的郵件內容是:請與今天12:00到二會議室參加會議...李四的郵件內容是:請與今天12:30到二會議室參加會議...王五的郵件內容是:請與今天12:30到二會議室參加會議...

鞏固基礎,提高技術,不懼困難,攀登高峰!!!!!!

參考文獻《編寫高品質代碼 改善Java程式的151個建議》----秦小波

java提高篇(六)-----使用序列化實現對象的拷貝

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.