Java基礎:序列化與反序列化

Java序列化是指把Java對象轉換爲字節序列的過程,而Java反序列化是指把字節序列恢復爲Java對象的過程。

序列化的使用場景

  • 永久性保存對象,保存對象的字節序列到本地文件或者數據庫中
  • 通過序列化以字節流的形式使對象在網絡中進行傳遞和接收
  • 通過序列化在進程間傳遞對象

序列化的好處

  • 實現了數據的持久化,通過序列化可以把數據永久地保存到硬盤上
  • 利用序列化實現遠程通信,即在網絡上傳送對象的字節序列

使用Serializable接口實現序列化

package xuliehua;

import java.io.Serializable;

public class Person implements Serializable {

    private static final long serialVersionUID = 6839537839090265344L;
    private int age;
    private String name;
    private String sex;

    public int getAge() {
        return age;
    }

    public String getName() {
        return name;
    }

    public String getSex() {
        return sex;
    }

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

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

    public void setSex(String sex) {
        this.sex = sex;
    }
}
package xuliehua;

import java.io.*;
import java.text.MessageFormat;

public class TestObjSerializeAndDeserialize {
    public static void main(String[] args) throws Exception {
        SerializePerson();//序列化Person對象
        Person p = DeserializePerson();//反序列Perons對象
        System.out.println(MessageFormat.format("name={0},age={1},sex={2}",
                p.getName(), p.getAge(), p.getSex()));
    }
    
    private static void SerializePerson() throws FileNotFoundException, IOException {
        Person person = new Person();
        person.setName("fate");
        person.setAge(25);
        person.setSex("男");
        // ObjectOutputStream 對象輸出流,將Person對象存儲到E盤的Person.txt文件中,完成對Person對象的序列化操作
        ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(
                new File("E:/Person.txt")));
        oo.writeObject(person);
        System.out.println("Person對象序列化成功!");
        oo.close();
    }
    
    private static Person DeserializePerson() throws Exception, IOException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
                new File("E:/Person.txt")));
        Person person = (Person) ois.readObject();
        System.out.println("Person對象反序列化成功!");
        return person;
    }

}

結果

Person對象序列化成功!
Person對象反序列化成功!
name=fate,age=25,sex=男

對象序列化包括如下步驟:

  • 創建一個對象輸出流,它可以包裝一個其他類型的目標輸出流,如文件輸出流;
  • 通過對象輸出流的writeObject()方法寫對象。

對象反序列化的步驟如下:

  • 創建一個對象輸入流,它可以包裝一個其他類型的源輸入流,如文件輸入流;
  • 通過對象輸入流的readObject()方法讀取對象。

使用Externalizable接口實現序列化

package xuliehua;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

public class PersonNew implements Externalizable {
    private static final long serialVersionUID = -7103994996403529702L;
    private int age;
    private String name;
    private String sex;

    public PersonNew(){

    }

    public int getAge() {
        return age;
    }

    public String getName() {
        return name;
    }

    public String getSex() {
        return sex;
    }

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

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

    public void setSex(String sex) {
        this.sex = sex;
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeInt(age);
        out.writeObject(name);
        out.writeObject(sex);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        age = in.readInt();
        name = (String) in.readObject();
        sex = (String) in.readObject();
    }
}

Externalizable繼承了Serializable,該接口中定義了兩個抽象方法:writeExternal()與readExternal()。
當使用Externalizable接口來進行序列化與反序列化的時候需要開發人員重寫writeExternal()與readExternal()方法。
在使用Externalizable進行序列化的時候,在讀取對象時,會調用被序列化類的無參構造器去創建一個新的對象,然後再將被保存對象的字段的值分別填充到新對象中。
所以,實現Externalizable接口的類必須要提供一個public的無參的構造器。

Externalizable和Serializable接口的區別

  • Externalizable繼承自Serializable接口
  • 需要我們重寫writeExternal()與readExternal()方法
  • 實現Externalizable接口的類必須要提供一個public的無參的構造器

serialVersionUID的作用

其目的是序列化對象版本控制,有關各版本反序列化時是否兼容。Java的序列化機制是通過判斷類的serialVersionUID來驗證版本一致性的。
如果在新版本中這個值修改了,新版本就不兼容舊版本,反序列化時會拋出InvalidClassException異常。
如果修改較小,比如僅僅是增加了一個屬性,我們希望向下兼容,老版本的數據都能保留,那就不用修改;
如果我們刪除了一個屬性,或者更改了類的繼承關係,必然不兼容舊數據,這時就應該手動更新版本號,即SerialVersionUid。

serialVersionUID有兩種顯示的生成方式
一是默認的1L,比如:private static final long serialVersionUID = 1L;
二是根據類名、接口名、成員方法及屬性等來生成一個64位的哈希字段,比如:private static final long serialVersionUID = xxxxL;

在實現這個Serializable 接口的時候,一定要給這個 serialVersionUID 賦值

靜態變量不會被序列化,序列化保存的是對象的狀態,靜態變量屬於類的狀態,因此 序列化並不保存靜態變量。

Transient 關鍵字作用

Transient 關鍵字的作用是控制變量的序列化,在變量聲明前加上該關鍵字,可以阻止該變量被序列化到文件中,在被反序列化後,transient 變量的值被設爲初始值,如 int 型的是 0,對象型的是 null

當一個父類實現序列化,子類自動實現序列化,不需要顯式實現Serializable接口

爲一個沒有實現Serializable接口的父類,編寫一個能夠序列化的子類 

1, 父類要有一個無參的constructor;

2, 子類要先序列化自身,然後子類要負責序列化父類的域

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章