對象序列化和反序列化(九)

勿以惡小而爲之,勿以善小而不爲--------------------------劉備

勸諸君,多行善事積福報,莫作惡

上一章簡單介紹了 DataOutputStream和DataInputStream(八),如果沒有看過,請觀看上一章

一. 爲什麼要序列化?

上一章節寫入文件的員工列表信息數據:

id name sex age desc
1 老蝴蝶 20 一個好人
2 蝴蝶 21 一個人
3 嶽澤霖 22 快樂的人
4 嶽建立 23 想要快樂的人

通過 DataOutputStream 和 DataInputStream ,發現太複雜了,需要固定的格式進行處理, 當數據信息複雜時,就瘋了。

我們發現,員工的信息可以封裝成一個對象,如 Person 對象, 該對象裏面有 id,name,sex,age,desc 屬性。

public class Person {

    private int id;
    private String name;
    private  char sex;
    private int age;
    private String desc;


    public Person() {
    }

    public Person(int id, String name, char sex, int age, String desc) {
        this.id = id;
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.desc = desc;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public char getSex() {
        return sex;
    }

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

    public int getAge() {
        return age;
    }

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

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", sex=" + sex +
                ", age=" + age +
                ", desc='" + desc + '\'' +
                '}';
    }
}

我們存儲時,能直接存儲對象就好了, 取數據時,直接取對象, 方便操作。

這種形式,其實就是序列化。

序列化是將對象存儲到文件裏面, 反序列化是將文件轉換成對象。

序列化和反序列化所需要的對象爲 ObjectOutputStream 和 ObjectInputStream.

二. 序列化 ObjectOutputStream

二.一 方法

二.一.一 構造方法

二.一.一.一 方法

方法 作用
ObjectOutputStream​(OutputStream out) 傳入 outputStream 對象

二.一.一.二 演示

  @Test
    public void objConTest() throws Exception{

        File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
                +File.separator+"data2.txt");
        //傳入  outputStream 
        ObjectOutputStream objectOutputStream=new ObjectOutputStream(new FileOutputStream(file));
    }

二.一.二 寫入方法

方法 作用
void flush​() 刷新此數據輸出流
void writeInt​(int v) 寫入 int 類型的值
void writeLong​(long v) 寫入long 類型的值
void writeFloat​(float v) 寫入float 值
void writeDouble​(double v) 寫入double 值
void writeBoolean​(boolean v) 寫入boolean 類型的值
void writeChar​(int v) 寫入char 類型的值, 可以直接寫入單箇中文字符。 可寫入 \t 和\n
void writeChars​(String s) 寫入字符串
void writeUTF​(String str) 使用機器無關的方式使用 modified UTF-8編碼將字符串寫入底層輸出流。
void writeObject​(Object obj) 寫入對象

對於 ObjectOutputStream 對象,只需要記住 writeObject() 方法即可。

二.二 演示 ObjectOutputStream

在序列化對象之前, 必須要保證 那個實體類, 即 Person 類,實現了 java.io.Serializable 接口, 否則會拋出異常的。

public class Person implements Serializable {


}

二.二.一 寫入 單個對象

  @Test
    public void write1Test() throws Exception{

        File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
                +File.separator+"data2.txt");
        ObjectOutputStream objectOutputStream=new ObjectOutputStream(new FileOutputStream(file));

        Person person=new Person(1,"兩個蝴蝶飛",'男',24,"一個快樂的程序員");

        //如果沒有序列化,會報錯。
        objectOutputStream.writeObject(person);

        objectOutputStream.close();
    }

運行程序, 查看 data2.txt 文件內容:

有圖片

大概可以猜測是三部分, 第一部分是 類全限定名稱, 第二部分是屬性, 第三部分是 數據。

二.二.二 寫入集合對象

同樣是 writeObject ()方法處理。

   @Test
    public void write2Test() throws Exception{

        File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
                +File.separator+"data3.txt");
        ObjectOutputStream objectOutputStream=new ObjectOutputStream(new FileOutputStream(file));

        Person person1=new Person(1,"兩個蝴蝶飛1",'男',24,"一個快樂的程序員1");
        Person person2=new Person(2,"兩個蝴蝶飛2",'男',24,"一個快樂的程序員2");
        Person person3=new Person(3,"兩個蝴蝶飛3",'男',24,"一個快樂的程序員3");
        Person person4=new Person(4,"兩個蝴蝶飛4",'男',24,"一個快樂的程序員4");

        Person[] ps=new Person[]{person1,person2,person3,person4};
        //如果沒有序列化,會報錯。
        objectOutputStream.writeObject(ps);

        objectOutputStream.close();
    }

運行程序,查看 data3.txt

有圖片

我們雖然看不懂,但 ObjectInputStream 卻能看懂,能解析。

三. 反序列化 ObjectInputStream

三.一 方法

三.一.一 構造方法

三.一.一.一 方法

方法 作用
ObjectInputStream​(InputStream in) 傳入 inputStream 對象

