Serializable是Java提供的序列化接口,是一個空接口,為對象提供標准的序列化與反序列化操作。使用Serializable實現序列化過程相當簡單,只需要在類聲明的時候指定一個標識,便可以自動的實現默認的序列化過程。
private static final long serialVersionUID = 1L;
上面已經說明讓對象實現序列化,只需要讓當前類實現Serializable接口,並且聲明一個serialVersionUID就可以了,非常的簡單方便。實際上serialVersionUID都不是必須的,沒有它同樣可以正常的實現序列化操作。
User類就是一個實現了Serialzable的類,它是可以被序列化和反序列化的。
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String userId;
private String userName;
}
通過Serializable實現對象的序列化過程非常的簡單,無需任何操作,系統就為我們自動實現了。如何進行對象的序列化與反序列化操作也是非常的簡單,只需要通過ObjectOutputStream,ObjectInputStream進行操作就可以了。
//序列化過程
public void toSerial() {
try {
User user = new User("id", "user");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("user.txt"));
objectOutputStream.writeObject(user);
objectOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//反序列化過程
public void fromSerial(){
try {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("user.txt"));
User user = (User) objectInputStream.readObject();
objectInputStream.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
是的,你沒有看錯,序列化與反序列化操作過程就是這么的簡單。只需要將User寫入到文件中,然后再從文件中進行恢復,恢復后得到的內容與之前完全一樣,但是兩者是不同的對象。當然了前面提到過一個問題,如果將serialVersionUID去掉會產生什么樣的影響呢?
剛開始提到了,serialVersionUID要不要指定呢?如果不指定會出現什么樣的后果?如果指定了以后后邊的值又代表着什么意思呢?同時我們也要明白,既然系統指定了這個字段,那么肯定是有它的作用的。
這個serialVersionUID是用來輔助對象的序列化與反序列化的,原則上序列化后的數據當中的serialVersionUID與當前類當中的serialVersionUID一致,那么該對象才能被反序列化成功。這個serialVersionUID的詳細的工作機制是:在序列化的時候系統將serialVersionUID寫入到序列化的文件中去,當反序列化的時候系統會先去檢測文件中的serialVersionUID是否跟當前的文件的serialVersionUID是否一致,如果一致則反序列化成功,否則就說明當前類跟序列化后的類發生了變化,比如是成員變量的數量或者是類型發生了變化,那么在反序列化時就會發生crash,並且回報出錯誤:
我這個操作的流程是:首先在手機sd卡的根目錄創建一個文件user.txt,在創建的時候做出判斷,如果user.txt不存在,則進行創建文件操作並進行序列化寫入操作。如果user.txt存在那么我們就直接讀取並進行反序列化操作。我在創建完成后,在User里面多加了一個屬性,然后在進行反序列化操作的時候就報出了這樣的錯誤,當然前提是我把類中聲明的serialVersionUID去掉了。
當我把serialVersionUID加上的時候,我在創建完user.txt后我在進行序列化讀取,成功的拿到了之前存進去的數據。這樣的一個操作過程就讓我們很清楚的看出來了serialVersionUID在序列化與反序列化過程當中的作用。
這個字段的作用顯而易見,加入我們升級了系統,在User當中增加了字段屬性,那么我們在手動添加了serialVersionUID之后,我們在進行序列化和反序列化的時候就會很從容,就不會再出現crash的情況了。
另外強調兩點,靜態成員變量屬於類不屬於對象所以不參與序列化的過程,還有如果使用transient標記的成員變量不參與序列化過程。
總結(重點思考)
工作中我們經常在進行持久化操作和返回數據時都會使用到javabean來統一封裝參數,方便操作,一般我們也都會實現Serializable接口,那么問題來了,首先:為什么要進行序列化;其次:每個實體bean都必須實現serializabel接口嗎?最后:我做一些項目的時候,沒有實現序列化,同樣沒什么影響,到底什么時候應該進行序列化操作呢?
網上找了很多資料,但是感覺大都沒有說的很清楚,所以結合自己的理解做一下總結。
首先第一個問題,實現序列化的兩個原因:1、將對象的狀態保存在存儲媒體中以便可以在以后重新創建出完全相同的副本;2、按值將對象從一個應用程序域發送至另一個應用程序域。實現serializabel接口的作用是就是可以把對象存到字節流,然后可以恢復,所以你想如果你的對象沒實現序列化怎么才能進行持久化和網絡傳輸呢,要持久化和網絡傳輸就得轉為字節流,所以在分布式應用中及設計數據持久化的場景中,你就得實現序列化。
第二個問題,是不是每個實體bean都要實現序列化,答案其實還要回歸到第一個問題,那就是你的bean是否需要持久化存儲媒體中以及是否需要傳輸給另一個應用,沒有的話就不需要,例如我們利用fastjson將實體類轉化成json字符串時,並不涉及到轉化為字節流,所以其實跟序列化沒有關系。
第三個問題,有的時候並沒有實現序列化,依然可以持久化到數據庫。這個其實我們可以看看實體類中常用的數據類型,例如Date、String等等,它們已經實現了序列化,而一些基本類型,數據庫里面有與之對應的數據結構,從我們的類聲明來看,我們沒有實現serializabel接口,其實是在聲明的各個不同變量的時候,由具體的數據類型幫助我們實現了序列化操作。
另外需要注意的是,在NoSql數據庫中,並沒有與我們java基本類型對應的數據結構,所以在往nosql數據庫中存儲時,我們就必須將對象進行序列化,同時在網絡傳輸中我們要注意到兩個應用中javabean的serialVersionUID要保持一致,不然就不能正常的進行反序列化。
下面是看到的別人整理的一個業務場景,感覺描述的挺不錯;整理到這里,供大家借鑒理解
今天在使用eclipse開發的時候,遇到一個warning,看到warning我總覺得不爽,使用自動修復后,發現eclipse在代碼中加入了“private static final long serialVersionUID = 1L;”。其實之前就遇過這種情況了,只是沒有去了解,於是今天我就查了一下serialVersionUID 這個變量的信息。
原來serialVersionUID是可序列化類的一個版本標識,在反序列化的時候使用這個標識的值來判斷序列化和反序列化的對象類型是否一致。Java的序列化機制是通過在運行時判斷類的serialVersionUID來驗證版本一致性的。在進行反序列化時,JVM會把傳來的字節流中的serialVersionUID與本地相應實體(類)的serialVersionUID進行比較,如果相同就認為是一致的,可以進行反序列化,否則就會出現序列化版本不一致的異常(InvalidClassException)。當你一個類實現了Serializable接口,如果沒有定義serialVersionUID,Eclipse會提供這個提示功能告訴你去定義之。 Eclipse中有兩種生成方式:
一個是默認的1L,比如:“private static final long serialVersionUID = 1L;";
一個是根據類名、接口名、成員方法及屬性等來生成一個64位的哈希字段,比如:“private static final long serialVersionUID = -8940196742313994740L;”。
使用serialVersionUID要注意以下幾點:
1.當實現java.io.Serializable接口的實體(類)沒有顯式地定義一個名為serialVersionUID,類型為long的變量時,Java序列化機制會根據編譯的class自動生成一個serialVersionUID作序列化版本比較用,這種情況下,只有同一次編譯生成的class才會生成相同的serialVersionUID 。如果我們不希望通過編譯來強制划分軟件版本,即實現序列化接口的實體能夠兼容先前版本中未作更改的類,就需要顯式地定義一個名為serialVersionUID,類型為long的變量,不修改這個變量值的序列化實體都可以相互進行串行化和反串行化。
2.記住應該總是在可序列化的類中包含這個字段,即使是在第一個類版本中,以便提醒自己這個字段的重要性。不要在未來的版本中改變這個字段值,除非你有意要改變類使其與舊的序列化對象不兼容。
3.如果你的類序列化到硬盤上面后,你更改了類別的field(增加或減少或改名),當你反序列化時,就會出現異常的,這樣就會造成不兼容性的問題。但當serialVersionUID相同時,它就會將不一樣的field以type的預設值Deserialize,這個可以避開不兼容性的問題。
4.當我們的系統不太經常需要序列化類時,可以去掉這些警告,做如下設置:Window-->Preferences-->Java,將serializable class without serialVersionUID的設置由warning改為Ignore。然后Eclipse會重新編譯程序,那些警告信息也就消失了。但如果在開發大量需要序列化的類的時候,建議還原為原來的設置。這樣可以保證系統的性能和健壯。
參考文章: