java提高篇(五)-----使用序列化實現物件的拷貝
我們知道在Java中存在這個介面Cloneable,實作該介面的類別都會具備被拷貝的能力,同時拷貝是在記憶體中進行,在效能方面比我們直接透過new產生物件來的快,特別是在大對象的生成上,使得性能的提升非常明顯。然而我們知道拷貝分為深拷貝和淺拷貝之分,但是淺拷貝存在物件屬性拷貝不徹底問題。關於深拷貝、淺拷貝的請參考這裡:漸析java的淺拷貝和深拷貝
一、淺拷貝問題
然後將該郵件發給張三、李四、王五三個人,由於他們是使用相同的郵件,並且僅有名字不同,所以使用張三該對象類拷貝李四、王五對象然後更改下名字即可。程式一直到這裡都沒有錯,但是如果我們需要張三提前30分鐘到,即把郵件的內容修改下:
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到二会议室参加会议...
在這裡同樣是使用張三該對象實現對李四、王五拷貝,最後將張三的郵件內容改為:請與今天12:00到二會議室參加會議...。但結果是:
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()); } }
這裡我們就疑惑了為什麼李四和王五的郵件內容也發送了改變呢?讓他們提早30分鐘到人家會有意見的!
其實出現問題的關鍵在於clone()方法上,我們知道該clone()方法是使用Object類別的clone()方法,但是該方法存在一個缺陷,它並不會將物件的所有屬性全部拷貝過來,而是有選擇性的拷貝,基本規則如下:
1、 基本型別
若變數為基本款很型,則拷貝其值,如int、float等。
2、 物件
若變數為實例對象,則拷貝其位址引用,即表示此時新物件與原始物件是公用此實例變數。
3、 String字串
若變數為String字串,則拷貝其位址引用。但是在修改時,它會從字串池重新產生一個新的字串,原有紫都城物件保持不變。
基於上面上面的規則,我們很容易發現問題的所在,他們三者公用一個對象,張三修改了該郵件內容,則李四和王五也會修改,所以才會出現上面的情況。對於這種情況我們還是可以解決的,只需要在clone()方法裡面新建一個對象,然後張三引用該對象即可:
张三的邮件内容是:请与今天12:00到二会议室参加会议... 李四的邮件内容是:请与今天12:00到二会议室参加会议... 王五的邮件内容是:请与今天12:00到二会议室参加会议...
所以:淺拷貝只是Java提供的一種簡單的拷貝機制,不便於直接使用。
對於上面的解決方案還是存在一個問題,若我們系統中存在大量的物件是透過拷貝產生的,如果我們每一個類別都寫一個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; }
使用該工具類別的物件必須實現Serializable接口,否則是沒有辦法實現複製的。
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介面就可實現物件的克隆,無須繼承Cloneable介面實作clone()方法。
public class Person implements Serializable{ private static final long serialVersionUID = 2631590509760908280L; .................. //去除clone()方法 } public class Email implements Serializable{ private static final long serialVersionUID = 1267293988171991494L; .................... }
以上就是 java提高篇(五)-----使用序列化實現物件的拷貝的內容,更多相關內容請關注PHP中文網(www.php.cn)!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

PHP資料處理技巧:如何使用serialize和unserialize函數實現資料序列化與反序列化序列化和反序列化是電腦科學中常用的資料處理技巧之一。在PHP中,我們可以使用serialize()和unserialize()函數來實現資料的序列化和反序列化操作。本文將為您詳細介紹如何使用這兩個函數,並提供相關程式碼範例。一、什麼是序列化和反序列化在電腦編

Flexjson是一個輕量級函式庫,用於序列化和反序列化Java物件>和來自JSON格式。我們可以使用JSONSerializer類別的serialize()方法序列化物件清單。此方法可以對目標實例執行淺層序列化。我們需要將清單類型的物件清單作為參數傳遞給serialize()方法。語法publicStringserialize(Objecttarget)範例importflexjson.JSONSerializer;importjava.util.*;publicclassJsonSerial

C++函式庫序列化和反序列化指南序列化:建立輸出流並將其轉換為存檔格式。將物件序列化到存檔中。反序列化:建立輸入流並將其從存檔格式還原。從存檔中反序列化物件。實戰範例:序列化:建立輸出流。建立存檔物件。建立物件並將其序列化到存檔中。反序列化:建立輸入流。建立存檔物件。建立物件並從存檔中反序列化。

序列化对Java性能的影响:序列化过程依赖于反射,会显著影响性能。序列化需要创建字节流存储对象数据,导致内存分配和处理成本。序列化大对象会消耗大量内存和时间。序列化后的对象在网络上传输时会增加负载量。

@JsonPropertyOrder是在類別層級使用的註解。它採用字段列表作為屬性,該列表定義字段在物件JSON序列化生成的字串中出現的順序。可以先序列化註釋聲明中包含的屬性(按定義的順序),然後序列化定義中未包含的任何屬性。語法public@interfaceJsonPropertyOrder範例importcom.fasterxml.jackson.core.*;importcom.fasterxml.jackson.databind.*;importcom.fasterxml.jac

介面無法直接序列化,抽象類別可以序列化但前提是不包含非靜態、非瞬態欄位或覆寫writeObject()和readObject()方法,具體實例可透過實作介面的具體類別或覆寫writeObject()和readObject ()方法的抽象類別實作。

GoLang函數類型可透過encoding/gob套件實現序列化和反序列化。序列化:註冊自訂類型並使用gob.NewEncoder將函數類型編碼為位元組數組。反序列化:使用gob.NewDecoder從位元組數組反序列化函數類型。

Flexjson是一個輕量級函式庫,用於將Java物件序列化為JSON格式以及反序列化為JSON格式。我們也可以使用JSONSerializer類別的serialize()方法來序列化Map,它對目標實例執行淺層序列化。語法publicStringserialize(Objecttarget)範例importflexjson.JSONSerializer;importjava.util.*;publicclassJsonSerializeMapTest{ publ
