溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

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

發(fā)布時(shí)間:2020-08-12 09:52:19 來源:網(wǎng)絡(luò) 閱讀:334 作者:凱哥Java 欄目:編程語(yǔ)言

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

本文主要內(nèi)容:

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

2:實(shí)現(xiàn)了Serializable接口的對(duì)象如果不顯示的給出序列化值,默認(rèn)值怎么算出來的

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

來源:凱哥Java(kaigejava)

凱哥個(gè)人博客:www.kaigejava.com

昨天快下班的時(shí)候遇到了一個(gè)這樣的問題:

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

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

嘛意思呢?

其實(shí)就是說,本地xx類流描述的序列化值是XXXX,但是在編譯運(yùn)行后值是xxx的問題。導(dǎo)致反序列化失敗。

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

經(jīng)過搜索得到解決方法。如下:

一:怎么查看已經(jīng)編譯過類的序列化值?

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

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

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

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

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

如果文件名稱有重復(fù)的,可以按照時(shí)間倒敘,最近查詢到修改的??焖?/span>定位到文件所在目錄。

切換到對(duì)應(yīng)目錄之后,進(jìn)入到class文件所在的包的頂級(jí)目錄所在的目錄。也就是項(xiàng)目的targetclasses目錄下。然后執(zhí)行serialver?文件的完全包路徑名稱。如:serialver com.kaigejava.kgseed.model.Person

運(yùn)行如下:

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

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

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

我們?cè)趤砜纯?/span>,不顯示寫的結(jié)果是什么:

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

類中沒有寫serialVersionUID的值。我們?cè)谶\(yùn)行上面命令,查看值:

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

發(fā)現(xiàn)值變化了。



二:Java中實(shí)現(xiàn)了serializable接口,默認(rèn)值怎么算出來的?

有時(shí)候,類實(shí)現(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.


重點(diǎn)如下圖:

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

uid的長(zhǎng)度是是一個(gè)長(zhǎng)度42long類型的。

最后一段話:

如果可序列化的類未明確聲明serialVersionUID,則序列化運(yùn)行時(shí)將根據(jù)該類的各個(gè)方面,為該類計(jì)算默認(rèn)的serialVersionUID值,如Java(TM)對(duì)象序列化規(guī)范中所述。但是,強(qiáng)烈建議所有可序列化的類顯式聲明serialVersionUID,因?yàn)槟J(rèn)的serialVersionUID計(jì)算對(duì)類詳細(xì)信息高度敏感,類詳細(xì)信息可能會(huì)因編譯器的實(shí)現(xiàn)而有所不同,因此可能在反序列化期間導(dǎo)致意外的InvalidClassExceptions。因此,為了保證不同Java編譯器實(shí)現(xiàn)之間的serialVersionUID值一致,可序列化的類必須聲明一個(gè)顯式的serialVersionUID值。還強(qiáng)烈建議顯式serialVersionUID聲明在可能的情況下使用private修飾符,因?yàn)榇祟惵暶鲀H適用于立即聲明的類-serialVersionUID字段作為繼承成員沒有用。數(shù)組類無法聲明顯式的serialVersionUID,因此它們始終具有默認(rèn)的計(jì)算值,但是對(duì)于數(shù)組類,無需匹配serialVersionUID值。

方給出的:雖然會(huì)根據(jù)類計(jì)算出默認(rèn)的uid值,但是強(qiáng)烈建議所有的可序列化類都顯示聲明uid的值。

為了驗(yàn)證是否真如官方說的,序列化運(yùn)行時(shí)候?qū)⒏鶕?jù)該類的各個(gè)面,為該來計(jì)算默認(rèn)的UID值。我們做如下實(shí)驗(yàn):

我們?cè)趽Q成jdk1.7編譯,還是用默認(rèn)的。再看看這個(gè)值是不是有變化化:

切換項(xiàng)目將jdk換成1.7

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

重新編譯:

使用JDK1.7?1.8?在類沒有發(fā)生變化的時(shí)候,UID值都是一樣的。

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

驗(yàn)證默認(rèn)生成的uid和類變化有沒有關(guān)系,我們?cè)陬愔刑砑右恍〇|西,來看看是否會(huì)影響值變化:

先添加一個(gè)@Data這個(gè)注解:

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

在運(yùn)行,查看uid的值:

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

我們發(fā)現(xiàn),在添加了注解前和注解后的值發(fā)生了變化。

我們?cè)谠陬愔刑砑右粋€(gè)string類型的name屬性:

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

再看運(yùn)行后結(jié)果:

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

發(fā)現(xiàn),值又不一樣了。所以,我們可以得出,uid的值變化和類有關(guān)的。所以,官方強(qiáng)烈建議顯示設(shè)置uid的值。


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

序列化:把對(duì)象轉(zhuǎn)換為字節(jié)序列的過程被稱為對(duì)象的序列化

反序列化:把字節(jié)序列恢復(fù)為對(duì)象過程為對(duì)象的反序列化

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

最常見的是,當(dāng)我們通過RPC遠(yuǎn)程調(diào)用的時(shí)候。如使用dubbo的時(shí)候,必須要求對(duì)象實(shí)現(xiàn)序列化。

圖片上傳不了,可以去凱哥個(gè)人博客網(wǎng)站查看:www.kaigejava.com



向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI