Java 物件序列化 NIO NIO2 深度解析
物件序列化
物件序列化機制允許把記憶體中的Java物件轉換成與平臺無關的二進位制流,從而可以儲存到磁碟或者進行網路傳輸,其它程式獲得這個二進位制流後可以將其恢復成原來的Java物件。 序列化機制可以使物件可以脫離程式的執行而對立存在
序列化的含義和意義
序列化
序列化機制可以使物件可以脫離程式的執行而對立存在
序列化(Serialize)指將一個java物件寫入IO流中,與此對應的是,物件的反序列化(Deserialize)則指從IO流中恢復該java物件
如果需要讓某個物件可以支援序列化機制,必須讓它的類是可序列化(serializable),為了讓某個類可序列化的,必須實現如下兩個介面之一:
- Serializable:標記介面,實現該介面無須實現任何方法,只是表明該類的例項是可序列化的
- Externalizable
所有在網路上傳輸的物件都應該是可序列化的,否則將會出現異常;所有需要儲存到磁碟裡的物件的類都必須可序列化;程式建立的每個JavaBean類都實現Serializable;
使用物件流實現序列化
實現Serializable實現序列化的類,程式可以通過如下兩個步驟來序列化該物件:
1.建立一個ObjectOutputStream,這個輸出流是一個處理流,所以必須建立在其他節點流的基礎之上
// 建立個ObjectOutputStream輸出流 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"));
2.呼叫ObjectOutputStream物件的writeObject方法輸出可序列化物件
// 將一個Person物件輸出到輸出流中 oos.writeObject(per);
定義一個NbaPlayer類,實現Serializable介面,該介面標識該類的物件是可序列化的
public class NbaPlayer implements java.io.Serializable { private String name; private int number; // 注意此處沒有提供無引數的構造器! public NbaPlayer(String name, int number) { System.out.println("有引數的構造器"); this.name = name; this.number = number; } // name的setter和getter方法 public void setName(String name) { this.name = name; } public String getName() { return this.name; } // number的setter和getter方法 public void setNumber(int number) { this.number = number; } public int getNumber() { return this.number; } }
使用ObjectOutputStream將一個NbaPlayer物件寫入磁碟檔案
import java.io.*; public class WriteObject { public static void main(String[] args) { try( // 建立一個ObjectOutputStream輸出流 ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream("object.txt"))) { NbaPlayer player = new NbaPlayer("維斯布魯克", 0); // 將player物件寫入輸出流 oos.writeObject(player); } catch (IOException ex) { ex.printStackTrace(); } } }
反序列化
從二進位制流中恢復Java物件,則需要使用反序列化,程式可以通過如下兩個步驟來序列化該物件:
1.建立一個ObjectInputStream輸入流,這個輸入流是一個處理流,所以必須建立在其他節點流的基礎之上
// 建立個ObjectInputStream輸出流 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt"));
2.呼叫ObjectInputStream物件的readObject()方法讀取流中的物件,該方法返回一個Object型別的Java物件,可進行強制型別轉換成其真實的型別
// 從輸入流中讀取一個Java物件,並將其強制型別轉換為Person類 Person p = (Person)ois.readObject();
從object.txt檔案中讀取NbaPlayer物件的步驟
import java.io.*; public class ReadObject { public static void main(String[] args) { try( // 建立一個ObjectInputStream輸入流 ObjectInputStream ois = new ObjectInputStream( new FileInputStream("object.txt"))) { // 從輸入流中讀取一個Java物件,並將其強制型別轉換為NbaPlayer類 NbaPlayer player = (NbaPlayer)ois.readObject(); System.out.println("名字為:" + player.getName() + "\n號碼為:" + player.getNumber()); } catch (Exception ex) { ex.printStackTrace(); } } }
反序列化讀取的僅僅是Java物件的資料,而不是Java類,因此採用反序列化恢復Java物件時,必須提供Java物件所屬的class檔案,否則會引發ClassNotFoundException異常;反序列化機制無須通過構造器來初始化Java物件
如果使用序列化機制向檔案中寫入了多個Java物件,使用反序列化機制恢復物件必須按照實際寫入的順序讀取。當一個可序列化類有多個父類時(包括直接父類和間接父類),這些父類要麼有無參的構造器,要麼也是可序列化的—否則反序列化將丟擲InvalidClassException異常。如果父類是不可序列化的,只是帶有無引數的構造器,則該父類定義的Field值不會被序列化到二進位制流中
物件引用的序列化
如果某個類的Field型別不是基本型別或者String型別,而是另一個引用型別,那麼這個引用型別必須是可序列化的,否則有用該型別的Field的類也是不可序列化的
public class AllStar implements java.io.Serializable { private String name; private NbaPlayer player; public AllStar(String name, NbaPlayer player) { this.name = name; this.player = player; } // 此處省略了name和player的setter和getter方法 // name的setter和getter方法 public String getName() { return this.name; } public void setName(String name) { this.name = name; } // player的setter和getter方法 public NbaPlayer getPlayer() { return player; } public void setPlayer(NbaPlayer player) { this.player = player; } }
Java特殊的序列化演算法
- 所有儲存到磁碟中的物件都有一個序列化編號
- 當程式試圖序列化一個物件時,程式將先檢查該物件是否已經被序列化過,只有該物件從未(在本次虛擬中機)被序列化過,系統才會將該物件轉換成位元組序列並輸出
- 如果某個物件已經序列化過,程式將只是直接輸出一個序列化編號,而不是再次重新序列化該物件
import java.io.*; public class WriteAllStar { public static void main(String[] args) { try( // 建立一個ObjectOutputStream輸出流 ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream("allStar.txt"))) { NbaPlayer player = new NbaPlayer("詹姆斯哈登", 13); AllStar allStar1 = new AllStar("西部全明星", player); AllStar allStar2 = new AllStar("首發後衛", player); // 依次將四個物件寫入輸出流 oos.writeObject(allStar1); oos.writeObject(allStar2); oos.writeObject(player); oos.writeObject(allStar2); } catch (IOException ex) { ex.printStackTrace(); } } }
4個寫入輸出流的物件,實際上只序列化了3個,而且序列的兩個AllStar物件的player引用實際是同一個NbaPlayer物件。以下程式讀取序列化檔案中的物件
import java.io.*; public class ReadAllStar { public static void main(String[] args) { try( // 建立一個ObjectInputStream輸出流 ObjectInputStream ois = new ObjectInputStream( new FileInputStream("allStar.txt"))) { // 依次讀取ObjectInputStream輸入流中的四個物件 AllStar star1 = (AllStar)ois.readObject(); AllStar star2 = (AllStar)ois.readObject(); NbaPlayer player = (NbaPlayer)ois.readObject(); AllStar star3 = (AllStar)ois.readObject(); // 輸出true System.out.println("star1的player引用和player是否相同:" + (star1.getPlayer() == player)); // 輸出true System.out.println("star2的player引用和player是否相同:" + (star2.getPlayer() == player)); // 輸出true System.out.println("star2和star3是否是同一個物件:" + (star2 == star3)); } catch (Exception ex) { ex.printStackTrace(); } } }
如果多次序列化同一個可變Java物件時,只有第一次序列化時才會把該Java物件轉換成位元組序列並輸出
當使用Java序列化機制序列化可變物件時,只有第一次呼叫WriteObject()方法來輸出物件時才會將物件轉換成位元組序列,並寫入到ObjectOutputStream;即使在後面程式中,該物件的例項變數發生了改變,再次呼叫WriteObject()方法輸出該物件時,改變後的例項變數也不會被輸出
import java.io.*; public class SerializeMutable { public static void main(String[] args) { try( // 建立一個ObjectOutputStream輸入流 ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream("mutable.txt")); // 建立一個ObjectInputStream輸入流 ObjectInputStream ois = new ObjectInputStream( new FileInputStream("mutable.txt"))) { NbaPlayer player = new NbaPlayer("斯蒂芬庫裡", 30); // 系統會player物件轉換位元組序列並輸出 oos.writeObject(player); // 改變per物件的name例項變數 player.setName("塞斯庫裡"); // 系統只是輸出序列化編號,所以改變後的name不會被序列化 oos.writeObject(player); NbaPlayer player1 = (NbaPlayer)ois.readObject(); //① NbaPlayer player2 = (NbaPlayer)ois.readObject(); //② // 下面輸出true,即反序列化後player1等於player2 System.out.println(player1 == player2); // 下面依然看到輸出"斯蒂芬庫裡",即改變後的例項變數沒有被序列化 System.out.println(player2.getName()); } catch (Exception ex) { ex.printStackTrace(); } } }
相關文章
- Java NIO2:緩衝區Java
- 深度解析JAVA序列化Java
- Tinking in Java ---Java的NIO和物件序列化Java物件
- 使用Java NIO 和 NIO2實現檔案輸入/輸出Java
- tomcat nio2原始碼分析Tomcat原始碼
- java物件序列化Java物件
- 理解Java物件序列化Java物件
- Java物件流與序列化Java物件
- java 物件序列化要序列化那些內容Java物件
- 談談 JAVA 的物件序列化Java物件
- 深入理解Java物件序列化Java物件
- Java物件的序列化和反序列化實踐Java物件
- Java ThreadLocal深度解析Javathread
- Java物件的序列化與反序列化-Json篇Java物件JSON
- Java基礎22--物件序列化和反序列化Java物件
- 【NIO】Java NIO之通道Java
- java單例模式深度解析Java單例模式
- 物件序列化(序列化)物件
- Java NIOJava
- 為什麼Java需要物件的序列化Java物件
- 【NIO】Java NIO之緩衝Java
- Java NIO系列2:NIO概述Java
- java 解析php序列化資料問題JavaPHP
- java8Stream原理深度解析Java
- 深度解析Java記憶體原型Java記憶體原型
- Java網路程式設計與NIO詳解10:深度解讀Tomcat中的NIO模型Java程式設計Tomcat模型
- 【NIO】Java NIO之選擇器Java
- Java NIO - BufferJava
- Java NIO - 群聊Java
- JAVA NIO BufferJava
- Java NIO filesJava
- JAVA 探究NIOJava
- Java NIO:通道Java
- Java NIO 通道Java
- java培訓教程分享:Java中怎樣將資料物件序列化和反序列化?Java物件
- java基礎-java NIOJava
- Java:對一個物件序列化和反序列化的簡單實現Java物件
- JAVA集合:TreeMap紅黑樹深度解析Java