2019-09-21

記錄一次序列化引起的問題解決辦法?查看已編譯類序列化值

本文主要內容:

1:怎么查看已經編譯的類的序列化(SerialVersionUid)的值

2:實現了Serializable接口的對象如果不顯示的給出序列化值,默認值怎么算出來的

3:拓展知識:序列化與反序列化及為什么要將類序列化

來源:凱哥Java(kaigejava)

昨天快下班的時候遇到了一個這樣的問題:

java.io.InvalidClassException:xxxx(具體文件全路徑);local class incompatible:stream classdesc?servialversionUid= XXXX,local calss serialVersionUid=xxxx。具體如下圖:嘛意思呢?

其實就是說,本地xx類流描述的序列化值是XXXX,但是在編譯運行后值是xxx的問題。導致反序列化失敗。

這種問題,說真的,想排查問題原因何在不好找,想要解決問題容易。找到對應的類,里面把serialVersionUid的值寫成提示的值就可以。其實也沒有怎么修改東西,就在類上實現了序列化接口,為什么會出現這種情況呢?而且已經編譯過的類怎么查看其序列化值呢?

經過搜索得到解決方法。如下:

一:怎么查看已經編譯過類的序列化值?

使用的是開發工具是idea,版本管理工具是git.

切換到出問題的分支上(非必須),檢查代碼之后,在idea的導航欄中Build--Build Project(不同版本之間名稱或許不一樣)。快捷鍵:ctrl+F9

將項目編譯完成之后,找到已編譯文件所在目錄。并在cmd中到對應目錄中。這里查找文件使用一個神器:everything.搜索電腦上東西很快的,而且軟件也很小。不到2M.

如果文件名稱有重復的,可以按照時間倒敘,最近查詢到修改的。快速定位到文件所在目錄。

切換到對應目錄之后,進入到class文件所在的包的頂級目錄所在的目錄。也就是項目的target的classes目錄下。然后執行serialver?文件的完全包路徑名稱。如:serialver com.kaigejava.kgseed.model.Person

運行如下:

就可以看到Person類的序列化值為-1.這個是顯示寫的。這個是顯示的序列化值。也就是直接在類中寫出來的。

我們在來看看,不顯示寫的結果是什么:

類中沒有寫serialVersionUID的值。我們在運行上面命令,查看值:

發現值變化了。

二:Java中實現了serializable接口,默認值怎么算出來的?

有時候,類實現了serializable接口之后,沒有顯示的給出serialVersionUID。這種情況下,編譯后的文件中uid值是怎么算出來的?

我們來看看JDK幫助文檔關于Serializable接口有如下關于SerialVersionUid的描述:

This?readResolve?method follows the same invocation rules and accessibility rules as?writeReplace.

The serialization runtime associates with each?serializable?class a version number, called a?serialVersionUID, which is used during deserialization to verify that the sender and receiver of a serialized object have loaded classes for that object that are compatible with respect to serialization. If the receiver has loaded a class for the object that has a different?serialVersionUID?than that of the corresponding sender's class, then deserialization will result in an

InvalidClassException. A?serializable?class can declare its own?serialVersionUID?explicitly by declaring a field named "serialVersionUID"

that?must be static, final, and of type long:

?ANY-ACCESS-MODIFIER static final long?serialVersionUID?= 42L;

?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. However, it is strongly recommended that all?serializable?classes explicitly declare?serialVersionUID?values, since the default?serialVersionUID?computation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpected

InvalidClassExceptions?during deserialization. Therefore, to guarantee a consistent?serialVersionUID?value across different java compiler implementations, a?serializable?class must declare an explicit?serialVersionUID?value. It is also strongly advised that explicit?serialVersionUID?declarations use the

private?modifier where possible, since such declarations apply only to the immediately declaring class--serialVersionUID?fields are not useful as inherited members. Array classes cannot declare an explicit?serialVersionUID, so they always have the default computed value, but the requirement for matching?serialVersionUID?values is waived for array classes.

重點如下圖:

uid的長度是是一個長度42的long類型的。

最后一段話:

如果可序列化的類未明確聲明serialVersionUID,則序列化運行時將根據該類的各個方面,為該類計算默認的serialVersionUID值,如Java(TM)對象序列化規范中所述。但是,強烈建議所有可序列化的類顯式聲明serialVersionUID值,因為默認的serialVersionUID計算對類詳細信息高度敏感,類詳細信息可能會因編譯器的實現而有所不同,因此可能在反序列化期間導致意外的InvalidClassExceptions。因此,為了保證不同Java編譯器實現之間的serialVersionUID值一致,可序列化的類必須聲明一個顯式的serialVersionUID值。還強烈建議顯式serialVersionUID聲明在可能的情況下使用private修飾符,因為此類聲明僅適用于立即聲明的類-serialVersionUID字段作為繼承成員沒有用。數組類無法聲明顯式的serialVersionUID,因此它們始終具有默認的計算值,但是對于數組類,無需匹配serialVersionUID值。

官方給出的:雖然會根據類計算出默認的uid值,但是強烈建議所有的可序列化類都顯示聲明uid的值。

為了驗證是否真如官方說的,序列化運行時候將根據該類的各個方面,為該來計算默認的UID值。我們做如下實驗:

我們在換成jdk1.7編譯,還是用默認的。再看看這個值是不是有變化化:

切換項目將jdk換成1.7:重新編譯:

使用JDK1.7?和1.8?在類沒有發生變化的時候,UID值都是一樣的。

驗證默認生成的uid和類變化有沒有關系,我們在類中添加一些東西,來看看是否會影響值變化:

先添加一個@Data這個注解:

在運行,查看uid的值:我們發現,在添加了注解前和注解后的值發生了變化。

我們在在類中添加一個string類型的name屬性:

?

再看運行后結果:

發現,值又不一樣了。所以,我們可以得出,uid的值變化和類有關的。所以,官方強烈建議顯示設置uid的值。

三:序列化和反序列化是什么及為什么需要使用序列化?

序列化:把對象轉換為字節序列的過程被稱為對象的序列化

反序列化:把字節序列恢復為對象過程為對象的反序列化

最常見的是,當我們通過RPC遠程調用的時候。如使用dubbo的時候,必須要求對象實現序列化。

本文來源:凱哥Java。

個人博客:www.kaigejava.com

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 對象的創建與銷毀 Item 1: 使用static工廠方法,而不是構造函數創建對象:僅僅是創建對象的方法,并非Fa...
    孫小磊閱讀 2,019評論 0 3
  • JAVA序列化機制的深入研究 對象序列化的最主要的用處就是在傳遞,和保存對象(object)的時候,保證對象的完整...
    時待吾閱讀 10,907評論 0 24
  • JAVA 基礎知識大盤點(1) 一 . 什么是JAVA ...
    我是一只小小猿閱讀 152評論 0 0
  • html簡介 超文本標記語言(HyperText Markup lange,簡稱html),用來創建網頁的標記語言...
    lsx的簡書閱讀 380評論 0 0
  • 今天畫一畫男生,明顯感覺沒有女士那圓圓的眼睛好畫,這兩天畫的都不好,因為心情也不是很好
    我的世界因為有你閱讀 267評論 0 0