三.一.一.二 演示

  @Test
    public void readConTest() throws Exception{
        File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
                +File.separator+"data2.txt");

        ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream(file));
    }

三.一.二 讀取方法

方法 作用
int readInt​() 讀取 int ,實際上就是讀取4個長度
long readLong​() 讀取 long
float readFloat​() 讀取float
double readDouble​() 讀取double
boolean readBoolean​() 讀取boolean
char readChar​() 讀取char, 實際內部讀取兩個字節
String readUTF​() 讀取字符串
Object readObject​() 讀取對象

對於 ObjectInputStream 對象,只需要記住 readObject() 方法即可。

三.二 演示 ObjectInputStream

三.二.一 讀取序列化的單個對象

  @Test
    public void read1Test() throws Exception{
        File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
                +File.separator+"data2.txt");

        ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream(file));

       Object obj= objectInputStream.readObject();
       //向上轉型
       Person person=(Person)obj;

        System.out.println("員工:"+person.toString());

        objectInputStream.close();
    }

運行程序,查看控制檯打印輸出:

有圖片

三.二.二 讀取序列化的對象集合

 @Test
    public void read2Test() throws Exception{
        File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
                +File.separator+"data3.txt");

        ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream(file));

        Object readObj= objectInputStream.readObject();
        //對象數組
        Object[] objs=(Object[])readObj;

        for(Object obj:objs){
            //對每一個進行強制轉換。
            Person person=(Person)obj;
            System.out.println("員工:"+person);
        }

        objectInputStream.close();
    }

運行程序,查看控制檯打印輸出:

有圖片

可以正確的讀取。

四. 序列化部分屬性 transient

有時候,一個實體類中屬性過多,不需要全部保存起來,或者某個屬性太重要,需要保證安全,不能存儲起來, 那麼就需要 使用 transient 關鍵字進行阻止序列化。

如 阻止序列化 name 和 sex

有圖片

重新運行一下 write2Test() 和 read2Test() 方法,查看控制檯

有圖片

就發現, name 和 sex 沒有被序列化。

五. Externalizable 接口實現序列化

除了 Serializable 接口可以實現序列化外,還可以使用 java.io.Externalizable 接口 進行序列化, 但常用的還是 Serializable 接口, 這個接口 Externalizable 只需要瞭解即可。

package java.io;

import java.io.ObjectOutput;
import java.io.ObjectInput;

/**
 * @since   JDK1.1
 */
public interface Externalizable extends java.io.Serializable {
    void writeExternal(ObjectOutput out) throws IOException;
     
    void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
}

writeExternal() 方法 設置的是 保存哪些屬性,

readExternal() 方法,設置的是讀取哪些屬性。

注意,保存和讀取屬性的順序 必須保持一致。

五.一 Externalizable 實現序列化

五.一.一 實體類 實現 接口

public class Person2 implements Externalizable {

}

五.一.二 重寫 writeExternal() 方法,設置保存的屬性

 @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        //保存哪些, 保存的數據。
        out.writeInt(id);
        out.writeObject(name);
		//不保存 sex 屬性
        out.writeInt(age);
        out.writeObject(desc);
    }

五.一.三 重寫 readExternal() 方法,讀取保存的屬性

@Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {

        //讀取的, 按照保存的順序讀取
        this.id=in.readInt();
        this.name=(String)in.readObject();

        this.age=in.readInt();

        this.desc=(String)in.readObject();
    }

五.一.四 序列化單個對象

 @Test
    public void write1Test() throws Exception{

        File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
                +File.separator+"data4.txt");
        ObjectOutputStream objectOutputStream=new ObjectOutputStream(new FileOutputStream(file));

        Person2 Person2=new Person2(1,"兩個蝴蝶飛",'男',24,"一個快樂的程序員");

        //如果沒有序列化,會報錯。
        objectOutputStream.writeObject(Person2);

        objectOutputStream.close();
    }

與 以前的方法是一樣的。

運行程序,查看 data4.txt 的內容。

有圖片

五.一.五 反序列化單個對象

 @Test
    public void read1Test() throws Exception{
        File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
                +File.separator+"data4.txt");

        ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream(file));

       Object obj= objectInputStream.readObject();
       Person2 Person2=(Person2)obj;

        System.out.println("員工:"+Person2);

        objectInputStream.close();
    }

運行程序:

有圖片

對象集合的序列化和反序列化與以前的也是一樣的,就不重複寫了。

五.二 Serializable 和 Externalizable 的區別

區別 Serializable Externalizable
實現上 實現簡單,Java提供了支持 實現複雜,需要開發人員寫保存和讀取
執行效率上 由Java統一 保存,性能較低 開發人員決定,速度可能會高
保存信息所佔空間 空間大 空間較小

開發中,常使用的是 Serializable

序列化非常重要,一定要掌握。

謝謝您的觀看,如果喜歡,請關注我,再次感謝 !!!

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