為什么阿里Java規約要求謹慎修改serialVersionUID字段


serialVersionUID簡要介紹

serialVersionUID是在Java序列化、反序列化對象時起作用的一個字段。Java的序列化機制是通過判斷類的serialVersionUID來驗證版本一致性的。在進行反序列化時,JVM會把傳來的字節流中的serialVersionUID與本地相應實體類的serialVersionUID進行比較,如果相同就認為是一致的,可以進行反序列化,否則就會出現序列化版本不一致的異常,即是InvalidClassException。

序列化是一種把對象持久化到外部的手段。在網絡傳輸等場景中應用廣泛,如Dubbo等框架。類通過實現 java.io.Serializable 接口以啟用其序列化功能。

阿里Java規約中的描述

img

阿里規約中強制性的提醒,修改serialVersionUID字段,會引起反序列化失敗的情況。

代碼演示

下面,通過實際代碼,來演示對象序列化、反序列化的操作。

引入依賴

<dependencies>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.8.1</version>
    </dependency>
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.6</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>

commons-lang3這個包有幫我們實現了序列化的工具類,commons-io幫我們實現了文件操作的工具類,junit用來寫單元測試。

序列化

有User.class如下:

@Data
@Builder
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    private String username;

}

@Data 和 @Builder 是Lombok里面的注解,可以自動生成getter、setter方法等,不懂的可以查看我之前的文章,對這個有講解。

需要序列化的類,需要實現Serializable接口,Serializable是一個標識接口,里面沒有具體需要實現的東西。

序列化代碼:

@Test
public void serializeTest() throws IOException {
    User user = User.builder().username("happyjava").build();
    byte[] serialize = SerializationUtils.serialize(user);
    FileUtils.writeByteArrayToFile(new File("serialize.txt"), serialize);
}

SerializationUtils是commons-lang3包給我們提供的工具類,它給我們提供了序列化和反序列化的方法,我們直接拿過來用就好了。

FileUtils是commons-io包給我們提供的工具類,它給我們提供了非常豐富的IO操作工具類。

執行方法后,查看serialize.txt:

這就是序列化后,保存到外部的對象了。我們可以把它反序列化成為對象。

反序列化

反序列化代碼如下:

@Test
public void deserializeTest() throws IOException {
    byte[] bytes = FileUtils.readFileToByteArray(new File("serialize.txt"));
    Object object = SerializationUtils.deserialize(bytes);
    System.out.println(object instanceof User);
    User user = (User)object;
    System.out.println(user.getUsername());
}

這里使用FileUtils把外部的文件讀入,使用SerializationUtils反序列化成為對象。然后判斷反序列化后對象的類型,強轉為User后輸出其username,運行結果如下:

說明反序列化已經成功過了

修改serialVersionUID值,使反序列化異常

現在把User的serialVersionUID值做一下修改:

@Data
@Builder
public class User implements Serializable {

    private static final long serialVersionUID = 2L;

    private String username;

}

之前是1,現在改為2,再次執行反序列化方法,結果如下:

這里出現了開頭提到的InvalidClassException異常,並且通過異常信息可以看到,這是serialVersionUID不一致引起的。

org.apache.commons.lang3.SerializationException: java.io.InvalidClassException: cn.happy.User; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2

總結

通過理論和代碼演示,我們已經知道了serialVersionUID字段可以造成反序列化失敗的情況。所以在做系統升級的時候,需要充分考慮是否要改動serialVersionUID的值,因為這會引起兼容性的問題。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM