JAVA中對象的序列化

對象的序列化

當創建對象時,程序運行時它就會存在,但是程序停止時,對象也就消失了.
但是如果希望對象在程序不運行的情況下仍能存在並保存其信息,將會非常有用,對象將被
重建並且擁有與程序上次運行時擁有的信息相同。可以使用對象的序列化。
 對象的序列化:   將內存中的對象直接寫入到文件設備中
 對象的反序列化: 將文件設備中持久化的數據轉換爲內存對象
基本的序列化由兩個方法產生:一個方法用於序列化對象並將它們寫入一個流,
另一個方法用於讀取流並反序列化對象。
    ObjectOutput
    writeObject(Object obj) 
              將對象寫入底層存儲或流。
    ObjectInput
    readObject() 
              讀取並返回對象。
由於上述ObjectOutput和ObjectInput是接口,所以需要使用具體實現類。
ObjectOutput
 ObjectOutputStream被寫入的對象必須實現一個接口:Serializable
否則會拋出:NotSerializableException 
ObjectInput
     ObjectInputStream    該方法拋出異常:ClassNotFountException     
ObjectOutputStream和ObjectInputStream 對象分別需要字節輸出流和字節輸入流
對象來構建對象。也就是這兩個流對象需要操作已有對象將對象進行本地持久化存儲。
案例:
序列化和反序列化Cat對象。
public class Demo3 {
    public static void main(String[] args) throws IOException,
            ClassNotFoundException {
        Cat cat = new Cat("tom", 3);
        FileOutputStream fos = new FileOutputStream(new File("c:\\Cat.txt"));
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(cat);
        System.out.println(cat);
        oos.close();
        // 反序列化
        FileInputStream fis = new FileInputStream(new File("c:\\Cat.txt"));
        ObjectInputStream ois = new ObjectInputStream(fis);
        Object readObject = ois.readObject();
        Cat cat2 = (Cat) readObject;
        System.out.println(cat2);
        fis.close();
}

class Cat implements Serializable {
    public String name;
    public int age;

    public Cat() {

    }

    public Cat(String name, int age) {

        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Cat [name=" + name + ", age=" + age + "]";
    }

}

Serializable

類通過實現 java.io.Serializable 接口以啓用其序列化功能。未實現此接口的類將
無法使其任何狀態序列化或反序列化。可序列化類的所有子類型本身都是可序列化的。序列
化接口沒有方法或字段,僅用於標識可序列化的語義。
所以需要被序列化的類必須是實現Serializable接口,該接口中沒有描述任何的屬性和
方法,稱之爲標記接口。
    如果對象沒有實現接口Serializable,在進行序列化時會拋出NotSerializableException 異常。
注意:
保存一個對象的真正含義是什麼?如果對象的實例變量都是基本數據類型,那麼就非常簡
單。但是如果實例變量是包含對象的引用,會怎麼樣?保存的會是什麼?很顯然在Java中
保存引用變量的實際值沒有任何意義,因爲Java引用的值是通過JVM的單一實例的上下文
中才有意義。通過序列化後,嘗試在JVM的另一個實例中恢復對象,是沒有用處的。
如下:
首先建立一個Dog對象,也建立了一個Collar對象。Dog中包含了一個Collar(項圈)
現在想要保存Dog對象,但是Dog中有一個Collar,意味着保存Dog時也應該保存
Collar。假如Collar也包含了其他對象的引用,那麼會發生什麼?意味着保存一個Dog對
象需要清楚的知道Dog對象的內部結構。會是一件很麻煩的事情。
    Java的序列化機制可以解決該類問題,當序列化一個對象時,Java的序列化機制會
    負責保存對象的所有關聯的對象(就是對象圖),反序列化時,也會恢復所有的相關內容。
口的類纔可以序列化。如果只是Dog實現了該接口,而Collar沒有實現該接口。會發生什
麼?
    Dog類和Collar類
import java.io.Serializable;

public class Dog implements Serializable {
    private Collar collar;
    private String name;

    public Dog(Collar collar, String name) {

        this.collar = collar;
        this.name = name;
    }

    public Collar getCollar() {
        return collar;
    }

}

class Collar {
    private int size;

    public int getSize() {
        return size;
    }

    public Collar(int size) {
        this.size = size;
    }

}

序列化
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class Demo4 {
    public static void main(String[] args) throws IOException {
        Collar coll = new Collar(10);
        Dog dog = new Dog(coll, "旺財");

        FileOutputStream fis = new FileOutputStream(new File("c:\\dog.txt"));
        ObjectOutputStream os = new ObjectOutputStream(fis);
        os.writeObject(dog);
    }
}

執行程序,出現了運行時異常。
Exception in thread "main" java.io.NotSerializableException: Collar

所以我們也必須將Dog中使用的Collar序列化。但是如果我們無法訪問Collar的源
代碼,或者無法使Collar可序列化,如何處理?
兩種解決方法:

一:繼承Collar類,使子類可序列化
-------------------

但是:如果Collar是final類,就無法繼承了。並且,如果Collar引用了其他非序列
化對象,也無法解決該問題。

二:transient
-----------

此時就可以使用transient修飾符,可以將Dog類中的成員變量標識爲transient
那麼在序列化Dog對象時,序列化就會跳過Collar。

public class Demo4 {
    public static void main(String[] args) throws IOException,
            ClassNotFoundException {
        Collar coll = new Collar(10);
        Dog dog = new Dog(coll, "旺財");
        System.out.println(dog.getCollar().getSize());

        FileOutputStream fis = new FileOutputStream(new File("c:\\dog.txt"));
        ObjectOutputStream os = new ObjectOutputStream(fis);
        os.writeObject(dog);

        // 反序列化
        FileInputStream fos = new FileInputStream(new File("c:\\dog.txt"));
        ObjectInputStream ois = new ObjectInputStream(fos);
        Object readObject = ois.readObject();
        Dog dog2 = (Dog) readObject;
        // Collar未序列化。
        dog2.getCollar().getSize();
    }
}

這樣我們具有一個序列化的Dog和非序列化的Collar。
此時反序列化Dog後,訪問Collar,就會出現運行時異常
10
Exception in thread "main" java.lang.NullPointerException
注意:序列化不適用於靜態變量,因爲靜態變量並不屬於對象的實例變量的一部分。靜態
變量隨着類的加載而加載,是類變量。由於序列化只適用於對象。

基本數據類型可以被序列化
public class Demo5 {
    public static void main(String[] args) throws IOException {
        // 創建序列化流對象
        FileOutputStream fis = new FileOutputStream(new File("c:\\basic.txt"));
        ObjectOutputStream os = new ObjectOutputStream(fis);
        // 序列化基本數據類型
        os.writeDouble(3.14);
        os.writeBoolean(true);
        os.writeInt(100);
        os.writeInt(200);

        // 關閉流
        os.close();

        // 反序列化
        FileInputStream fos = new FileInputStream(new File("c:\\basic.txt"));
        ObjectInputStream ois = new ObjectInputStream(fos);

        System.out.println(ois.readDouble());
        System.out.println(ois.readBoolean());
        System.out.println(ois.readInt());
        System.out.println(ois.readInt());

        fos.close();
    }
}

serialVersionUID

用於給類指定一個UID。該UID是通過類中的可序列化成員的數字簽名運算出來的一個long
型的值。
只要是這些成員沒有變化,那麼該值每次運算都一樣。
該值用於判斷被序列化的對象和類文件是否兼容。
如果被序列化的對象需要被不同的類版本所兼容。可以在類中自定義UID。
定義方式:static final long serialVersionUID = 42L;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章