Java序列化接口,為什么提倡所有類都實現(xiàn)?
什么是序列化和反序列化?
Java提供了一種對象序列化的機制。
序列化:把java對象轉(zhuǎn)換為字節(jié)序列的過程;在網(wǎng)絡(luò)傳輸對象或者將對象持久化到文件中時,將對象轉(zhuǎn)換成有序字節(jié)流,字節(jié)流中包含對象完整的狀態(tài)數(shù)據(jù)信息,保證對象的完整性和可傳遞性。
反序列化:把字節(jié)序列轉(zhuǎn)換為java對象的過程;程序在文件或者網(wǎng)絡(luò)傳輸中,獲取到字節(jié)流后,根據(jù)字節(jié)流中包含的對象狀態(tài)數(shù)據(jù)信息,重建java對象的過程。
可以將序列化想象成人去銀行存錢的過程。
將現(xiàn)金放入ATM機中,存入銀行卡中的過程可當(dāng)作序列化的過程。
現(xiàn)金 => ATM => 銀行卡
java對象 => JVM => 字節(jié)序列
將銀行卡中的錢通過ATM取出的過程,可以當(dāng)作反序列化的過程。
銀行卡 => ATM => 現(xiàn)金
字節(jié)序列 => JVM => java對象
為什么要序列化?
在文件存儲和網(wǎng)絡(luò)傳輸時,所有的文件,視頻也好,文本,圖片,音頻也好,都是以二進制序列的形式進行存儲或傳輸。
那么在java兩個進程中進行通信的時候,就需要我們也同樣利用二進制進行傳輸,而往往客戶端在接收到服務(wù)端傳輸過來的字節(jié)序列后,由jvm將字節(jié)序列轉(zhuǎn)換為java對象,已供程序使用。
由于序列化是jvm提供的一種機制,所有序列化可以保證在一個平臺中序列化后的對象,可以在另一個平臺上進行重建出來
Java如何實現(xiàn)序列化和反序列化?
JDK序列化接口
java.io.ObjectOutputStream:表示對象輸出流。
try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("user.txt"))){
objectOutputStream.writeObject(userOut);
}
初始化一個指向user.text文件的輸出流。
objectOutputStream.writeObject(userOut);方法可以將對象二進制輸出到指向的user.text中。
java.io.ObjectInputStream:表示對象輸入流。
try(ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("user.txt"))) {
User user = (User) objectInputStream.readObject();
}
初始化一個指向uset.text文件的輸入流。
objectInputStream.readObject();方法可以將文件user.text中的字節(jié)序列讀取并重建對象,重建后可直接強制轉(zhuǎn)換為序列化前的對象。
實例化對象的要求
只有自身類或者父類、實現(xiàn)類實現(xiàn)了Serializable接口的類對象才能被序列化,否則拋出java.io.NotSerializableException異常,需要注意的是,如果類的成員變量屬于類對象,那么也需要實現(xiàn)Serializable接口才能實例化,否則也將拋出異常。
若實現(xiàn)了Serializable接口,在序列化時,將遞歸將對象的屬性(方法不會序列化)轉(zhuǎn)化為字節(jié)序列:
ObjectOutputStream采用默認(rèn)的序列化方式,對User對象的非transient的實例變量進行序列化。
ObjcetInputStream采用默認(rèn)的反序列化方式,對User對象的非transient的實例變量進行反序列化。
序列化和反序列化代碼實例:
import java.io.*;
public class TestSerializable {
public static void main(String[] args) throws IOException, ClassNotFoundException {
User userOut = new User("榴蓮java", 23,"a");
try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("user.txt"))){
objectOutputStream.writeObject(userOut);
}
try(ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("user.txt"))) {
User userPut = (User) objectInputStream.readObject();
System.out.println(userPut.toString());
}
}
static class User implements Serializable {
private static final long serialVersionUid = 9992L;
private transient String name;
private Integer age;
public User(String name, Integer age, String sex) {
this.name = name;
this.age = age;
}
public String toString(){
return "name:" + this.name + "\tage:" + this.age;
}
}
}
執(zhí)行結(jié)果:
name:null age:23
transient關(guān)鍵字詳解
大家也看到了,上述代碼執(zhí)行結(jié)果中name變量并未實例化,原因是name被transient關(guān)鍵字修飾,用transient關(guān)鍵字標(biāo)記的成員變量將不參與類的實例化和反實例化過程。