《Java網(wǎng)絡(luò)程序設(shè)計(jì)》課件-第7章_第1頁
《Java網(wǎng)絡(luò)程序設(shè)計(jì)》課件-第7章_第2頁
《Java網(wǎng)絡(luò)程序設(shè)計(jì)》課件-第7章_第3頁
《Java網(wǎng)絡(luò)程序設(shè)計(jì)》課件-第7章_第4頁
《Java網(wǎng)絡(luò)程序設(shè)計(jì)》課件-第7章_第5頁
已閱讀5頁,還剩47頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡(jiǎn)介

第7章對(duì)象序列化

7.1對(duì)象序列化7.2序列化操作7.3定制序列化

7.1對(duì)?象?序?列?化

7.1.1序列化的概念

計(jì)算機(jī)軟件的主要工作是處理數(shù)據(jù)。在處理數(shù)據(jù)時(shí),經(jīng)常會(huì)遇到需要處理一組相關(guān)的數(shù)據(jù),例如,要保存這樣一條聊天信息“張三:李四:你好”,這條信息中包括三個(gè)內(nèi)容,即發(fā)信者、接收者和聊天內(nèi)容,通常需要使用三個(gè)變量進(jìn)行存儲(chǔ)。在C語言中使用結(jié)構(gòu)體實(shí)現(xiàn)儲(chǔ)存一組相關(guān)的數(shù)據(jù),如例7-1所示。

【例7-1】

使用結(jié)構(gòu)體存儲(chǔ)數(shù)據(jù)。

StructMessage{

charsender[10];

charreceiver[10];

charcontent[30];

}msg[20];

【例7-2】利用結(jié)構(gòu)體存儲(chǔ)一組數(shù)據(jù),并保存在文件中或者從文件中讀取。

Filefp=File();

//存儲(chǔ)的方法

for(i=0;i<20;i++)

fwrite(&msg[i],sizeof(StructMessage),1,fp);

//讀取的方法

for(i=0;i<20;i++)

fread(&msg[i],sizeof(StructMessage),1,fp);

Java語言中,取消了結(jié)構(gòu)體,除了基本數(shù)據(jù)類型都是對(duì)象。如果要存儲(chǔ)一組相關(guān)的數(shù)據(jù),就必須采用自定義相關(guān)類。

【例7-3】使用Java定義存儲(chǔ)消息的類。對(duì)比兩種語言對(duì)聊天消息的描述方法,其主要區(qū)別在于,使用Java語言描述方式不僅包含成員變量,而且包括了成員方法,從而實(shí)現(xiàn)了信息的封裝。

在Java語言中,處理數(shù)據(jù)均采用流的方式,其最大的特點(diǎn)就是數(shù)據(jù)的輸入和輸出都按順序進(jìn)行。Java語言是面向?qū)ο蟮恼Z言,除了字符串對(duì)象可以通過writeUTF()和readUTF()直接操作外,其他對(duì)象不能直接存儲(chǔ)和通過網(wǎng)絡(luò)發(fā)送。當(dāng)需要對(duì)使用例7-3的類所定義的實(shí)例對(duì)象進(jìn)行存儲(chǔ)或者發(fā)送時(shí),就應(yīng)該將該對(duì)象進(jìn)行序列化操作,使其轉(zhuǎn)變?yōu)榱餍问?,然后才能?shí)現(xiàn)存儲(chǔ)和通過網(wǎng)絡(luò)發(fā)送。序列化是一種用來處理對(duì)象流的機(jī)制,對(duì)象流是將對(duì)象的內(nèi)容進(jìn)行流化,然后將經(jīng)過流化后的對(duì)象進(jìn)行讀/寫操作和用于網(wǎng)絡(luò)傳輸。發(fā)送端將對(duì)象序列化為流,接收端反序列化從數(shù)據(jù)流重新構(gòu)造對(duì)象,保證了對(duì)象的完整性和可傳遞性。即,序列化的過程是將對(duì)象寫入字節(jié)流,反序列化的過程是從字節(jié)流中讀取對(duì)象。將對(duì)象狀態(tài)轉(zhuǎn)換成字節(jié)流之后,可以用java.io包中的各種字節(jié)流類將其保存到文件中,或者通過管道在線程之間傳輸,或者通過網(wǎng)絡(luò)連接將對(duì)象數(shù)據(jù)發(fā)送到另一主機(jī)。

序列化在網(wǎng)絡(luò)程序設(shè)計(jì)(如Socket、RMI(RemoteMethodInvocation)、JMS(JavaMessageService)、EJB(EnterpriseJavaBean))中有廣泛的應(yīng)用。7.1.2序列化的實(shí)現(xiàn)

