本篇使用Java 將物件序列化並輸出至檔案,然後將檔案反序列化為物件。
Java的序列化是指將一個物件(Object)序列化為位元組串流(Byte Streams),其包含了物件的型態和儲存值。目的為物件的持久化,也就是說"物件"可以被輸出為檔案,或儲存於資料庫。
由於序列化後的物件資訊是獨立於JVM外的,所以序列化物件可在兩個不同的平台間傳遞。
菜鳥工程師肉豬
下面範例使用ObjectOutputStream.writeObject()
將物件序列化輸出為檔案;使用ObjectInputStream.readObject()
將檔案反序列化回物件。
要被序列化及反序列化的物件必須實作Serializable
介面,否則在過程中會出現NotSerializableException
例外錯誤。
繼承了Serializable
會要求給定一個serialVersionUID
,用來代表示序列化物件的版本。
package com.abc.demo;
import java.io.*;
public class Main {
public static void main(String[] args) {
// 物件序列化
try (
FileOutputStream fos = new FileOutputStream("D:/MyClass");
ObjectOutputStream oos = new ObjectOutputStream(fos)
) {
MyClass obj1 = new MyClass("hello world", 1000);
System.out.println("obj1:" + obj1);
oos.writeObject(obj1);
oos.flush();
} catch (IOException e) {
e.printStackTrace();
}
// 物件反序列化
try (
FileInputStream fis = new FileInputStream("D:/MyClass");
ObjectInputStream ois = new ObjectInputStream(fis)
) {
MyClass obj2;
obj2 = (MyClass) ois.readObject();
System.out.println("obj2 " + obj2);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class MyClass implements Serializable {
private static final long serialVersionUID = 1L;
private String s;
private int i;
public MyClass(String s, int i) {
this.s = s;
this.i = i;
}
@Override
public String toString() {
return "s:" + s + ", i:" + i;
}
}
執行結果如下
obj1:s:hello world, i:1000
obj2 s:hello world, i:1000
若不讓MyClass
實作Serializable
介面,則執行時發生錯誤如下。
obj1:s:hello world, i:1000
java.io.NotSerializableException: com.abc.demo.MyClass
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at com.abc.demo.Main.main(Main.java:16)
java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: com.abc.demo.MyClass
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1577)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431)
at com.abc.demo.Main.main(Main.java:28)
Caused by: java.io.NotSerializableException: com.abc.demo.MyClass
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at com.abc.demo.Main.main(Main.java:16)
因為當ObjectOutputStream.writeObject()
將物件寫出時會檢查此物件是否為Serializable
的實例,若否則會丟出NotSerializableException
。原始碼節錄如下。
...
if (obj instanceof String) {
writeString((String) obj, unshared);
} else if (cl.isArray()) {
writeArray(obj, desc, unshared);
} else if (obj instanceof Enum) {
writeEnum((Enum<?>) obj, desc, unshared);
} else if (obj instanceof Serializable) { // <---- 判斷是否為Serializable的實例
writeOrdinaryObject(obj, desc, unshared);
} else {
if (extendedDebugInfo) {
throw new NotSerializableException(
cl.getName() + "\n" + debugInfoStack.toString());
} else {
throw new NotSerializableException(cl.getName()); // <---- 丟出錯誤
}
}
...
若在序列化類別的成員變數前加上transient
關鍵字,則該成員變數不會被序列化,例如在MyClass.i
前面加上transient
:
class MyClass implements Serializable{
private static final long serialVersionUID = 1L;
private String s;
transient private int i; // <---加上transient關鍵字
public MyClass(String s, int i) {
this.s = s;
this.i = i;
}
@Override
public String toString() {
return "s:" + s + ", i:" + i;
}
}
執行結果如下,可以看到因為i
的值沒有被序列化,因此從檔案反序列化回物件後,i的值為初始值0。
obj1:s:hello world, i:1000
obj2 s:hello world, i:0
沒有留言:
張貼留言