記錄一次序列化引起的問題解決辦法?查看已編譯類序列化值
創(chuàng)新互聯(lián)堅持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:做網(wǎng)站、網(wǎng)站設(shè)計、企業(yè)官網(wǎng)、英文網(wǎng)站、手機端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿足客戶于互聯(lián)網(wǎng)時代的和平網(wǎng)站設(shè)計、移動媒體設(shè)計的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!
本文主要內(nèi)容:
1:怎么查看已經(jīng)編譯的類的序列化(SerialVersionUid)的值
2:實現(xiàn)了Serializable接口的對象如果不顯示的給出序列化值,默認(rèn)值怎么算出來的
3:拓展知識:序列化與反序列化及為什么要將類序列化
來源:凱哥Java(kaigejava)
凱哥個人博客:www.kaigejava.com
昨天快下班的時候遇到了一個這樣的問題:
java.io.InvalidClassException:xxxx(具體文件全路徑);local class incompatible:stream classdesc?servialversionUid= XXXX,local calss serialVersionUid=xxxx。具體如下圖:
嘛意思呢?
其實就是說,本地xx類流描述的序列化值是XXXX,但是在編譯運行后值是xxx的問題。導(dǎo)致反序列化失敗。
這種問題,說真的,想排查問題原因何在不好找,想要解決問題容易。找到對應(yīng)的類,里面把serialVersionUid的值寫成提示的值就可以。其實也沒有怎么修改東西,就在類上實現(xiàn)了序列化接口,為什么會出現(xiàn)這種情況呢?而且已經(jīng)編譯過的類怎么查看其序列化值呢?
經(jīng)過搜索得到解決方法。如下:
一:怎么查看已經(jīng)編譯過類的序列化值?
使用的是開發(fā)工具是idea,版本管理工具是git.
切換到出問題的分支上(非必須),檢查代碼之后,在idea的導(dǎo)航欄中Build--Build Project(不同版本之間名稱或許不一樣)??旖萱I:ctrl+F9
將項目編譯完成之后,找到已編譯文件所在目錄。并在cmd中到對應(yīng)目錄中。這里查找文件使用一個神器:everything.搜索電腦上東西很快的,而且軟件也很小。不到2M.
如果文件名稱有重復(fù)的,可以按照時間倒敘,最近查詢到修改的??焖俣ㄎ坏轿募谀夸?。
切換到對應(yīng)目錄之后,進入到class文件所在的包的頂級目錄所在的目錄。也就是項目的target的classes目錄下。然后執(zhí)行serialver?文件的完全包路徑名稱。如:serialver com.kaigejava.kgseed.model.Person
運行如下:
就可以看到Person類的序列化值為-1.這個是顯示寫的。這個是顯示的序列化值。也就是直接在類中寫出來的。
我們在來看看,不顯示寫的結(jié)果是什么:
類中沒有寫serialVersionUID的值。我們在運行上面命令,查看值:
發(fā)現(xiàn)值變化了。
二:Java中實現(xiàn)了serializable接口,默認(rèn)值怎么算出來的?
有時候,類實現(xiàn)了serializable接口之后,沒有顯示的給出serialVersionUID。這種情況下,編譯后的文件中uid值是怎么算出來的?
我們來看看JDK幫助文檔關(guān)于Serializable接口有如下關(guān)于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,則序列化運行時將根據(jù)該類的各個方面,為該類計算默認(rèn)的serialVersionUID值,如Java(TM)對象序列化規(guī)范中所述。但是,強烈建議所有可序列化的類顯式聲明serialVersionUID值,因為默認(rèn)的serialVersionUID計算對類詳細(xì)信息高度敏感,類詳細(xì)信息可能會因編譯器的實現(xiàn)而有所不同,因此可能在反序列化期間導(dǎo)致意外的InvalidClassExceptions。因此,為了保證不同Java編譯器實現(xiàn)之間的serialVersionUID值一致,可序列化的類必須聲明一個顯式的serialVersionUID值。還強烈建議顯式serialVersionUID聲明在可能的情況下使用private修飾符,因為此類聲明僅適用于立即聲明的類-serialVersionUID字段作為繼承成員沒有用。數(shù)組類無法聲明顯式的serialVersionUID,因此它們始終具有默認(rèn)的計算值,但是對于數(shù)組類,無需匹配serialVersionUID值。
官方給出的:雖然會根據(jù)類計算出默認(rèn)的uid值,但是強烈建議所有的可序列化類都顯示聲明uid的值。
為了驗證是否真如官方說的,序列化運行時候?qū)⒏鶕?jù)該類的各個方面,為該來計算默認(rèn)的UID值。我們做如下實驗:
我們在換成jdk1.7編譯,還是用默認(rèn)的。再看看這個值是不是有變化化:
切換項目將jdk換成1.7:
重新編譯:
使用JDK1.7?和1.8?在類沒有發(fā)生變化的時候,UID值都是一樣的。
驗證默認(rèn)生成的uid和類變化有沒有關(guān)系,我們在類中添加一些東西,來看看是否會影響值變化:
先添加一個@Data這個注解:
在運行,查看uid的值:
我們發(fā)現(xiàn),在添加了注解前和注解后的值發(fā)生了變化。
我們在在類中添加一個string類型的name屬性:
再看運行后結(jié)果:
發(fā)現(xiàn),值又不一樣了。所以,我們可以得出,uid的值變化和類有關(guān)的。所以,官方強烈建議顯示設(shè)置uid的值。
三:序列化和反序列化是什么及為什么需要使用序列化?
序列化:把對象轉(zhuǎn)換為字節(jié)序列的過程被稱為對象的序列化
反序列化:把字節(jié)序列恢復(fù)為對象過程為對象的反序列化
最常見的是,當(dāng)我們通過RPC遠(yuǎn)程調(diào)用的時候。如使用dubbo的時候,必須要求對象實現(xiàn)序列化。
圖片上傳不了,可以去凱哥個人博客網(wǎng)站查看:www.kaigejava.com