對(duì)象序列化是將攜帶信息的對(duì)象轉(zhuǎn)換為可以用于存儲(chǔ)或傳輸形式的過程。在序列化期間,對(duì)象將其當(dāng)前狀態(tài)寫入到臨時(shí)或持久性存儲(chǔ)區(qū),然后,可以通過從存儲(chǔ)區(qū)中讀取或反序列化對(duì)象的狀態(tài),重新創(chuàng)建該對(duì)象。序列化分為兩大部分:序列化和反序列化,即對(duì)象序列化不僅要將對(duì)象轉(zhuǎn)換成字節(jié)表示,有時(shí)還要恢復(fù)對(duì)象。

序列化是這個(gè)過程的第一部分,將數(shù)據(jù)分解成字節(jié)流,以便存儲(chǔ)在文件中或在網(wǎng)絡(luò)上傳輸。反序列化就是打開字節(jié)流,按照被恢復(fù)數(shù)據(jù)的對(duì)象實(shí)例重構(gòu)對(duì)象。這兩個(gè)過程結(jié)合起來,就可以輕松地存儲(chǔ)和傳輸對(duì)象數(shù)據(jù)。序列化的目的:

●?以某種存儲(chǔ)形式使自定義對(duì)象持久化;

●?將對(duì)象從一個(gè)地方傳遞到另一個(gè)地方;

●?使程序更具維護(hù)性。

Java序列化是基于TCP協(xié)議的,實(shí)現(xiàn)比較簡(jiǎn)單,通常不需要編寫保存和恢復(fù)對(duì)象狀態(tài)的定制代碼。但是要求被序列化的類是實(shí)現(xiàn)了java.io.Serializable接口的,這樣就可以轉(zhuǎn)換成字節(jié)流或從字節(jié)流恢復(fù),不需要在類中增加額外任何代碼。Serializable接口定義如圖7-1所示。圖7-1Serializable接口定義

在Serinalizable接口中未提供任何方法,即不存在需要被覆蓋的方法。通過implements實(shí)現(xiàn)Serializable接口的目的在于標(biāo)識(shí)該對(duì)象是可被序列化的。

在Java語言中并不是任何個(gè)類都可被序列化。通常將用于存儲(chǔ)信息的類進(jìn)行序列化,因?yàn)檫@些類所定義的對(duì)象是用于存儲(chǔ)和傳輸?shù)?,例如學(xué)生信息類、消息信息類。而用于執(zhí)行指令和操作的類是不用于序列化的,例如將學(xué)生信息儲(chǔ)存和讀取類、消息的發(fā)送和接收類。尤其要注意,涉及線程的類和與特定JVM有非常復(fù)雜關(guān)系的類,都不能進(jìn)行序列化操作。序列化使其他代碼可以查看或修改那些不序列化便無法訪問的對(duì)象實(shí)例數(shù)據(jù)。確切地說,代碼執(zhí)行序列化需要特殊的權(quán)限,即指定了SerializationFormatter標(biāo)志的Security?Permission。在默認(rèn)策略下,通過Internet下載的代碼或Intranet代碼不會(huì)授予該權(quán)限;只有本地計(jì)算機(jī)上的代碼才被授予該權(quán)限。

通常,實(shí)現(xiàn)Serializable接口的類所聲明的對(duì)象實(shí)例中所有字段都會(huì)被序列化,這意味著數(shù)據(jù)會(huì)被表示為實(shí)例的序列化數(shù)據(jù)。這樣能夠解釋該格式的代碼有可能能夠確定這些數(shù)據(jù)的值,而不依賴于該成員的可訪問性。類似地,反序列化從序列化的表示形式中提取數(shù)據(jù),并直接設(shè)置對(duì)象狀態(tài),這也與可訪問性規(guī)則無關(guān)。7.1.3ObjectInputStream與ObjectOutputStream

為了將存儲(chǔ)于對(duì)象中的數(shù)據(jù)保存在磁盤文件或通過網(wǎng)絡(luò)發(fā)送等,需要使用基于對(duì)象的輸入/輸出流。java.io包提供兩個(gè)類用于序列化對(duì)象傳輸,分別是對(duì)象輸入類ObjectInput?Stream與對(duì)象輸出流類ObjectOutputStream。

