Java對象的序列化與還原序列化

來源:互聯網
上載者:User

一、為什麼要進行序列化

再介紹之前,我們有必要先瞭解下對象的生命週期,我們知道Java中的對象都是存在於堆記憶體中的,而堆記憶體是可以被記憶體回收行程不定期回收的。從對象被建立到被回收這一段時間就是Java對象的生命週期,也即Java對象只存活於這個時間段內。

對象被記憶體回收行程回收意味著對象和對象中的成員變數所佔的記憶體也就被回收,這意味著我們就再也得不到該對象的任何內容了,因為已經被銷毀了嘛,當然我們可以再重新建立,但這時的對象的各種屬性都又被重新初始化了。所以如果我們需要儲存某對象的狀態,然後再在未來的某段時間將該對象再恢複出來的話,則必須要在對象被銷毀即被記憶體回收行程回收之前儲存對象的狀態。要儲存對象狀態的話,我們可以使用檔案、資料庫,也可以使用序列化,這裡我們主要介紹對象序列化。我們很有必要瞭解這方面的內容,因為對象序列化不僅在儲存對象狀態時可以被用到(對象持久化),在Java中的遠程方法調用RMI也會被用到,在網路中要傳輸對象的話,則必須要對對象進行序列化,關於RMI有機會我會再專門開貼介紹。

簡單總結起來,進行對象序列化的話的主要原因就是實現對象持久化和進行網路傳輸,這裡先只介紹怎樣通過對象序列化儲存對象的狀態。

下面我們通過一個簡單的例子來介紹下如何進行對象序列化。

二、怎樣進行對象序列化

假設我們要儲存Person類的某三個對象的name、age、height這三個成員變數,當然這裡只是簡單舉例

我們先看下Person類,要序列化某個類的對象的話,則該類必要實現Serializable介面,從Java API中我們發現該介面是個空介面,即該介面中沒聲明任何方法。

import java.io.Serializable;public class Person implements Serializable {int age;int height;String name;public Person(String name, int age, int height){this.name = name;this.age = age;this.height = height;}}

下面我們看一下如何來進行序列化,這其中主要涉及到Java的I/O方面的內容,主要用到兩個類FileOutputStream和ObjectOutputStream,FileOutputStream用於將位元組輸出到檔案,ObjectOutputStream通過調用writeObject方法將對象轉換為可以寫出到流的資料。所以整個流程是這樣的:ObjectOutputStream將要序列化的對象轉換為某種資料,然後通過FileOutputStream串連某磁碟檔案,再對象轉化的資料轉化為位元組資料再將其寫出到磁碟檔案。下面是具體代碼:

import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectOutputStream;public class MyTestSer {/** * Java對象的序列化與還原序列化 */public static void main(String[] args) {Person zhangsan = new Person("zhangsan", 30, 170);Person lisi = new Person("lisi", 35, 175);Person wangwu = new Person("wangwu", 28, 178);try {//需要一個檔案輸出資料流和對象輸出資料流;檔案輸出資料流用於將位元組輸出到檔案,對象輸出資料流用於將對象輸出為位元組ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("person.ser"));out.writeObject(zhangsan);out.writeObject(lisi);out.writeObject(wangwu);out.close();} catch (IOException e) {e.printStackTrace();}}}

三、對象的還原序列化

我們儲存的目的主要是為了再恢複使用,下面我們來看下加上還原序列化後的代碼:

import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;public class MyTestSer {/** * Java對象的序列化與還原序列化 */public static void main(String[] args) {Person zhangsan = new Person("zhangsan", 30, 170);Person lisi = new Person("lisi", 35, 175);Person wangwu = new Person("wangwu", 28, 178);try {//需要一個檔案輸出資料流和對象輸出資料流;檔案輸出資料流用於將位元組輸出到檔案,對象輸出資料流用於將對象輸出為位元組ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("person.ser"));out.writeObject(zhangsan);out.writeObject(lisi);out.writeObject(wangwu);} catch (IOException e) {e.printStackTrace();}try {ObjectInputStream in = new ObjectInputStream(new FileInputStream("person.ser"));Person one = (Person) in.readObject();Person two = (Person) in.readObject();Person three = (Person) in.readObject();System.out.println("name:"+one.name + " age:"+one.age + " height:"+one.height);System.out.println("name:"+two.name + " age:"+two.age + " height:"+two.height);System.out.println("name:"+three.name + " age:"+three.age + " height:"+three.height);} catch (Exception e) {e.printStackTrace();}}}

輸出結果如下:

name:zhangsan age:30 height:170name:zhangsan age:35 height:175name:zhangsan age:28 height:178

從添加的代碼我們可以看到進行還原序列化也很簡單,主要用到的流是FileInputstream和ObjectInputstream正好與儲存時用到的流相對應。另外從結果順序我們可以看到還原序列化後得到對象的順序與序列化時的順序一致。

四、總結

進行對象序列化主要目的是為了儲存對象的狀態(成員變數)。

進行序列化主要用到的流是FileOutputStream和ObjectOutputStream。FileOutputStream主要用於串連磁碟檔案,並把位元組寫出到該磁碟檔案;ObjectOutputStream主要用於將對象寫出為可轉化為位元組的資料。

要將某類的對象序列化,則該類必須實現Serializable介面,該介面僅是一個標誌,告訴JVM該類的對象可以被序列化。如果某類未實現Serializable介面,則該類對象不能實現序列化。

儲存狀態的目的就是為了在未來的某個時候再恢複儲存的內容,這可以通過還原序列化來實現。對象的還原序列化過程與序列化正好相反,主要用到的兩個流是FileInputstream和ObjectInputStream。

還原序列化後得到的對象的順序與儲存時的順序一致。

五、補充

補充一:上面我們舉得例子很簡單,要儲存的成員變數要麼是基本類型的要麼是String類型的。但有時成員變數有可能是參考型別的,這是的情況會複雜一點。那就是當要對某對象進行序列化時,該對象中的引用變數所引用的對象也會被同時序列化,並且該對象中如果也有引用變數的話則該對象也將被序列化。總結說來就是在序列化的時候,對象中的所有引用變數所對應的對象將會被同時序列化。這意味著,引用變數類型也都要實現Serializable介面。當然其他對象的序列化都是自動進行的。所以我們只要保證裡面的參考型別是都實現Serializable介面就行了,如果沒有的話,會在編譯時間拋出異常。如果序列化的對象中包含沒有實現Serializable的成員變數的話,這時可以使用transient關鍵字,讓序列化的時候跳過該成員變數。使用關鍵字transient可以讓你在序列化的時候自動跳過transient所修飾的成員變數,在還原序列化時這些變數會恢複到預設值。

補充二:如果某類實現了Serializable介面的話,其子類會自動編程可序列化的,這個好理解,繼承嘛。

補充三:在還原序列化的時候,並不會調用對象的構造器,這也好理解,如果調用了構造器的話,對象的狀態不就又重新初始化了嗎。

補充四:我們說到對象序列化的是為了儲存對象的狀態,即對象的成員變數,所以靜態變數不會被序列化。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.