Java序列化和反序列化

1. Java序列化和反序列化(What)

Java序列化(Serialize)是指將一個Java對象寫入IO流中;
Java反序列化(Deserialize)指的是從IO流中回復(fù)IO對象。

2. 序列化的意義(Why)

序列化機(jī)制可以將Java對象轉(zhuǎn)換為數(shù)據(jù)流用來保存在磁盤上或者通過網(wǎng)絡(luò)傳輸。這使得對象可以脫離程序獨(dú)立存在。

3. 如何進(jìn)行序列化(How)

為了使對象支持序列化機(jī)制,需要讓它的類變成可序列化的(serializable)。通過實現(xiàn)兩個接口之一實現(xiàn):

  • Serializable
  • Externalnalizable
3.1 序列化的步驟

實現(xiàn)了Serializable接口的類,可以通過兩個步驟序列化該對象:

  1. 創(chuàng)建建立在其他節(jié)點(diǎn)流上的ObjectOutputStream
//創(chuàng)建ObjectOutputStream輸出流
ObjectOutputStream oos = new ObejctOutputStream(new FileOutputStream("object.txt"));
  1. 調(diào)用ObjectOutputStream對象的writeObject()方法輸出可序列化對象
//將一個Person對象輸出到輸出流中
oos.writeObject(per);
3.2 序列化的代碼
  1. 定義一個Person類,實現(xiàn)了Serializable接口,標(biāo)識該類的對象是可序列化的。
public class Person implements java.io.Serializable {
    private String name;
    private int age;

    // 這里沒有無參構(gòu)造器
    public Person(String name, int age){
        this.name = name;
        this.age = age;
    }

    // name和age的setter和getter方法
    ...
}
  1. 將Person對象寫入硬盤
import java.io.*;
public class Test{
    public static void main(String[] args){
        try{
            // 創(chuàng)建ObjectOutputStream輸出流
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Object.txt"));
            Person per = new Person("Junzerg", 20);
            // 將per對象寫入輸出流
            oos.writeObject(per);
        }
        catch (IOException ex){
            ex.printStackTrace();
        }
    }   
}
3.3 序列化結(jié)果

AC ED 00 05 73 72 00 06 50 65 72 73 6F 6E 2A 98
15 B9 5C 2E C1 6C 02 00 02 49 00 03 61 67 65 4C
00 04 6E 61 6D 65 74 00 12 4C 6A 61 76 61 2F 6C
61 6E 67 2F 53 74 72 69 6E 67 3B 78 70 00 00 00

14 74 00 07 4A 75 6E 7A 65 72 67

共75字節(jié)。

3.4 序列化的字節(jié)數(shù)

把Person類的age屬性設(shè)置為Long,重新序列化,結(jié)果為:

AC ED 00 05 73 72 00 06 50 65 72 73 6F 6E BE CF
78 98 E0 A3 0B E9 02 00 02 4A 00 03 61 67 65 4C
00 04 6E 61 6D 65 74 00 12 4C 6A 61 76 61 2F 6C
61 6E 67 2F 53 74 72 69 6E 67 3B 78 70 00 00 00
00 00 00 00 14 74 00 07 4A 75 6E 7A 65 72 67

共79字節(jié)。

4. 反序列化

4.1 反序列化的步驟
  1. 創(chuàng)建一個建立在其他節(jié)點(diǎn)流基礎(chǔ)上的ObjectInputStream輸入流
// 出啊構(gòu)建一個ObjectInputStream輸入流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Object.txt"));
  1. 調(diào)用ObjectInputStream對象的readObject方法讀取流中的對象,該方法返回一個Java對象,再強(qiáng)制轉(zhuǎn)換為真實類型。
//從輸入流中讀取一個Java對象并將其強(qiáng)制類型轉(zhuǎn)換成Person類
Person p = (Person) ois.readObject();
4.2 反序列化的代碼

從前文創(chuàng)建的Object.txt中讀取Person類

import java.io.*;
public class ReadObject{
    public static void main(String[] args){
        try{
            // 創(chuàng)建一個ObjectInputStream輸入流
            ObjectInputStream ois = 
                new ObjectInputStream(new FileInputStream("Object.txt"));
            // 從輸入流中讀取一個Java對象,轉(zhuǎn)換為Person類
                Person p = (Person)ois.readObject();
                System.out.println("name: " + p.getName()
                    + "\n age: " + p.getAge());
        }
        catch(Exception ex){
            ex.printStackTrace();
        }
    }
}
4.3 反序列化的結(jié)果

name: Junzerg
age: 20

5 對象引用的序列化和序列化算法

5.1 對象引用的序列化

如果要序列化的類的某個成員變量是一個非String類型的引用類型,那么這個引用類型必須是可序列化的。
例如有一個Teacher類持有Person類的引用

