天天看點

Java序列化和反序列化

  我們知道,web應用在實作rememberme操作,即記住使用者登入狀态的是通過向用戶端添加cookie的方法實作的。cookie中往往儲存了使用者的使用者名和密碼,那麼問題來了,隻要黑客在任意我們通路網站的路由上截獲我們的請求資料,我們的使用者名和密碼豈不就洩露了?

  對此,java中常用的解決方法是,不直接向cookie中寫明文的使用者名和密碼,而是将使用者的資訊通過java序列化,把對象資訊序列化為隻有擁有源代碼(源class檔案)的程式才能還原的序列化資訊。

  還有在大學時和老是一起做項目,總發現許多類需要設定一個s​e​r​i​a​l​v​e​r​s​i​o​n​u​i​d,當時也不知道這是什麼,糊了巴嘟加上了,不報錯了,就沒再探究為什麼要加,今天正好來解決兩三年前的疑問。

把對象轉換為位元組序列的過程稱為對象的序列化。把位元組序列恢複為對象的過程稱為對象的反序列化。

  對象的序列化主要有兩種用途:

  1、把對象的位元組序列永久地儲存到硬碟上,通常存放在一個檔案中;

  2、在網絡上傳送對象的位元組序列。

  在很多應用中,需要對某些對象進行序列化,讓它們離開記憶體空間,入住實體硬碟,以便長期儲存。比如最常見的是web伺服器中的session對象,當有 10萬使用者并發通路,就有可能出現10萬個session對象,記憶體可能吃不消,于是web容器就會把一些seesion先序列化到硬碟中,等要用了,再把儲存在硬碟中的對象還原到記憶體中。

  當兩個程序在進行遠端通信時,彼此可以發送各種類型的資料。無論是何種類型的資料,都會以二進制序列的形式在網絡上傳送。發送方需要把這個java對象轉換為位元組序列,才能在網絡上傳送;接收方則需要把位元組序列再恢複為java對象。

  java.io.objectoutputstream代表對象輸出流,它的writeobject(object obj)方法可對參數指定的obj對象進行序列化,把得到的位元組序列寫到一個目标輸出流中。

  java.io.objectinputstream代表對象輸入流,它的readobject()方法從一個源輸入流中讀取位元組序列,再把它們反序列化為一個對象,并将其傳回。

  隻有實作了serializable和externalizable接口的類的對象才能被序列化。externalizable接口繼承自 serializable接口,實作externalizable接口的類完全由自身來控制序列化的行為,而僅實作serializable接口的類可以 采用預設的序列化方式 。

  對象序列化包括如下步驟:

  1、建立一個對象輸出流,它可以包裝一個其他類型的目标輸出流,如檔案輸出流;

  2、通過對象輸出流的writeobject()方法寫對象。

  對象反序列化的步驟如下:

  1、建立一個對象輸入流,它可以包裝一個其他類型的源輸入流,如檔案輸入流;

  2、通過對象輸入流的readobject()方法讀取對象。

  s​e​r​i​a​l​v​e​r​s​i​o​n​u​i​d​​意​思​​是​序​列​化​的​版​本​号​,凡是實作serializable接口的類都有一個表示序列化版本辨別符的靜态變量。檔案流中的class和classpath中的class,在序列化後進行了修改,由于内部安全機制考慮,這時再進行反序列化會失敗。那麼如果我們真的有需求要在序列化後添加一個字段或者方法,隻要去指定serialversionuid就行。如果沒有給要序列化的類設定serialversionuid,那麼java編譯器會自動給這個class進行一個摘要算法,類似于指紋算法,隻要這個檔案多一個空格,得到的uid就會截然不同的,可以保證在這麼多類中,這個編号是唯一的。是以,添加了一個字段後,由于沒有顯指定 serialversionuid,編譯器又為我們生成了一個uid,當然和前面儲存在檔案中的那個不會一樣了,于是就出現了2個序列化版本号不一緻的錯誤。是以,隻要我們自己指定了serialversionuid,就可以在序列化後,去添加一個字段,或者方法,而不會影響到後期的還原,還原後的對象照樣可以使用,而且還多了方法或者屬性可以用。  

  大概意思就是說,serialversionuid的取值是java運作時環境根據類的内部細節自動生成的。如果對類的源代碼作了修改,再重新編譯,新生成的類檔案的serialversionuid的取值有可能也會發生變化。類的serialversionuid的預設值完全依賴于java編譯器的實作,對于同一個類,用不同的java編譯器編譯,有可能會導緻不同的 serialversionuid,也有可能相同。為了提高serialversionuid的獨立性和确定性,強烈建議在一個可序列化類中顯示的定義serialversionuid,為它賦予明确的值。

  顯式地定義serialversionuid有兩種用途:

  1、在某些場合,希望類的不同版本對序列化相容,是以需要確定類的不同版本具有相同的serialversionuid;

  2、在某些場合,不希望類的不同版本對序列化相容,是以需要確定類的不同版本具有不同的serialversionuid。