ObjectInputStream用于從底層輸入流中讀取對(duì)象類型的數(shù)據(jù),ObjectOutputStream用于將對(duì)象類型的數(shù)據(jù)寫入到底層輸入流。ObjectInputStream與ObjectOutputStream類所讀/寫的對(duì)象必須實(shí)現(xiàn)了Serializable接口。對(duì)象中被transient和static修飾的成員變量不會(huì)被讀取和寫入。●?ObjectInputStream從字節(jié)流重構(gòu)對(duì)象,實(shí)現(xiàn)反序列化過程。反序列化時(shí),JVM用頭信息生成對(duì)象實(shí)例,然后將對(duì)象字節(jié)流中的數(shù)據(jù)復(fù)制到對(duì)象數(shù)據(jù)成員中。

●?ObjectOutputStream負(fù)責(zé)將對(duì)象寫入字節(jié)流,實(shí)現(xiàn)序列化過程,該序列化過程與字節(jié)流連接,包括對(duì)象類型和版本信息。

用一個(gè)輸出流(如FileOutputStream)來構(gòu)造一個(gè)ObjectOutputStream(對(duì)象流)對(duì)象,接著,使用ObjectOutputStream對(duì)象的writeObject(Objectobj)方法就可以將參數(shù)為obj的對(duì)象寫出(即保存其狀態(tài)),要恢復(fù)的話則用輸入流。當(dāng)需要將數(shù)據(jù)存儲(chǔ)在本地磁盤時(shí),在Java中需要使用ObjectOutputStream類,該類擴(kuò)展DataOutput接口。writeObject()方法是最重要的方法,用于對(duì)象序列化。如果對(duì)象包含其他對(duì)象的引用,則writeObject()方法遞歸序列化這些對(duì)象。由于writeObject()可以序列化整組交叉引用的對(duì)象,因此,同一ObjectOutputStream實(shí)例可能不小心被請(qǐng)求序列化同一對(duì)象。每個(gè)ObjectOutputStream維護(hù)序列化的對(duì)象引用表,防止發(fā)送同一對(duì)象的多個(gè)拷貝。

【例7-4】

在文件tmp中保存兩個(gè)對(duì)象,其中一個(gè)是字符串“Today”,另一個(gè)是當(dāng)前的日期。//

序列化

today's

?date

到一個(gè)文件中.

FileOutputStream

f

=

new

FileOutputStream("tmp");

ObjectOutputStream

s

=

new

ObjectOutputStream(f);

s.writeObject("Today");

s.writeObject(new

Date());

s.flush();

當(dāng)需要將數(shù)據(jù)從本地磁盤讀取出時(shí),在Java中需要使用ObjectInputStream類,該類擴(kuò)展DataInput接口。readObject()為重要的方法,實(shí)現(xiàn)從字節(jié)流中反序列化對(duì)象。每次調(diào)用readObject()方法都返回流中下一個(gè)Object。對(duì)象字節(jié)流并不傳輸類的字節(jié)碼,而是包括類名及其簽名。readObject()收到對(duì)象時(shí),JVM裝入頭中指定的類。如果找不到這個(gè)類,則readObject()拋出ClassNotFoundException。如果需要傳輸對(duì)象數(shù)據(jù)和字節(jié)碼,則可以用RMI框架。ObjectInputStream的其余方法用于定制反序列化過程。

【例7-5】從文件tmp中讀取數(shù)據(jù)對(duì)象。

//從文件中反序列化

string

對(duì)象和

date

對(duì)象

FileInputStream

in

=

new

FileInputStream(“tmp”);

ObjectInp

utStream

s

=

new

ObjectInputStream(in);

String

today

=

(String)s.readObject();

Date

date

=

(Date)s.readObject();

7.2序?列?化?操?作

7.2.1序列化存儲(chǔ)

通常,實(shí)現(xiàn)序列化接口的類是用于存儲(chǔ)和傳輸?shù)念悾糜诓僮鞯念愂遣贿M(jìn)行序列化的。首先,通過代碼學(xué)習(xí)在Java中如何通過序列化實(shí)現(xiàn)聊天信息的儲(chǔ)存。在下面的例子中定義一個(gè)聊天信息類,該類用于存儲(chǔ)信息,需要實(shí)現(xiàn)Serializable接口。并且該類需要在存儲(chǔ)和讀取時(shí)都使用,通常將自定義信息類、存儲(chǔ)類和讀取類設(shè)計(jì)在一個(gè)包中,否則會(huì)出現(xiàn)無法找到類的異常發(fā)生。代碼注釋如下:

①第1行通常為了保證能夠順利地進(jìn)行序列化和反序列化操作,聲明為Serializable接口的存儲(chǔ)類和實(shí)現(xiàn)操作類應(yīng)在一個(gè)message包中,以保證訪問的路徑;

