1.什么是序列化和反序列化
序列化就是將java對象轉成字節序列的過程;反序列化就是將字節序列轉成java對象的過程。
java中,序列化的目的一種是需要將對象保存到硬盤上,一種是對象需要在網絡中傳輸。
2.序列化和反序列化的方式
序列化和反序列化有很多種方式,如JDK類庫中提供的序列化API、常用的json工具類等。本篇博客使用JDK提供的序列化API進行演示。重點說明serialVersionUID的作用。
假設現在有一個Student類,我們要對Student類進行序列化操作
①該類必須實現Serializable接口
public class Student implements Serializable{ private static final long serialVersionUID = -595470438262181967L; private String name ; private String sex; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } }
②在main方法中執行序列化和反序列化操作
public static void main(String[] args) throws Exception { //序列化 OutputStream os = new FileOutputStream(new File("D://student.txt")); ObjectOutputStream oos = new ObjectOutputStream(os); Student student = new Student(); student.setName("張三"); student.setSex("男"); oos.writeObject(student); //反序列化 InputStream is = new FileInputStream(new File("D://student.txt")); ObjectInputStream ois = new ObjectInputStream(is); Student student1 = new Student(); student1 = (Student) ois.readObject(); System.out.println(student1.getName()); }
輸出結果:張三
即student1對象在反序列化時進行了賦值
3.為什么要serialVersionUID
serialVersionUID: 字面意思上是序列化的版本號,凡是實現Serializable接口的類都有一個表示序列化版本標識符的靜態變量。
(1)下面進行測試,如果沒有serialVersionUID會出現什么?
①去掉Student類中的serialVersionUID屬性。執行main方法,結果顯示序列化成功!輸出張三
②修改Student類,在Student類中添加number字段
public class Student implements Serializable{ private String name ; private String sex; private String number; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } }
不執行序列化方法,只執行反序列化方法,結果出現異常:
Exception in thread "main" java.io.InvalidClassException: com.iot.study.serialize.Student; local class incompatible: stream classdesc serialVersionUID = -595470438262181967, local class serialVersionUID = -4254220179260112271 at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:621) at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1623) at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518) at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371) at com.iot.study.serialize.Client.main(Client.java:35)
意思是原來序列化的時候(沒有指定serialVersionUID)硬盤存的class的 serialVersionUID = -595470438262181967,而當前class的serialVersionUID = -4254220179260112271。 二者不一樣,無法反序列化。
原因分析:
serialVersionUID沒有指定時,java編譯器會自動給這個class進行一個摘要算法,類似於指紋算法,只要這個文件 多一個空格,得到的UID就會截然不同的,可以保證在這么多類中,這個編號是唯一的。所以,添加了一個number字段后,由於沒有顯指定 serialVersionUID,編譯器又為我們生成了一個UID,當然和前面保存在文件中的那個不會一樣了,於是就出現了2個序列化版本號不一致的錯誤。
(2)指定serialVersionUID測試
如果為Student類顯示的指定serialVersionUID,那么在序列化和反序列化的時候,即使修改了Student類中的部分內容,也能序列化成功。
4.serialVersionUID的取值
serialVersionUID的取值是Java運行時環境根據類的內部細節自動生成的。如果對類的源代碼作了修改,再重新編譯,新生成的類文件的serialVersionUID的取值有可能也會發生變化。
類的serialVersionUID的默認值完全依賴於Java編譯器的實現,對於同一個類,用不同的Java編譯器編譯,有可能會導致不同的 serialVersionUID,也有可能相同。為了提高serialVersionUID的獨立性和確定性,強烈建議在一個可序列化類中顯示的定義serialVersionUID,為它賦予明確的值。
顯式地定義serialVersionUID有兩種用途:
1、 在某些場合,希望類的不同版本對序列化兼容,因此需要確保類的不同版本具有相同的serialVersionUID;
2、 在某些場合,不希望類的不同版本對序列化兼容,因此需要確保類的不同版本具有不同的serialVersionUID。