Java內(nèi)置接口Serializable示例詳解_第1頁
Java內(nèi)置接口Serializable示例詳解_第2頁
Java內(nèi)置接口Serializable示例詳解_第3頁
Java內(nèi)置接口Serializable示例詳解_第4頁
Java內(nèi)置接口Serializable示例詳解_第5頁
已閱讀5頁,還剩5頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

第Java內(nèi)置接口Serializable示例詳解目錄引言Serializable接口Serializable是一個(gè)標(biāo)記型接口serializableVersionUIDJava序列化與JSON序列化的區(qū)別Java序列化相較于JSON的優(yōu)勢(shì)Java類對(duì)象的序列化代碼演示總結(jié)

引言

上一部分我們著重講了Java集合框架中在開發(fā)項(xiàng)目時(shí)經(jīng)常會(huì)被用到的數(shù)據(jù)容器,在講解、演示使用實(shí)踐的同時(shí),把這個(gè)過程中遇到的各種相關(guān)知識(shí)點(diǎn):泛型、Lambada、Stream操作,一并給大家做了梳理。

從這篇開始我們進(jìn)入下一部分,用三到五部分給大家梳理一下,在用Java編程時(shí),那些我們繞不開的interface;從最基本的Serializable到Comparable和Iterator這些,再到Java為了支持函數(shù)式編程而提供的Function、Predicate等interface。

這些Java內(nèi)置提供的interface或多或少我們?cè)趯慗ava代碼的時(shí)候都見過,有的甚至是潛移默化地在日常編碼中已經(jīng)實(shí)現(xiàn)過其中的一些interface,只不過我們沒有察覺到罷了。相信通過閱讀著幾篇文章,一定會(huì)讓你在寫Java代碼時(shí)更清楚自己是在做什么,不會(huì)再被這些個(gè)似曾相識(shí)的interface困擾到。

本文大綱如下:

Serializable接口

作為Java中那些繞不開的內(nèi)置接口這個(gè)小系列的開篇文章,首先要給大家介紹的interface是Serializable。

Serializable這個(gè)接口的全限定名(包名+接口名)是java.io.Serializable,這里給大家說個(gè)小技巧,當(dāng)你看到一個(gè)類或者接口的包名前綴里包含java.io那就證明這個(gè)類/接口它跟數(shù)據(jù)的傳輸有關(guān)。

Serializable是Java中非常重要的一個(gè)接口,如果一個(gè)類的對(duì)象是可序列化的,即對(duì)象在程序里可以進(jìn)行序列化和反序列化,對(duì)象的類就一定要實(shí)現(xiàn)Serializable接口。那么為什么要進(jìn)行序列化和反序列化呢?

序列化的意思是將對(duì)象的狀態(tài)轉(zhuǎn)換為字節(jié)流;反序列化則相反。換句話說,序列化是將Java對(duì)象轉(zhuǎn)換為靜態(tài)字節(jié)流(序列),然后我們可以將其保存到文件、數(shù)據(jù)庫(kù)或者是通過通過網(wǎng)絡(luò)傳輸,反序列化則是在我們讀取到字節(jié)流后再轉(zhuǎn)換成Java對(duì)象的過程;這也正好解釋了為什么Serializable接口會(huì)歸屬到j(luò)ava.io包下面。

Serializable是一個(gè)標(biāo)記型接口

雖說需要進(jìn)行序列化的對(duì)象,它們的類都需要實(shí)現(xiàn)Serializable接口,但其實(shí)你會(huì)發(fā)現(xiàn),我們?cè)谧屢粋€(gè)類實(shí)現(xiàn)Serializable接口時(shí),并沒有額外實(shí)現(xiàn)過什么抽線方法。

importjava.io.Serializable;

publicclassPersonimplementsSerializable{

privateStringname;

privateintage;

比如向上面?zhèn)€類文件里的內(nèi)容,Person類聲明實(shí)現(xiàn)Serializable接口后,并沒有去實(shí)現(xiàn)什么抽象方法,IDE也不會(huì)用紅線警告提示我們:你有一個(gè)抽象方法需要實(shí)現(xiàn),原因是Serializable接口里并沒有聲明抽象方法。

