首先我們說答案:實體類對象在保存在內存中的,而對于web應用程序而言,很多客戶端會對服務器后臺提交數據請求,如得到某種類型的商品,此時后臺程序會從數據庫中讀取符合條件的記錄,并它們打包成對象的集合,再轉化為JSON,回傳給前端渲染。類似請求會有很多,所以如果有這么多對象常駐內存,服務器的內存是承載不了的,此時web容器會將一個暫時沒有使用的對象保存在硬盤中,這個過程我們就稱之為序列化。等到需要使用時,再把再把保存在硬盤中的對象還原到內存中,這個過程,我們稱之為反序列化。這種策略實質上與Windows中設置虛擬內存的做法是一樣的,我們開玩笑就叫“內存不夠外存湊”。而在Java中實現序列化接口是不需要實現任何方法的,因為JDK在這個接口中沒定義任何方法,實現這個接口的意義只是告訴JDK當前這個類可以進行序列化。至于SerialversionUID,是用于序列化和反序列化過程中進行校驗用的,一般用長整型的數據保存,用于判斷當前類和反序列化后生成對象是否是同一個版本,如果還想不通我們可以舉個例的,在經典的諜戰片都有這么一個情節,某個情報人員從家里出門后,要在門上夾一根頭發,然后出門,回來開門時再觀察這根頭發還在不在,如果不在了,說明家里可能來過不速之客。這個長整型的SerialversionUID與這根頭發的意義是一樣的,頭發很細小,只要門有任何狀態的改變,它就可以感知到,造成頭發的掉落。由于序列化是把一個對象變成字節流,而反序列化則是將字節流程重新組織成一個類的對象,因此一個長整型的數,只有需要反序列的類有細微的變化,反序列后就會造成這個長整型數的改變,如何使用一個比較簡單的int類型,有可能類發生改變,然后反序列化后這個數仍然沒有改變,從而喪失校驗的功能。

下面再開始制式化的解釋與說明
一、什么是序列化和反序列化
把對象轉換為字節序列的過程稱為對象的序列化。
把字節序列恢復為對象的過程稱為對象的反序列化。
對象的序列化主要有兩種用途:
1) 把對象的字節序列永久地保存到硬盤上,通常存放在一個文件中;
2) 在網絡上傳送對象的字節序列。
二、實體類為什么要序列化
客戶端訪問了某個能開啟會話功能的資源, web服務器就會創建一個與該客戶端對應的HttpSession對象,每個HttpSession對象都要站用一定的內存空間。如果在某一時間段內訪問站點的用戶很多,web服務器內存中就會積累大量的HttpSession對象,消耗大量的服務器內存,即使用戶已經離開或者關閉了瀏覽器,web服務器仍要保留與之對應的HttpSession對象,在他們超時之前,一直占用web服務器內存資源。
web服務器通常將那些暫時不活動但未超時的HttpSession對象轉移到文件系統或數據庫中保存,服務器要使用他們時再將他們從文件系統或數據庫中裝載入內存,這種技術稱為Session的持久化。
將HttpSession對象保存到文件系統或數據庫中,需要采用序列化的方式將HttpSession對象中的每個屬性對象保存到文件系統或數據庫中;將HttpSession對象從文件系統或數據庫中裝載如內存時,需要采用反序列化的方式,恢復HttpSession對象中的每個屬性對象。所以存儲在HttpSession對象中的每個屬性對象必須實現Serializable接口。
三、Mybatis為何要求持久層的javabean序列化?
MyBatis使用SerializedCache序列化緩存來實現可讀寫緩存類,并通過序列化和反序列化來保證通過緩存獲取數據時,得到的是一個新的實例。如果配置為只讀緩存,MyBatis就會使用Map來存儲緩存值,這種情況下,從緩存中獲取的對象就是同一個實例。
MyBatis中配置緩存時,緩存元素<cache>有個readOnly屬性,readOnly屬性可以被設置為 true 或 false。只讀緩存將對所有調用者返回同一個實例,因為對象沒有進行序列化,所以速度最快。可寫的緩存將通過序列化來返回一個緩存對象的拷貝。因為對象進行了序列化,會比較慢,但是得到的都是新的對象,線程安全。默認值是 false。即Mybatis的二級緩存默認是可寫的,可寫緩存會使用序列化。
序列化緩存
* 先將對象序列化成2進制,再緩存,好處是將對象壓縮了,省內存
* 壞處是速度慢了(因為對象需要進行序列化)
總結:Mybatis通過序列化得到對象的新實例,保證多線程安全(因為是從緩存中取數據,速度還是比從數據庫獲取要快)。具體說就是對象序列化后存儲到緩存中,從緩存中取數據時是通過反序列化得到新的實例。
四、為什么定義SerializableID
對于JVM來說,要進行持久化的類必須要有一個標記,只有持有這個標記JVM才允許類創建的對象可以通過其IO系統轉換為字節數據,從而實現持久化,而這個標記就是Serializable接口。而在反序列化的過程中則需要使用serialVersionUID來確定由那個類來加載這個對象,所以我們在實現Serializable接口的時候,一般還會要去盡量顯示地定義serialVersionUID.
如果我們在序列化中沒有顯示地聲明serialVersionUID,則序列化運行時將會根據該類的各個方面計算該類默認的serialVersionUID值。但是,Java官方強烈建議所有要序列化的類都顯示地聲明serialVersionUID字段,因為如果高度依賴于JVM默認生成serialVersionUID,可能會導致其與編譯器的實現細節耦合,這樣可能會導致在反序列化的過程中發生意外的InvalidClassException異常。因此,為了保證跨不同Java編譯器實現的serialVersionUID值的一致,實現Serializable接口的必須顯示地聲明serialVersionUID字段。我們前面做了了一個類比 ,某個情報人員從家里出門后,要在門上夾一根頭發,然后出門,回來開門時再觀察這根頭發還在不在,如果不在了,說明家里可能來過不速之客。這個長整型的SerialversionUID與這根頭發的意義是一樣的,也就是說JDK對內存對象反序列化之后,需要利用這個長整型的數與現有的類的這個同名成員進行比對,看是否相同,如果不相同則會以拋異常的方式報警,提示反序列化失敗,用于提示保存在原來序列化時的對象結構與當前類的結構不相同了(有可能是反序列之前類發生改變了)。那為什么需要使用一個長整型的數來作為serialVersionUID,一個普通的int不行嗎,
此外serialVersionUID字段地聲明要盡可能使用private關鍵字修飾,這是因為該字段的聲明只適用于聲明的類,該字段作為成員變量被子類繼承是沒有用處的!有個特殊的地方需要注意的是,數組類是不能顯示地聲明serialVersionUID的,因為它們始終具有默認計算的值,不過數組類反序列化過程中也是放棄了匹配serialVersionUID值的要求。
浙公網安備 33010602011771號