1. 程式人生 > >Java物件序列化為什麼要使用SerialversionUID

Java物件序列化為什麼要使用SerialversionUID

1、首先談談為什麼要序列化物件

把物件轉換為位元組序列的過程稱為物件的序列化。
  把位元組序列恢復為物件的過程稱為物件的反序列化。
  物件的序列化主要有兩種用途:
  1) 把物件的位元組序列永久地儲存到硬碟上,通常存放在一個檔案中;
  2) 在網路上傳送物件的位元組序列。

  在很多應用中,需要對某些物件進行序列化,讓它們離開記憶體空間,入住物理硬碟,以便長期儲存。比如最常見的是Web伺服器中的Session物件,當有 10萬用戶併發訪問,就有可能出現10萬個Session物件,記憶體可能吃不消,於是Web容器就會把一些seesion先序列化到硬碟中,等要用了,再把儲存在硬碟中的物件還原到記憶體中。

  當兩個程序在進行遠端通訊時,彼此可以傳送各種型別的資料。無論是何種型別的資料,都會以二進位制序列的形式在網路上傳送。傳送方需要把這個Java物件轉換為位元組序列,才能在網路上傳送;接收方則需要把位元組序列再恢復為Java物件
  
2、為什麼要使用SerialversionUID呢

簡單看一下 Serializable介面的說明
If a serializable class does not explicitly declare a serialVersionUID, then the serialization runtime will calculate a default serialVersionUID value for that class based on various aspects of the class, as described in the Java(TM) Object Serialization Specification.
如果使用者沒有自己宣告一個serialVersionUID,介面會預設生成一個serialVersionUID
However, it is stronglyrecommended that all serializable classes explicitly declareserialVersionUID values, since the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpectedInvalidClassExceptions during deserialization.
但是強烈建議使用者自定義一個serialVersionUID,因為預設的serialVersinUID對於class的細節非常敏感,反序列化時可能會導致InvalidClassException這個異常。
e.g:1.使用預設的serialVersionUID
我們先建一個實體類Person 實現Serializable介面

public class Person implements Serializable {


private int age;
private String name;
private String sex;


public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

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;
}
}
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
29
30
31
32
  然後去序列化和反序列化它
  

public class TestPersonSerialize {

public static void main(String[] args) throws Exception {
serializePerson();
Person p = deserializePerson();
System.out.println(p.getName()+";"+p.getAge());


}

private static void serializePerson() throws FileNotFoundException,IOException {
Person person = new Person();
person.setName("測試例項");
person.setAge(25);
person.setSex("male");

ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(
new File("E:/person.txt")));
oo.writeObject(person);
System.out.println("序列化成功");
oo.close();
}

private static Person deserializePerson() throws IOException, Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("E:/person.txt")));
Person person = (Person) ois.readObject();
System.out.println("反序列化成功");
return person;
}
}
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
29
30
  結果如圖
  
  e.g:2
  如果我們先盡心序列化,然後在反序列化之前修改了Person類會怎樣呢
  

public class Person implements Serializable {

private int age;
private String name;
private String sex;
private String address;

public String getAddress() {
return address;
}

public void setAddress(String address) {
this.address = address;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

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;
}
}
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
29
30
31
32
33
34
35
36
37
38
39
40
  執行結果
  
  可以看到,當我們修改Person類的時候,Person類對應的SerialversionUID也變化了,而序列化和反序列化就是通過對比其SerialversionUID來進行的,一旦SerialversionUID不匹配,反序列化就無法成功。在實際的生產環境中,我們可能會建一系列的中間Object來反序列化我們的pojo,為了解決這個問題,我們就需要在實體類中自定義SerialversionUID。
  e.g:3 在Person類中加入自定義SerialversionUID
  

public class Person implements Serializable {

private static final long serialVersionUID = -5809782578272943999L;
private int age;
private String name;
private String sex;
private String address;

public String getAddress() {
return address;
}

public void setAddress(String address) {
this.address = address;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

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;
}
}
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
29
30
31
32
33
34
35
36
37
38
39
40
  不管我們序列化之後如何更改我們的Person(不刪除原有欄位),最終都可以反序列化成功。