publicinterfaceSerializable{

這種不包含任何方法的interface被稱為標(biāo)記型接口,類實(shí)現(xiàn)Serializable接口不必實(shí)現(xiàn)任何特定方法,它只起標(biāo)記作用,讓Java知道該類可以用于對(duì)象序列化。

serializableVersionUID

雖說一個(gè)類實(shí)現(xiàn)了Serializable接口的時(shí)候不需要實(shí)現(xiàn)特定的方法,但是經(jīng)常會(huì)看到一些實(shí)現(xiàn)了Serializable的類中,都有一個(gè)名為serialVersionUID類型為long的私有靜態(tài)屬性。

importjava.io.Serializable;

publicstaticclassPersonimplementsSerializable{

privatestaticfinallongserialVersionUID=-7792628363939354385L;

publicStringname;

publicintage;

該屬性修飾符里使用了final即賦值后不可更改。Java的對(duì)象序列化API在從讀取到的字節(jié)序列中反序列化出對(duì)象時(shí),使用serialVersionUID這個(gè)靜態(tài)類屬性來判斷:是否序列化對(duì)象時(shí)使用了當(dāng)前相同版本的類進(jìn)行的序列化。Java使用它來驗(yàn)證保存和加載的對(duì)象是否具有相同的屬性,確保在序列化上是兼容的。

大多數(shù)的IDE都可以自動(dòng)生成這個(gè)serialVersionUID靜態(tài)屬性的值,規(guī)則是基于類名、屬性和相關(guān)的訪問修飾符。任何更改都會(huì)導(dǎo)致不同的數(shù)字,并可能導(dǎo)致InvalidClassException。如果一個(gè)實(shí)現(xiàn)Serializable的類沒有聲明serialVersionUID,JVM會(huì)在運(yùn)行時(shí)自動(dòng)生成一個(gè)。但是,強(qiáng)烈建議每個(gè)可序列化類都聲明serialVersionUID,因?yàn)槟J(rèn)生成的serialVersionUID依賴于編譯器,因此可能會(huì)導(dǎo)致意外的InvalidClassExceptions。

我上面那個(gè)例子里,Person類的serialVersionUID是用IntelijIDEA自動(dòng)生成的,所以值看起來一大串,不是我自己些的。IDEA默認(rèn)不會(huì)給可序列化類自動(dòng)生成serialVersionUID需要安裝一個(gè)插件。

這里給大家放一個(gè)截圖,插件的安裝和使用,網(wǎng)上有很多例子,大家需要的話動(dòng)手搜一下,這里就不再占用太多篇幅講怎么安裝和使用這個(gè)插件了。

Java序列化與JSON序列化的區(qū)別

Java的序列化與現(xiàn)在互聯(lián)網(wǎng)上Web應(yīng)用交互數(shù)據(jù)常用的JSON序列化并不是一回事兒,這是咱們需要注意的,像Java、C#、PHP這些編程語言,都有自己的序列化機(jī)制把自家的對(duì)象序列化成字節(jié)然后進(jìn)行傳輸或者保存,但是這些語言的序列化機(jī)制之間并不能互認(rèn),即用Java把對(duì)象序列化成字節(jié)、通過網(wǎng)絡(luò)RESTfulAPI傳給一個(gè)PHP開發(fā)的服務(wù),PHP是沒辦法反序列化還原出這個(gè)對(duì)象的。這樣才有了JSON、XML、ProtocolBuffer這樣的更通用的序列化標(biāo)準(zhǔn)。

例如在實(shí)際項(xiàng)目開發(fā)的時(shí)候,Java對(duì)象往往被序列化為JSON、XML后再在網(wǎng)絡(luò)上傳輸,如果對(duì)數(shù)據(jù)大小敏感的場(chǎng)景,會(huì)把Java對(duì)象序列化成空間占用更小的一些二進(jìn)制格式,比如ProtocolBuffer(分布式RPC框架gRPC的數(shù)據(jù)交換格式)。這樣做的好處是序列化后的數(shù)據(jù)可以被非Java應(yīng)用程序讀取和反序列化,例如,在Web瀏覽器中運(yùn)行的JavaScript可以在本地將對(duì)象序列化成JSON傳輸給Java寫的API接口,也可以從JavaAPI接口返回響應(yīng)中的JSON數(shù)據(jù),反序列化成JavaScript本地的對(duì)象。

像上面列舉的這些對(duì)象序列化機(jī)制,是不需要我們的Java類實(shí)現(xiàn)Serializable接口的。這些JSON、XML等格式的序列化類,通常使用Java反射來檢查類,配合一些特定的注解完成序列化。

Java序列化相較于JSON的優(yōu)勢(shì)

上面介紹了JSON這樣的通用序列化格式的優(yōu)勢(shì),有的可能會(huì)問了,那還用Java序列化干啥。這里再給大家分析一下,Java對(duì)象序列化雖然在通用性上不如JSON那些序列化格式,但是在Java生態(tài)內(nèi)部卻是十分好用的,其最聰明的一點(diǎn)是,它不僅能保存對(duì)象的副本,而且還會(huì)跟著對(duì)象里面的reference,把它所引用的對(duì)象也保存起來,然后再繼續(xù)跟蹤那些對(duì)象的reference,以此類推。

這個(gè)機(jī)制所涵蓋的范圍不僅包括對(duì)象的成員數(shù)據(jù),而且還包含數(shù)組里面的reference。如果你要自己實(shí)現(xiàn)對(duì)象序列化的話,那么編寫跟蹤這些鏈接的程序?qū)?huì)是一件非常痛苦的任務(wù)。但是,Java的對(duì)象序列化就能精確無誤地做到這一點(diǎn),毫無疑問,它的遍歷算法是做過優(yōu)化的。