public class Teacher implements Seializable{
    private String name;
    Private Person student;
    public Teacher(String name, Person student){
        this.name = name;
        this.student = student;
    }
    // 省略name和student的stter和getter方法
    ...
}

為了在反序列化Teacher對象時正確恢復(fù),Person類必須也是可序列化的,否則Teacher不可序列化

5.2 多個實例變量引用同一個引用對象的特殊情況

當(dāng)兩個Teacher對象引用同一個Person對象的時候:

Person per = new Person("Junzerg", 20);
Teacher t1 = new Teacher("Miss Li", per);
Teacher t2 = new Teacher("Mr Wu", per);

在程序依次序列化三個對象的過程中,看起來似乎會向輸出流中輸出三個Person對象。
這時當(dāng)程序從輸入流中反序列化這些對象時,就會得到三個Person對象,這樣這樣t1和t2引用的就不是同一個Person對象了。

5.3 Java序列化算法

為了避免5.2中出現(xiàn)的錯誤,Java的序列化算法如下:

  1. 所有保存在磁盤中的對象都有一個序列化編號
  2. 當(dāng)程序試圖序列化一個對象時,程序會先檢查該對象是否已經(jīng)被序列化過,只有改對象從未(在本次虛擬機(jī)中)被序列化過,系統(tǒng)才會將給對象轉(zhuǎn)換成字節(jié)序列并出輸出。
  3. 如果某個對象已經(jīng)被序列化過,程序?qū)⒅苯映鰰粋€序列化編號,而不是重新序列化該對象。

6. 自定義序列化

6.1 遞歸序列化

當(dāng)對某個對象及進(jìn)行序列化時,系統(tǒng)自動把該對象的所有實例變量依次進(jìn)行序列化,如果某個實例變量引用另一個對象,則被引用的變量也會被序列化,這種情況被稱為遞歸序列化。

6.2 transient關(guān)鍵字

在遞歸序列化的過程中,可能遇到不想被序列化或者不能被序列化的變量。這時可以使用transient關(guān)鍵字在序列化時忽略該變量,避免引發(fā)java.io.NotSerializableException異常。

6.3 transient關(guān)鍵字的使用
  1. 有帶有transient關(guān)鍵字修飾的變量的Person類
public class Person implements java.io.Serializable {
    private String name;
    private transient int age;

    // 這里沒有無參構(gòu)造器
    public Person(String name, int age){
        this.name = name;
        this.age = age;
    }

    // name和age的setter和getter方法
    ...
}

注意:transient關(guān)鍵字只能用于修飾實例變量,不可修飾Java程序中的其他部分。

  1. Person對象的序列化和反序列化。
public class TransientTest{
    public static void main(String[] args){
        try{
            // 創(chuàng)建ObjectOutputStream輸出流
            ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream("transient.txt"));
            //創(chuàng)建ObjectInputStream輸入流
            ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream("transient.txt"));
            Person per = new Person("Junzerg", 20);
            // 將per對象序列化輸出
            oos.writeObject(per);
            Person p = (Person) ois.readObject();
            System.out.println(p.getAge());
        }
        catch (Exception ex){
            ex.printStackTrace();
        }
    }
}
  1. 輸出結(jié)果
    3.1 控制臺輸出:

0

3.2 transient.txt內(nèi)容為:

AC ED 00 05 73 72 00 06 50 65 72 73 6F 6E DB F9
DD 8C 83 99 C5 2E 02 00 01 4C 00 04 6E 61 6D 65
74 00 12 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74
72 69 6E 67 3B 78 70 74 00 07 4A 75 6E 7A 65 72
67

大小為65字節(jié)。

可以看到per實例中的age變量并沒有序列化。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • JAVA序列化機(jī)制的深入研究 對象序列化的最主要的用處就是在傳遞,和保存對象(object)的時候,保證對象的完整...
    時待吾閱讀 11,163評論 0 24
  • 1.背景 某天,我在寫代碼定義 bean 的時候,順手寫了個 public class User implemen...
    李眼鏡閱讀 854評論 0 2
  • 什么是序列化? 序列化是將對象存儲為二進(jìn)制格式。在序列化的過程中,對象和它的元數(shù)據(jù)(比如對象的類名和它的屬性名稱)...
    Chokez閱讀 1,142評論 0 0
  • Java 提供了一種對象序列化的機(jī)制,該機(jī)制中,一個對象可以被表示為一個字節(jié)序列,該字節(jié)序列包括該對象的數(shù)據(jù)、有關(guān)...
    笑漫人生閱讀 472評論 0 0
  • 對象序列化(serialization)和反序列化(deserialization)是將對象轉(zhuǎn)化為便于傳輸?shù)母袷竭M(jìn)...
    JerryL_閱讀 7,628評論 1 7

友情鏈接更多精彩內(nèi)容