<div id="cnblogs_post_body" class="blogpost-body cnblogs-markdown"> <div id="img-content">javascript
<h2 class="rich_media_title" id="activity-name"> 講真,下次打死我也不敢隨便改serialVersionUID了 </h2> <div id="meta_content" class="rich_media_meta_list"> <span class="rich_media_meta rich_media_meta_nickname" id="profileBt"> <a id="js_name"> 碼農沉思錄 </a> <div id="js_profile_qrcode" class="profile_container" style="display:none;"> <div class="profile_inner"> <strong class="profile_nickname">碼農沉思錄</strong> <img class="profile_avatar" id="js_profile_qrcode_img" src="" alt=""> <p class="profile_meta"> <label class="profile_meta_label">微信號</label> <span class="profile_meta_value">code-thinker</span> </p> <p class="profile_meta"> <label class="profile_meta_label">功能介紹</label> <span class="profile_meta_value">筆者爲國內某知名企業不知名碼農,專一Java Web領域多年,有豐富的bug開發經驗。</span> </p> </div> <span class="profile_arrow_wrp" id="js_profile_arrow_wrp"> <i class="profile_arrow arrow_out"></i> <i class="profile_arrow arrow_in"></i> </span> </div> </span> <em id="publish_time" class="rich_media_meta rich_media_meta_text">3天前</em> </div> <div class="rich_media_content " id="js_content"> <p style="font-family: -apple-system-font, BlinkMacSystemFont, 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);text-align: right;" data-mpa-powered-by="yiban.io"><span style="color: rgba(0, 0, 0, 0.65);font-family: 'Monospaced Number', 'Chinese Quote', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Helvetica Neue', Helvetica, Arial, sans-serif;text-align: start;white-space: pre;letter-spacing: 1.4px;font-size: 12px;"></span></p><p style="text-align: center;"><img class="rich_pages " data-ratio="0.666" data-s="300,640" data-src="https://mmbiz.qpic.cn/mmbiz_jpg/XA3sPCPib1l5P34dAsQQPqibib8tyCUCMnwX6RXYICMHYibO8b56dpM8cuHhew6LceId2PJiaEbdsKDZ4Eggzur5UyA/640?wx_fmt=jpeg" data-type="jpeg" data-w="500" style="width: 500px !important; height: auto !important; visibility: visible !important;" _width="500px" src="" crossorigin="anonymous" data-fail="0"></p><section class="code-snippet__fix code-snippet__js"><pre class="code-snippet__js" data-lang="javascript"><code class="hljs"><span class="code-snippet_outer">來源:Java成長路</span></code><code class="hljs less"><span class="code-snippet_outer">連接:<span class="hljs-attribute"><span class="hljs-attribute">https</span></span>:<span class="code-snippet__comment"><span class="hljs-comment"><span class="hljs-comment">//www.jianshu.com/p/a4508a8f2420</span></span></span></span></code></pre></section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">序列化是一種對象持久化的手段。廣泛應用在網絡傳輸、RMI等場景中。類經過實現 java.io.Serializable 接口以啓用其序列化功能。</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">可是,還有一個知識點並未展開介紹,那就是關於serialVersionUID 。這個字段到底有什麼用?若是不設置會怎麼樣?爲何《阿里巴巴Java開發手冊》中有如下規定:</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;"><img class="" data-ratio="0.1527777777777778" data-type="other" data-w="1080" data-src="https://mmbiz.qpic.cn/mmbiz/CvQa8Yf8vq11tWicgbQ0jTiaGle6Dibibo4ZXozI7y0sxrnbthHA5eZ8qRHia8kegttJeOTzWuEAttacty5mBibQHvuw/640?wx_fmt=other" _width="677px" src="https://img2018.cnblogs.com/blog/1112483/201909/1112483-20190909143819446-485477025.png" crossorigin="anonymous" data-fail="0"></section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;"><strong style="color: rgb(191, 54, 12);">背景知識</strong></section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">在展開本文的介紹以前,先來簡單介紹一些和序列化有關的知識,</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;"><strong style="color: rgb(191, 54, 12);">Serializable 和 Externalizable</strong></section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">Java類經過實現 java.io.Serializable 接口以啓用其序列化功能。<strong style="color: rgb(191, 54, 12);">未實現此接口的類將沒法進行序列化或反序列化。</strong>可序列化類的全部子類型自己都是可序列化的。</section><p style="color: rgb(62, 62, 62);font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 16px;white-space: normal;widows: 1;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-align: center;"></p><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">若是讀者看過Serializable的源碼,就會發現,他只是一個空的接口,裏面什麼東西都沒有。<strong style="color: rgb(191, 54, 12);">Serializable接口沒有方法或字段,僅用於標識可序列化的語義。</strong>可是,若是一個類沒有實現這個接口,想要被序列化的話,就會拋出java.io.NotSerializableException異常。</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">它是怎麼保證只有實現了該接口的方法才能進行序列化與反序列化的呢?</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">緣由是在執行序列化的過程當中,會執行到如下代碼:</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;"><img class="" data-ratio="0.5575485799701047" data-type="other" data-w="669" data-src="https://mmbiz.qpic.cn/mmbiz/CvQa8Yf8vq11tWicgbQ0jTiaGle6Dibibo4Zr7iaP8yEwHzfzaicHTBfibGaF3BZbCKiboDanJqzLzuKLxUTeBbcPI8klA/640?wx_fmt=other" _width="669px" src="https://img2018.cnblogs.com/blog/1112483/201909/1112483-20190909145454097-584363508.png
" crossorigin="anonymous" data-fail="0"></section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">在進行序列化操做時,會判斷要被序列化的類是不是Enum、Array和Serializable類型,若是都不是則直接拋出NotSerializableException。</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">Java中還提供了Externalizable接口,也能夠實現它來提供序列化能力。</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">Externalizable繼承自Serializable,該接口中定義了兩個抽象方法:writeExternal()與readExternal()。</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">當使用Externalizable接口來進行序列化與反序列化的時候須要開發人員重寫writeExternal()與readExternal()方法。不然全部變量的值都會變成默認值。</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;"><strong style="color: rgb(191, 54, 12);">transient</strong></section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">transient 關鍵字的做用是控制變量的序列化,在變量聲明前加上該關鍵字,能夠阻止該變量被序列化到文件中,在被反序列化後,transient 變量的值被設爲初始值,如 int 型的是 0,對象型的是 null。</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;"><strong style="color: rgb(191, 54, 12);">自定義序列化策略</strong></section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">在序列化過程當中,若是被序列化的類中定義了writeObject 和 readObject 方法,虛擬機會試圖調用對象類裏的 writeObject 和 readObject 方法,進行用戶自定義的序列化和反序列化。</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">若是沒有這樣的方法,則默認調用是 ObjectOutputStream 的 defaultWriteObject方法以及 ObjectInputStream 的 defaultReadObject 方法。</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">用戶自定義的 writeObject 和 readObject 方法能夠容許用戶控制序列化的過程,好比能夠在序列化的過程當中動態改變序列化的數值。</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">因此,對於一些特殊字段須要定義序列化的策略的時候,能夠考慮使用transient修飾,並本身重寫writeObject 和 readObject 方法,如java.util.ArrayList中就有這樣的實現。</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">以上,就是一些讀者須要掌握和和序列化有關的知識。</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">咱們隨便找幾個Java中實現了序列化接口的類,如String、Integer等,咱們能夠發現一個細節,那就是這些類除了實現了Serializable外,還定義了一個serialVersionUID</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;"><img class="" data-ratio="0.27191679049034173" data-type="other" data-w="673" data-src="https://mmbiz.qpic.cn/mmbiz/CvQa8Yf8vq11tWicgbQ0jTiaGle6Dibibo4Zt3oib2h4ZFQPiavOt0mKtZr6IUdQKJibibt6iawemcP9G0KXXgibaGb2wekA/640?wx_fmt=other" _width="673px" src="https://img2018.cnblogs.com/blog/1112483/201909/1112483-20190909145539342-666537876.png " crossorigin="anonymous" data-fail="0"></section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">那麼,到底什麼是serialVersionUID呢?爲何要設置這樣一個字段呢?</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;"><strong style="color: rgb(191, 54, 12);">什麼是serialVersionUID</strong></section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;"><strong style="color: rgb(191, 54, 12);">序列化是將對象的狀態信息轉換爲可存儲或傳輸的形式的過程。咱們都知道,Java對象是保存在JVM的堆內存中的,也就是說,若是JVM堆不存在了,那麼對象也就跟着消失了。</strong></section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">而序列化提供了一種方案,可讓你在即便JVM停機的狀況下也能把對象保存下來的方案。就像咱們平時用的U盤同樣。把Java對象序列化成可存儲或傳輸的形式(如二進制流),好比保存在文件中。這樣,當再次須要這個對象的時候,從文件中讀取出二進制流,再從二進制流中反序列化出對象。</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;"><strong style="color: rgb(191, 54, 12);">虛擬機是否容許反序列化,不只取決於類路徑和功能代碼是否一致,一個很是重要的一點是兩個類的序列化 ID 是否一致</strong>,這個所謂的序列化ID,就是咱們在代碼中定義的serialVersionUID。</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;"><strong style="color: rgb(191, 54, 12);">若是serialVersionUID變了會怎樣</strong></section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;"><strong style="color: rgb(191, 54, 12);">咱們舉個例子吧,看看若是serialVersionUID被修改了會發生什麼?</strong></section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;"><img class="" data-ratio="0.8105263157894737" data-type="other" data-w="665" data-src="https://mmbiz.qpic.cn/mmbiz/CvQa8Yf8vq11tWicgbQ0jTiaGle6Dibibo4ZpEiasXYkMiaGzbI67iavd5b9BMibFm0xIuEboDJFeTma5tqhTbrSvL312w/640?wx_fmt=other" _width="665px" src="https://img2018.cnblogs.com/blog/1112483/201909/1112483-20190909144318980-1902841039.png" crossorigin="anonymous" data-fail="0"></section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">咱們先執行以上代碼,把一個User1對象寫入到文件中。而後咱們修改一下User1類,把serialVersionUID的值改成2L。</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;"><img class="" data-ratio="0.35993975903614456" data-type="other" data-w="664" data-src="https://mmbiz.qpic.cn/mmbiz/CvQa8Yf8vq11tWicgbQ0jTiaGle6Dibibo4ZLSHT5gBLbM7UCJAdj29azoWxtz6SrrC90Clxj1lMfLWnSlSJ2UMqsA/640?wx_fmt=other" _width="664px" src="https://img2018.cnblogs.com/blog/1112483/201909/1112483-20190909144401535-282792285.png" crossorigin="anonymous" data-fail="0"></section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">而後執行如下代碼,把文件中的對象反序列化出來:</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;"><img class="" data-ratio="0.8026905829596412" data-type="other" data-w="669" data-src="https://mmbiz.qpic.cn/mmbiz/CvQa8Yf8vq11tWicgbQ0jTiaGle6Dibibo4ZzAr71KicemvmQIjh1y3u6y7vQ0xeWaicgT5sjTyS2ZwjJJK9KTFniaaeg/640?wx_fmt=other" _width="669px" src="https://img2018.cnblogs.com/blog/1112483/201909/1112483-20190909144438629-1575673924.png" style="width: 669px !important; height: auto !important; visibility: visible !important;" crossorigin="anonymous" data-fail="0"></section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">執行結果以下:</section><blockquote style="margin: 10px 5px 10px 1em;padding-top: 0px;padding-right: 10px;border-left-width: 2px;border-left-color: rgb(0, 150, 136);color: rgb(119, 119, 119);letter-spacing: 1.4px;white-space: normal;quotes: none;"><section style="line-height: 2em;margin: 1.5em 5px !important;"><span style="font-size: 12px;">java.io.InvalidClassException: com.hollis.User1; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2</span></section></blockquote><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">能夠發現,以上代碼拋出了一個java.io.InvalidClassException,而且指出serialVersionUID不一致。</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">這是由於,在進行反序列化時,JVM會把傳來的字節流中的serialVersionUID與本地相應實體類的serialVersionUID進行比較,若是相同就認爲是一致的,能夠進行反序列化,不然就會出現序列化版本不一致的異常,便是InvalidCastException。</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">這也是《阿里巴巴Java開發手冊》中規定,在兼容性升級中,在修改類的時候,不要修改serialVersionUID的緣由。<strong style="color: rgb(191, 54, 12);">除非是徹底不兼容的兩個版本</strong>。因此,<strong style="color: rgb(191, 54, 12);">serialVersionUID實際上是驗證版本一致性的。</strong></section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">若是讀者感興趣,能夠把各個版本的JDK代碼都拿出來看一下,那些向下兼容的類的serialVersionUID是沒有變化過的。好比String類的serialVersionUID一直都是-6849794470754667710L。</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">可是,做者認爲,這個規範其實還能夠再嚴格一些,那就是規定:</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">若是一個類實現了Serializable接口,就必須手動添加一個private static final long serialVersionUID變量,而且設置初始值。</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;"><strong style="color: rgb(191, 54, 12);">爲何要明肯定一個serialVersionUID</strong></section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;"><strong style="color: rgb(191, 54, 12);">若是咱們沒有在類中明確的定義一個serialVersionUID的話,看看會發生什麼。</strong></section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">嘗試修改上面的demo代碼,先使用如下類定義一個對象,該類中不定義serialVersionUID,將其寫入文件。</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;"><img class="" data-ratio="0.25487256371814093" data-type="other" data-w="667" data-src="https://mmbiz.qpic.cn/mmbiz/CvQa8Yf8vq11tWicgbQ0jTiaGle6Dibibo4ZlrAkn283vqJ4QU7YBgACYpx16oEoC51OZlC9sqo589xq5icO8gE7ibIw/640?wx_fmt=other" _width="667px" src="https://img2018.cnblogs.com/blog/1112483/201909/1112483-20190909144515846-1187075442.png" crossorigin="anonymous" data-fail="0"></section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">而後咱們修改User1類,向其中增長一個屬性。在嘗試將其從文件中讀取出來,並進行反序列化。</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;"><img class="" data-ratio="0.44328358208955226" data-type="other" data-w="670" data-src="https://mmbiz.qpic.cn/mmbiz/CvQa8Yf8vq11tWicgbQ0jTiaGle6Dibibo4Zl1DBo8xNFIcssWT4iam1LQbR95q8R5CyUiaIpN8rAYIeuejOovGiaXBeg/640?wx_fmt=other" _width="670px" src="https://img2018.cnblogs.com/blog/1112483/201909/1112483-20190909144553364-2141493138.png" crossorigin="anonymous" data-fail="0"></section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">執行結果:</section><blockquote style="margin: 10px 5px 10px 1em;padding-top: 0px;padding-right: 10px;border-left-width: 2px;border-left-color: rgb(0, 150, 136);color: rgb(119, 119, 119);letter-spacing: 1.4px;white-space: normal;quotes: none;"><section style="line-height: 2em;margin: 1.5em 5px !important;"><span style="font-size: 12px;">java.io.InvalidClassException: com.hollis.User1; local class incompatible: stream classdesc serialVersionUID = -2986778152837257883, local class serialVersionUID = 7961728318907695402</span></section></blockquote><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">一樣,拋出了InvalidClassException,而且指出兩個serialVersionUID不一樣,分別是-2986778152837257883和7961728318907695402。</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">從這裏能夠看出,系統本身添加了一個serialVersionUID。</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">因此,一旦類實現了Serializable,就建議明確的定義一個serialVersionUID。否則在修改類的時候,就會發生異常。</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">serialVersionUID有兩種顯示的生成方式:</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">一種是默認的1L,好比:</section><blockquote style="margin: 10px 5px 10px 1em;padding-top: 0px;padding-right: 10px;border-left-width: 2px;border-left-color: rgb(0, 150, 136);color: rgb(119, 119, 119);letter-spacing: 1.4px;white-space: normal;quotes: none;"><section style="line-height: 2em;margin: 1.5em 5px !important;"><span style="font-size: 12px;">private static final long serialVersionUID = 1L;</span></section></blockquote><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">另一種是根據類名、接口名、成員方法及屬性等來生成一個64位的哈希字段,好比:</section><blockquote style="margin: 10px 5px 10px 1em;padding-top: 0px;padding-right: 10px;border-left-width: 2px;border-left-color: rgb(0, 150, 136);color: rgb(119, 119, 119);letter-spacing: 1.4px;white-space: normal;quotes: none;"><section style="line-height: 2em;margin: 1.5em 5px !important;"><span style="font-size: 12px;">private static final long serialVersionUID = xxxxL;</span></section></blockquote><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">後面這種方式,能夠藉助IDE生成,後面會介紹。</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;"><strong style="color: rgb(191, 54, 12);">背後原理</strong></section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">知其然,要知其因此然,咱們再來看看源碼,分析一下爲何serialVersionUID改變的時候會拋異常?在沒有明肯定義的狀況下,默認的serialVersionUID是怎麼來的?</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">爲了簡化代碼量,反序列化的調用鏈以下:</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;"><img class="" data-ratio="0.22388059701492538" data-type="other" data-w="670" data-src="https://mmbiz.qpic.cn/mmbiz/CvQa8Yf8vq11tWicgbQ0jTiaGle6Dibibo4Z7eNHz4ER9su66rs1fd6Duj2e0KYXG2gQgZc4X4Q9ZAwWnVekPTsjlw/640?wx_fmt=other" _width="670px" src="https://img2018.cnblogs.com/blog/1112483/201909/1112483-20190909144618305-250158459.png" crossorigin="anonymous" data-fail="0"></section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">在initNonProxy中 ,關鍵代碼以下:</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;"><img class="" data-ratio="1.1997439180537772" data-type="other" data-w="781" data-src="https://mmbiz.qpic.cn/mmbiz/CvQa8Yf8vq11tWicgbQ0jTiaGle6Dibibo4ZXvDSmHMtHSia6rE503uF0SHuAZaDNqWFg0ibicTKzAiaso7OrtCza8CedQ/640?wx_fmt=other" _width="677px" src="https://img2018.cnblogs.com/blog/1112483/201909/1112483-20190909144641184-844308109.png" crossorigin="anonymous" data-fail="0"></section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">在反序列化過程當中,對serialVersionUID作了比較,若是發現不相等,則直接拋出異常。</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">深刻看一下getSerialVersionUID方法:</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;"><img class="" data-ratio="0.46038863976083705" data-type="other" data-w="669" data-src="https://mmbiz.qpic.cn/mmbiz/CvQa8Yf8vq11tWicgbQ0jTiaGle6Dibibo4ZQmsAoOYkoCzKTlpztBf3CJGW1HrAgQIe4eBlG4BPQj8qc4wcEOnfcQ/640?wx_fmt=other" _width="669px" src="https://img2018.cnblogs.com/blog/1112483/201909/1112483-20190909144706675-2135875415.png " crossorigin="anonymous" data-fail="0"></section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">在沒有定義serialVersionUID的時候,會調用computeDefaultSUID 方法,生成一個默認的serialVersionUID。</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">這也就找到了以上兩個問題的根源,實際上是代碼中作了嚴格的校驗,而且在未定義的時候自動生成了一個serialVersionUID。</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;"><strong style="color: rgb(191, 54, 12);">IDEA提示</strong></section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">爲了確保咱們不會忘記定義serialVersionUID,能夠調節一下Intellij IDEA的配置,在實現Serializable接口後,若是沒定義serialVersionUID的話,IDEA(eclipse同樣)會進行提示:</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;"><img class="" data-ratio="0.2564516129032258" data-type="other" data-w="620" data-src="https://mmbiz.qpic.cn/mmbiz/CvQa8Yf8vq11tWicgbQ0jTiaGle6Dibibo4ZLadFb2HFXQFpzsvJHtObH8MNrS4iabKqPXRORG2nTyCW6Eiaw3dIF2Yg/640?wx_fmt=other" _width="620px" src="https://img2018.cnblogs.com/blog/1112483/201909/1112483-20190909144738018-1470948292.png" crossorigin="anonymous" data-fail="0"></section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">而且能夠一鍵生成一個:</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;"><img class="" data-ratio="0.27364864864864863" data-type="other" data-w="592" data-src="https://mmbiz.qpic.cn/mmbiz/CvQa8Yf8vq11tWicgbQ0jTiaGle6Dibibo4Zn0ZnVia7X4obR4GfTuwAIettiavtaR8KIQluZSEOYmFCvgHicwKsSicCMA/640?wx_fmt=other" _width="592px" src="https://img2018.cnblogs.com/blog/1112483/201909/1112483-20190909145146945-2066640931.png " crossorigin="anonymous" data-fail="0"></section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">固然,這個配置並非默認生效的,須要手動到IDEA中設置一下:</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;"><img class="" data-ratio="0.525" data-type="other" data-w="1080" data-src="https://mmbiz.qpic.cn/mmbiz/CvQa8Yf8vq11tWicgbQ0jTiaGle6Dibibo4ZrmkyBBG8c5ZKkajaI6MMGv2wBuvYEOAclKDJ6m70zucXEFD5SuDHEg/640?wx_fmt=other" _width="677px" src="https://img2018.cnblogs.com/blog/1112483/201909/1112483-20190909145216153-1139264835.png " crossorigin="anonymous" data-fail="0"></section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">在圖中標號3的地方(Serializable class without serialVersionUID的配置),打上勾,保存便可。</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;"><strong style="color: rgb(191, 54, 12);">總結</strong></section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">serialVersionUID是用來驗證版本一致性的。因此<strong style="color: rgb(191, 54, 12);">在作兼容性升級的時候,不要改變類中serialVersionUID的值。</strong></section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">特別說明一下,因爲本文標題並不徹底能表達本文的所有內容,這裏再強調一下:serialVersionUID <strong style="color: rgb(191, 54, 12);">既然是驗證版本一致性的,在作版本升級的時候(非兼容性升級),記得要修改這個字段的值哦,這樣能夠避免序列化混亂。</strong></section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">若是一個類實現了Serializable接口,必定要記得定義serialVersionUID,不然會發生異常。能夠在IDE中經過設置,讓他幫忙提示,而且能夠一鍵快速生成一個serialVersionUID。</section><section style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;line-height: 2em;margin: 1.5em 5px !important;">之因此會發生異常,是由於反序列化過程當中作了校驗,而且若是沒有明肯定義的話,會根據類名及屬性等自動生成一個。</section><p style="text-align: center;"><img class="rich_pages " data-ratio="1" data-s="300,640" data-src="https://mmbiz.qpic.cn/mmbiz_jpg/XA3sPCPib1l5P34dAsQQPqibib8tyCUCMnwNjBUzGPmpXb6OVNjVic7fZMGIqXv28Veb3sGu5Opicemia8XwCsRlVQDg/640?wx_fmt=jpeg" data-type="jpeg" data-w="258" style="width: 258px !important; height: auto !important; visibility: visible !important;" _width="258px" src="https://mmbiz.qpic.cn/mmbiz_jpg/XA3sPCPib1l5P34dAsQQPqibib8tyCUCMnwNjBUzGPmpXb6OVNjVic7fZMGIqXv28Veb3sGu5Opicemia8XwCsRlVQDg/640?wx_fmt=jpeg" crossorigin="anonymous" data-fail="0"></p> </div>css
<div class="ct_mpda_wrp" id="js_sponsor_ad_area" style="display: none;"></div>java
<div class="read-more__area" id="js_more_read_area" style="display:none;"> </div> <div class="reward_area tc reward_area_primary" id="js_preview_reward_author" style="display:none;"> </div>
</div>原文地址:https://mp.weixin.qq.com/s/HP4P4IcD8oTEovBUSq54pA</div>微信