另外你們?cè)谝恍┵Y料里看過JavaBean的定義

1、所有屬性為private

2、提供默認(rèn)構(gòu)造方法

3、提供getter和setter

4、實(shí)現(xiàn)java.io.Serializable接口

那么問題來了,為什么要進(jìn)行序列化?每個(gè)實(shí)體bean都必須實(shí)現(xiàn)serializabel接口嗎?以及我做項(xiàng)目的時(shí)候,沒有實(shí)現(xiàn)序列化,同樣沒什么影響,到底什么時(shí)候應(yīng)該進(jìn)行序列化操作呢?

這里轉(zhuǎn)載一個(gè)網(wǎng)上大佬對(duì)這個(gè)問題的解釋

首先第一個(gè)問題,實(shí)現(xiàn)序列化的兩個(gè)原因:

1、將對(duì)象的狀態(tài)保存在存儲(chǔ)媒體中以便可以在以后重新創(chuàng)建出完全相同的副本;

2、按值將對(duì)象從一個(gè)應(yīng)用程序域發(fā)送至另一個(gè)應(yīng)用程序域。實(shí)現(xiàn)serializabel接口的作用是就是可以把對(duì)象存到字節(jié)流,然后可以恢復(fù),所以你想如果你的對(duì)象沒實(shí)現(xiàn)序列化怎么才能進(jìn)行持久化和網(wǎng)絡(luò)傳輸呢,要持久化和網(wǎng)絡(luò)傳輸就得轉(zhuǎn)為字節(jié)流,所以在分布式應(yīng)用中及設(shè)計(jì)數(shù)據(jù)持久化的場(chǎng)景中,你就得實(shí)現(xiàn)序列化。

第二個(gè)問題,是不是每個(gè)實(shí)體bean都要實(shí)現(xiàn)序列化,答案其實(shí)還要回歸到第一個(gè)問題,那就是你的bean是否需要持久化存儲(chǔ)媒體中以及是否需要傳輸給另一個(gè)應(yīng)用,沒有的話就不需要,例如我們利用fastjson將實(shí)體類轉(zhuǎn)化成json字符串時(shí),并不涉及到轉(zhuǎn)化為字節(jié)流,所以其實(shí)跟序列化沒有關(guān)系。

第三個(gè)問題,有的時(shí)候并沒有實(shí)現(xiàn)序列化,依然可以持久化到數(shù)據(jù)庫(kù)。這個(gè)其實(shí)我們可以看看實(shí)體類中常用的數(shù)據(jù)類型,例如Date、String等等,它們已經(jīng)實(shí)現(xiàn)了序列化,而一些基本類型,數(shù)據(jù)庫(kù)里面有與之對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu),從我們的類聲明來看,我們沒有實(shí)現(xiàn)serializabel接口,其實(shí)是在聲明的各個(gè)不同變量的時(shí)候,由具體的數(shù)據(jù)類型幫助我們實(shí)現(xiàn)了序列化操作。

