帶你了解Java的序列化(Serializable)與反序列化

      網友投稿 896 2025-04-02

      這篇可幫助你大體了解Java中的序列化(Serializable)。包括為什么需要它,如何工作,何時使用它,相關概念(serialVersionUID和transient)以及有關序列化和反序列化的其他必要信息。本教程中的序列化示例保持簡單,以幫助你理解要點。


      目錄

      1.為什么要進行Java序列化

      2.Java中的序列化如何工作

      2-1.什么是serialVersionUID常數

      2-2.什么是瞬時變量?

      3.有關Java序列化的更多信息

      1.為什么要進行Java序列化

      序列化過程:

      是指把一個Java對象變成二進制內容,實質上就是一個byte[]數組。

      因為序列化后可以把byte[]保存到文件中,或者把byte[]通過網絡傳輸到遠程(IO),這樣,就相當于把Java對象存儲到文件或者通過網絡傳輸出去了。

      反序列化過程:

      把一個二進制內容(也就是byte[]數組)變回Java對象。有了反序列化,保存到文件中的byte[]數組又可以“變回”Java對象,或者從網絡上讀取byte[]并把它“變回”Java對象。

      以下是一些使用序列化的示例:

      -以面向對象的方式將數據存儲到磁盤上的文件,例如,Redis存儲Student對象的列表。 -將程序的狀態保存在磁盤上,例如,保存游戲狀態。 -通過網絡以表單對象形式發送數據,例如,在聊天應用程序中以對象形式發送消息。

      1

      2

      3

      4

      5

      一個Java對象要能序列化,必須實現一個特殊的java.io.Serializable接口,它的定義如下:

      public interface Serializable { }

      1

      2

      Serializable接口沒有定義任何方法,它是一個空接口。我們把這樣的空接口稱為“標記接口”(Marker Interface),實現了標記接口的類僅僅是給自身貼了個“標記”,并沒有增加任何方法。

      2.Java中的序列化如何工作

      當且僅當對象的類實現

      java.io.Serializable

      接口時,該對象才有資格進行序列化??尚蛄谢?是一個標記接口(不包含任何方法),該接口告訴Java虛擬機(JVM)該類的對象已準備好寫入持久性存儲或通過網絡進行讀取。

      默認情況下,JVM負責編寫和讀取可序列化對象的過程。序列化/反序列化功能通過對象流類的以下兩種方法公開:

      ObjectOutputStream。writeObject(Object)

      :將

      可序列化

      的對象寫入輸出流。如果要序列化的某些對象未實現Serializable接口,則此方法將引發

      NotSerializableException

      。

      ObjectInputStream。readObject()

      :從輸入流讀取,構造并返回一個對象。如果找不到序列化對象的類,則此方法將引發

      ClassNotFoundException

      如果序列化使用的類有問題,則這兩種方法都將引發

      InvalidClassException

      ,如果發生I / O錯誤,則將引發

      IOException

      。無論

      NotSerializableException

      InvalidClassException

      是子類

      IOException

      異常。

      讓我們來看一個簡單的例子。以下代碼將String對象序列化為名為“ data.ser”的文件。字符串對象是可序列化的,因為String類實現了

      Serializable

      接口:

      String filePath = "data.ser"; String message = "Java Serialization is Cool"; try ( FileOutputStream fos = new FileOutputStream(filePath); ObjectOutputStream outputStream = new ObjectOutputStream(fos); ) { outputStream.writeObject(message); } catch (IOException ex) { System.err.println(ex); }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      以下代碼反序列化文件“ data.ser”中的String對象:

      String filePath = "data.ser"; try ( FileInputStream fis = new FileInputStream(filePath); ObjectInputStream inputStream = new ObjectInputStream(fis); ) { String message = (String) inputStream.readObject(); System.out.println("Message: " + message); } catch (ClassNotFoundException ex) { System.err.println("Class not found: " + ex); } catch (IOException ex) { System.err.println("IO error: " + ex); }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      請注意,readObject()返回一個Object類型的對象,因此您需要將其強制轉換為可序列化的類,在這種情況下為String類。

      讓我們看一個涉及使用自定義類的更復雜的示例。

      給定以下

      學生班

      import java.io.*; import java.util.*; /** * Student.java * @author chenhh */ public class Student extends Person implements Serializable { public static final long serialVersionUID = 1234L; private long studentId; private String name; private transient int age; public Student(long studentId, String name, int age) { super(); this.studentId = studentId; this.name = name; this.age = age; System.out.println("Constructor"); } public String toString() { return String.format("%d - %s - %d", studentId, name, age); } }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      如上面代碼,你會發現兩點:

      -long serialVersionUID類型的常量。 -成員變量age被標記為transient。

      1

      2

      3

      下面讓我解釋一下它們。

      2-1.什么是serialVersionUID常數

      serialVersionUID

      是一個常數,用于唯一標識可序列化類的版本。從輸入流構造對象時,JVM在反序列化過程中檢查此常數。如果正在讀取的對象的

      serialVersionUID

      與類中指定的序列號不同,則JVM拋出

      InvalidClassException

      。這是為了確保正在構造的對象與具有相同

      serialVersionUID

      的類兼容。

      請注意,

      serialVersionUID

      是可選的。這意味著如果您不顯式聲明Java編譯器,它將生成一個。

      那么,為什么要顯式聲明

      serialVersionUID

      呢?

      原因是:自動生成的

      serialVersionUID

      是基于類的元素(成員變量,方法,構造函數等)計算的。如果這些元素之一發生更改,

      serialVersionUID

      也將更改。想象一下這種情況:

      -您編寫了一個程序,將Student類的某些對象存儲到文件中。Student類沒有顯式聲明的serialVersionUID。 -有時,您更新了Student類(例如,添加了一個新的私有方法),現在自動生成的serialVersionUID也被更改了。 -您的程序無法反序列化先前編寫的Student對象,因為那里的serialVersionUID不同。JVM拋出InvalidClassException。

      1

      2

      3

      4

      5

      這就是為什么建議為可序列化類顯式添加serialVersionUID的原因。

      2-2.什么是瞬時變量?

      在上面的Student類中,您看到成員變量age被標記為transient,對嗎?JVM 在序列化過程中跳過瞬態變量。這意味著在序列化對象時不會存儲age變量的值。

      因此,如果成員變量不需要序列化,則可以將其標記為瞬態。

      以下代碼將Student對象序列化為名為“ students.ser”的文件:

      String filePath = "students.ser"; Student student = new Student(123, "John", 22); try ( FileOutputStream fos = new FileOutputStream(filePath); ObjectOutputStream outputStream = new ObjectOutputStream(fos); ) { outputStream.writeObject(student); } catch (IOException ex) { System.err.println(ex); }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      請注意,在序列化對象之前,變量age的值為22。

      下面的代碼從文件中反序列化Student對象:

      String filePath = "students.ser"; try ( FileInputStream fis = new FileInputStream(filePath); ObjectInputStream inputStream = new ObjectInputStream(fis); ) { Student student = (Student) inputStream.readObject(); System.out.println(student); } catch (ClassNotFoundException ex) { System.err.println("Class not found: " + ex); } catch (IOException ex) { System.err.println("IO error: " + ex); }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      此代碼將輸出以下輸出:

      1個

      123 - John - 0

      3.有關Java序列化的更多信息

      你應該了解一些有關序列化的重要信息:

      序列化一個對象時,它所引用的所有其他對象也會被序列化,依此類推,直到序列化完整的對象樹為止。

      如果超類實現Serializable,則其子類會自動執行。

      反序列化可序列化類的實例時,構造函數將不會運行。

      如果超類未實現Serializable,則在反序列化子類對象時,超類構造函數將運行。

      靜態變量未序列化,因為它們不是對象本身的一部分。

      如果序列化集合或數組,則每個元素都必須可序列化。單個不可序列化的元素將導致序列化失?。?/p>

      NotSerializableException

      )。

      帶你了解Java的序列化(Serializable)與反序列化

      JDK中的可序列化類包括原始包裝器(

      Integer,Long,Double

      等),

      String,Date,collection

      類…對于其他類,請查閱相關的Javadoc來了解它們是否可序列化。

      Java JVM

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:Linux之fgrep命令
      下一篇:wps表格怎樣粘貼公式(wps如何帶公式粘貼)
      相關文章
      久久久久亚洲精品无码网址色欲| 亚洲精品高清久久| 国产亚洲日韩一区二区三区| 亚洲av日韩精品久久久久久a| 亚洲一卡2卡三卡4卡无卡下载| 亚洲国产成人综合| 亚洲电影在线免费观看| 亚洲视频在线免费观看| 亚洲国产精品lv| 亚洲高清视频在线观看| 亚洲成熟xxxxx电影| 久久亚洲精品人成综合网| 亚洲妇熟XXXX妇色黄| 亚洲AV无码成人精品区在线观看| 亚洲av永久无码制服河南实里| 亚洲av无码成人黄网站在线观看| 久久精品国产精品亚洲艾草网 | 亚洲AV香蕉一区区二区三区| 亚洲精品无码你懂的| 亚洲av成人无码网站…| 偷自拍亚洲视频在线观看| 国产亚洲日韩在线a不卡| 亚洲日韩人妻第一页| 国产亚洲精品岁国产微拍精品| 亚洲成在人线av| 亚洲精品高清国产麻豆专区| 亚洲中文无码av永久| 亚洲精品乱码久久久久蜜桃| 一本色道久久88综合亚洲精品高清| 亚洲精品动漫人成3d在线| 亚洲色成人WWW永久网站| 亚洲精品免费视频| 亚洲国产成人精品无码区在线网站| 中文字幕亚洲男人的天堂网络| 亚洲精品人成网线在线播放va| 亚洲国产精品成人久久蜜臀| 亚洲一区AV无码少妇电影☆| 国产亚洲综合久久系列| 内射少妇36P亚洲区| 亚洲人成网站色在线观看| 九九精品国产亚洲AV日韩|