②第3行定義的存儲(chǔ)類實(shí)現(xiàn)了Serializable接口;

③第4~6行聲明了三個(gè)成員變量;

④第7~11行聲明了構(gòu)造方法;

⑤第12~16行定義了輸出方法show()。

例7-6中定義的信息類需要實(shí)現(xiàn)Serializable接口,所以需要引用io類庫(kù)。在類中的成員變量有三個(gè),分別記錄消息的發(fā)送者、接收者和內(nèi)容。成員方法有兩個(gè),一個(gè)是用于賦初值的構(gòu)造方法Message(),另一個(gè)是用于輸出顯示的方法show()。接下來,定義將該類進(jìn)行存儲(chǔ)操作的類,即進(jìn)行序列化操作的類。該類中首先申明了兩個(gè)需要被存儲(chǔ)的信息對(duì)象,然后申明文件輸出對(duì)象,通過ObjectOutputStream.write

Object()方法,將信息對(duì)象保存到文件中。

【例7-7】

定義用于存儲(chǔ)學(xué)生信息的類,執(zhí)行序列化操作,保存數(shù)據(jù)至文件。

代碼注釋如下:

①第1行與實(shí)現(xiàn)序列化的存儲(chǔ)類在一個(gè)message包中;

②第9~10行使用Message類聲明了兩個(gè)被存儲(chǔ)對(duì)象;

③第12~13行聲明對(duì)象輸出流;

④第14~15行通過writeOjbect()輸出對(duì)象,實(shí)現(xiàn)序列化操作。

運(yùn)行該程序,將在當(dāng)前目錄下生成一個(gè)新的文件msgDB.dat,用于存儲(chǔ)信息。使用記事本軟件打開該文件,可以看到存儲(chǔ)的內(nèi)容,如圖7-2所示。圖7-2msgDB.dat文件內(nèi)容

【例7-8】

定義用于存儲(chǔ)信息的類,執(zhí)行反序列化操作,從文件中讀取數(shù)據(jù)還原成對(duì)象。

代碼注釋如下:

①第1行與實(shí)現(xiàn)序列化的存儲(chǔ)類在一個(gè)message包中;

②第9~10行聲明對(duì)象輸入流;

③第12~13行采用readObject()實(shí)現(xiàn)反序列化操作,因?yàn)樽x取時(shí)默認(rèn)類型為Object,所以需要進(jìn)行一次強(qiáng)制類型轉(zhuǎn)換為需要的類對(duì)象;

④第16~17行輸出內(nèi)容。

程序運(yùn)行結(jié)果如圖7-3所示。圖7-3反序列化結(jié)果7.2.2序列化傳輸

序列化操作不僅可以在本地磁盤保存類數(shù)據(jù),也可以將對(duì)象用于網(wǎng)絡(luò)傳輸。首先,定義一個(gè)用于存儲(chǔ)消息的消息類,用于記錄消息發(fā)送者、消息接收者、消息內(nèi)容,在這里直接利用了例7-6,注意Message類的包信息需要進(jìn)行相應(yīng)修改。另外分別編寫客戶端和服務(wù)器端程序。

【例7-9】實(shí)現(xiàn)客戶端,用于序列化對(duì)象,發(fā)送對(duì)象流。代碼注釋如下:

①第9行通過TCP連接指定的服務(wù)器localhost的8080端口;

②第10行套接字上獲得輸出流,使其作為對(duì)象輸出流的參數(shù);

③第11行準(zhǔn)備要傳輸?shù)膶?duì)象;

④第12~13行通過writeObject()發(fā)送信息對(duì)象。

【例7-10】

實(shí)現(xiàn)服務(wù)器端,用于接收對(duì)象流,進(jìn)行反序列化操作。代碼注釋如下:

①第10行啟動(dòng)本地的TCP8080端口,監(jiān)聽客戶端的連接請(qǐng)求;

②第12行與客戶端實(shí)現(xiàn)連接;

③第13行獲得套接字的getInputStream(),使其作為ObjectInputStream的參數(shù);

④第15行獲得對(duì)象,并進(jìn)行反序列化操作,還原為Message對(duì)象;

⑤第16~17行輸出結(jié)果。

服務(wù)器端接收序列化及反序列化操作運(yùn)行結(jié)果如圖7-4所示。圖7-4服務(wù)器端運(yùn)行結(jié)果從運(yùn)行結(jié)果可見,服務(wù)器收到的信息對(duì)象直接輸出是不可讀的,除非該信息類中覆蓋了Object中的toString()方法。