另外需要注意的是,在NoSql數(shù)據(jù)庫(kù)中,并沒有與我們Java基本類型對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu),所以在往nosql數(shù)據(jù)庫(kù)中存儲(chǔ)時(shí),我們就必須將對(duì)象進(jìn)行序列化,同時(shí)在網(wǎng)絡(luò)傳輸中我們要注意到兩個(gè)應(yīng)用中javabean的serialVersionUID要保持一致,不然就不能正常的進(jìn)行反序列化。

Java類對(duì)象的序列化代碼演示

到這里Serializable需要了解的基礎(chǔ)知識(shí)就都給大家梳理出來了,這塊屬于選讀,用Java編程寫序列化代碼的場(chǎng)景并不是太多,不過有興趣就再接著往下看吧,有個(gè)印象,這樣以后寫代碼的時(shí)候,哪天用上了,還能快速想起來在哪看過,再回來翻看。

Java對(duì)象序列化(寫入)由ObjectOutputStream完成,反序列化(讀?。┯蒓bjectInputStream完成。ObjectInputStream和ObjectOutputStream是分別繼承了java.io.InputStream和java.io.OutputStream抽象的實(shí)體類。ObjectOutputStream可以將對(duì)象的原型作為字節(jié)流寫入OutputStream。然后我們可以使用ObjectInputStream讀取這些流。ObjectOutputStream中最重要的方法是:

publicfinalvoidwriteObject(Objecto)throwsIOException;

這個(gè)方法接收一個(gè)可序列化對(duì)象(實(shí)現(xiàn)了Serializable接口的類的對(duì)象)并將其轉(zhuǎn)換為字節(jié)序列。同樣,在ObjectInputStream中最重要的方法是:

publicfinalObjectreadObject()throwsIOException,ClassNotFoundException;

此方法可以讀取字節(jié)流并將其轉(zhuǎn)換回Java對(duì)象。然后我們可以再使用類型轉(zhuǎn)換(TypeCast)將其轉(zhuǎn)換回原始的類型對(duì)象。

下面我們使用文章示例里的Person類再給大家演示一下Java的序列化代碼。

publicclassPersonimplementsSerializable{

privatestaticfinallongserialVersionUID=1L;

staticStringcountry="ITALY";

privateintage;

privateStringname;

transientintheight;

//省略getter和setter

這里要注意一下,static修飾的靜態(tài)屬性是類屬性,并不屬于對(duì)象,所以在序列化對(duì)象時(shí)不會(huì)把類中的靜態(tài)屬性序列化了,另外我們也可以使用transient關(guān)鍵字修飾那些我們想在序列化過程中忽略調(diào)的對(duì)象屬性。

@Test

publicvoidserializingAndDeserializing_ThenObjectIsTheSame()()

throwsIOException,ClassNotFoundException{

Personperson=newPerson();

person.setAge(20);

person.setName("Joe");

//用指定文件路徑--當(dāng)前目錄的test_serialization.txt文件創(chuàng)建FileOutputStream。

//在寫入FileOutputStream時(shí),FileOutputStream會(huì)在在項(xiàng)目目錄中創(chuàng)建文件

//“test_serialization.txt”

FileOutputStreamfileOutputStream

=newFileOutputStream("./test_serialization.txt");

//以FileOutputStream為底層輸出流創(chuàng)建對(duì)象輸出流ObjectOutputStream

ObjectOutputStreamobjectOutputStream

=newObjectOutputStream(fileOutputStream);

//向ObjectOutputStream中寫入person對(duì)象

objectOutputStream.writeObject(person);

//把數(shù)據(jù)從流中刷到磁盤上

objectOutputStream.flush();

objectOutputStream.close();

//用上面的文件路徑,創(chuàng)建文件輸入流

FileInputStreamfileInputStream

=newFileInputStream("./test_serialization.txt");

//以文件輸入流創(chuàng)建對(duì)象輸入流ObjectInputStream

ObjectInputStreamobjectInputStream

=newObjectInputStream(fileInputS

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論