記錄一次序列化引發的問題解決辦法 查看已編譯類序列化值java
本文主要內容:git
1:怎麼查看已經編譯的類的序列化(SerialVersionUid)的值數組
2:實現了Serializable接口的對象若是不顯示的給出序列化值,默認值怎麼算出來的app
3:拓展知識:序列化與反序列化及爲何要將類序列化ide
來源:凱哥Java(kaigejava)工具
凱哥我的博客:www.kaigejava.com開發工具
昨天快下班的時候遇到了一個這樣的問題:網站
java.io.InvalidClassException:xxxx(具體文件全路徑);local class incompatible:stream classdesc servialversionUid= XXXX,local calss serialVersionUid=xxxx。具體以下圖:ui
嘛意思呢?idea
其實就是說,本地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的時候,必需要求對象實現序列化。
圖片上傳不了,能夠去凱哥我的博客網站查看:www.kaigejava.com