7.3定?制?序?列?化

7.3.1序列化成員變量

在Java中類的成員變量可以是簡(jiǎn)單數(shù)據(jù)類型,也可以是復(fù)雜數(shù)據(jù)類型,如字符串類類型、自定義類類型等。當(dāng)某個(gè)類實(shí)現(xiàn)了序列化接口時(shí),其未加transient和static修飾的成員變量一同被序列化。所以,當(dāng)使用類類型定義成員變量時(shí),要保證這些類是可以被序列化的,即實(shí)現(xiàn)了Serializable接口。如String類的定義中就聲明實(shí)現(xiàn)了Serializable接口,如圖7-5所示。圖7-5String類的定義當(dāng)實(shí)現(xiàn)序列化的自定義類的成員變量中還包含了其他的自定義類對(duì)象時(shí),就需要該成員變量實(shí)現(xiàn)序列化接口。

代碼注釋如下:

①第6行使用Detail類對(duì)象作為成員變量;

②第20~27行實(shí)現(xiàn)Detail類的定義,此時(shí)它未實(shí)現(xiàn)Serializable接口。

可以從以上代碼可以看出,新定義了Details類,用于存儲(chǔ)消息內(nèi)容和發(fā)送時(shí)間,但是該類未實(shí)現(xiàn)序列化接口。在Message類中,采用Details類定義了一個(gè)details對(duì)象。在對(duì)此類進(jìn)行編譯時(shí),未出現(xiàn)任何錯(cuò)誤提示。

在運(yùn)行對(duì)象流發(fā)送端時(shí)將會(huì)拋出異常,提示Details類未實(shí)現(xiàn)Serializable接口,如圖7-6所示??梢娨粋€(gè)實(shí)現(xiàn)序列化接口中的成員變量如果是類對(duì)象,那么該類也必須實(shí)現(xiàn)了序列化接口,否則在運(yùn)行時(shí)就會(huì)出現(xiàn)無法實(shí)現(xiàn)序列化操作的異常提示信息。圖7-6異常提示信息7.3.2定制序列化

對(duì)于一個(gè)正在運(yùn)行的類對(duì)象來說,存在以下概念:

●?生命周期:從JVM提供一個(gè)對(duì)象所需要的資源,到釋放該對(duì)象資源為止,就是一個(gè)對(duì)象的生命周期。

●?短暫存儲(chǔ):對(duì)象在內(nèi)存中構(gòu)建后,會(huì)隨著程序的運(yùn)行和結(jié)束而改變和結(jié)束,這就是短暫存儲(chǔ)。在Java語言中,序列化過程中,使用transient關(guān)鍵字表示屬性或?qū)ο笫嵌虝旱摹?/p>

●?永久存儲(chǔ):對(duì)象被保存在永久設(shè)備中,這些永久設(shè)備包括文件、磁盤、數(shù)據(jù)庫(kù)等,該對(duì)象數(shù)據(jù)不會(huì)隨程序結(jié)束而消失。對(duì)象序列化是對(duì)象永久化的一種機(jī)制。序列化通??梢宰詣?dòng)完成,但有時(shí)可能要對(duì)這個(gè)過程進(jìn)行控制。對(duì)于任何可能包含重要的安全性數(shù)據(jù)的對(duì)象,應(yīng)該使該對(duì)象不可序列化。如果它必須為可序列化的,也需要指定特定字段來保存不可序列化的重要數(shù)據(jù)。如果無法實(shí)現(xiàn)這一點(diǎn),則應(yīng)注意該數(shù)據(jù)會(huì)被公開給任何擁有序列化權(quán)限的代碼,并確保不讓任何惡意代碼獲得該權(quán)限。

Java在對(duì)類實(shí)現(xiàn)serializable接口時(shí),可通過關(guān)鍵字static或transient為類中的數(shù)據(jù)成員變量進(jìn)行定制,以實(shí)現(xiàn)保護(hù)特殊數(shù)據(jù)的目的。

將數(shù)據(jù)成員聲明為transient后,序列化過程就無法將其加進(jìn)對(duì)象字節(jié)流中,也就沒有從transient數(shù)據(jù)成員發(fā)送的數(shù)據(jù)。后面數(shù)據(jù)反序列化時(shí),要重建數(shù)據(jù)成員(因?yàn)樗穷惗x的一部分),但不包含任何數(shù)據(jù),因?yàn)檫@個(gè)數(shù)據(jù)成員不向流中寫入任何數(shù)據(jù)。

在本例的基礎(chǔ)上運(yùn)行例

溫馨提示

  • 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)論