71 Serializable(序列化和反序列化)
對象的輸出流:ObjectOutputStream 把對象輸出到文件存儲起來,我們稱作為序列化
對象的輸入流:ObjectInputStream 把對象從文件中讀取出來,我們稱作為反序列化
ObjectOutputStream
構造方法:
ObjectOutputStream()
為完全重新實現 ObjectOutputStream 的子類提供一種方法,讓它不必分配僅由 ObjectOutputStream 的實現使用的私有數據。
ObjectOutputStream(OutputStream out)
創建寫入指定 OutputStream 的 ObjectOutputStream。
一些方法:
writeObject(Object obj) 將指定的對象寫入 ObjectOutputStream。
註意:其他的一些方法可以查看jdk文檔
ObjectInputStream
構造方法:
ObjectInputStream()
為完全重新實現 ObjectInputStream 的子類提供一種方式,讓它不必分配僅由 ObjectInputStream 的實現使用的私有數據。
ObjectInputStream(InputStream in)
創建從指定 InputStream 讀取的 ObjectInputStream。
一些方法:
readObject() 從 ObjectInputStream 讀取對象。
註意:readObject方法一次只會讀取一個對象,如果想要讀取多個對象需要利用EOFException
EOFException 此異常主要被數據輸入流用來表明到達流的末尾。
其他許多輸入操作返回一個特殊值表示到達流的末尾,而不是拋出異常。
註意:其他的一些方法可以查看jdk文檔
對象的輸入輸出流作用:對象的輸入輸出流主要的作用是用於讀寫對象的信息。對象一但寫到文件上那麽就可以做到持久化了
對象的輸入輸出流的使用步驟:
1.被序列化對象的所屬類必須實現Serializable(標識)接口
2.給被序列化對象的所屬類指定SerialVersionUID值(可選)
3.被序列化對象的所屬類的某些隱秘成員要使用transient修飾(可選)
對象輸入輸出流要註意的細節:
1.如果對象需要被寫出到文件上,那麽對象所屬的類必須要實現Serializable接口。Serializable只是一個沒有任何方法的接口,一般這樣的接口我們稱作為標識接口
2.對象的反序列化創建對象的時候並不會調用構造方法
3.SerialVersionUID適用於記錄被寫入對象所屬類的版本信息的 ,ServialVersionUID這個數字是通過一個類名、成員、包名、工程名算出來的一個數字
4.如果反序列化的時候,ServialVersionUID不一致,那麽會反序列化失敗,也就是接受ObjectOutputStream的writeObject方法的返回值的所屬類的版本信息一定要跟存入文本時的對象所屬類的版本信息一樣(類名、成員、包名、工程名一樣)
5.如果序列化和反序列化的時候可能會修改類的成員,那麽最好一開始就給這個類指定一個ServialVersionUID,那麽當我們把對象存入文本中的時候jvm不會自己再算ServialVersionUID。這個就可以保證接受對象和存入對象的所屬類的版本信息一樣,我們也就可以修改成員了(我自己偏向於指定,畢竟有時候自己並不知道會不會修改)
6.如果一個對象某些數據不想被序列化到硬盤上,那麽對象的所屬類的成員變量可以使用transient(中文:透明)修飾,這樣就可以保證被修飾的數據就不會序列化到硬盤上,當然反序列的時候也得不到這個數據
7.如果要被序列化對象所屬類的內部維護了另一個類的引用,那麽另一個類也需要實現Serializable接口,當然版本信息也需要相同
下面是一些實例:
class Work implements Serializable{ private static final long serialVersionUID = 1L; String workname; double money; public Work(String workname , double money) { this.workname = workname; this.money = money; } } class People implements Serializable{ //指定版本編號 private static final long serialVersionUID = 1L; transient int card; //身份證涉及隱私我們可以使用transient修飾,不序列化到硬盤中 String name; int age; //維護一個另一個類的引用 Work work; public People(int card ,String name , int age,Work work) { this.card = card; this.name = name; this.age = age; this.work = work; } //自定義輸出格式 @Override public String toString() { return "{省份證編號:"+this.card+" 姓名:"+this.name+" 年齡:"+this.age+" 工作:"+work.workname+" 工資:"+this.work.money+"}"; } } public class Demo1 { public static void main(String[] args) throws IOException, ClassNotFoundException { writeObject(); readObject(); } //序列化,把對象存入硬盤中 public static void writeObject() throws IOException { //找到目標文件 File file = new File("D:\\新建文件夾\\a.txt"); //建立數據傳輸通道 FileOutputStream fileOutputStream = new FileOutputStream(file); //建立對象的傳輸通道 ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); //序列化對象,寫入硬盤 objectOutputStream.writeObject(new People(1001,"張三",18,new Work("打醬油",5000.0))); objectOutputStream.writeObject(new People(1002,"李四",19,new Work("收醬油",5000.0))); //關閉資源 objectOutputStream.close();//相當於fileOutputStream.close() } //反序列化 public static void readObject() throws IOException{ //找到目標文件 File file = new File("D:\\新建文件夾\\a.txt"); //建立數據傳輸通道 FileInputStream fileInputStream = new FileInputStream(file); //建立對象的傳輸通道 ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); //讀取文件 //EOFException 此異常主要被數據輸入流用來表明到達流的末尾。 //註意,其他許多輸入操作返回一個特殊值表示到達流的末尾,而不是拋出異常。 People p; while(true) { try { p = (People)objectInputStream.readObject(); System.out.println(p); } catch(EOFException e) { break; }catch(ClassNotFoundException e) { break; } } objectInputStream.close();//相當於fileInputStream.close() } }
文件被序列化到文本中存儲的內容
文件被反序列化
從這裏我們可以發現,我們使用transient修飾的card沒有被寫入和讀取出來(0是默認的初始值)
71 Serializable(序列化和反序列化)