1.關(guān)于序列化和反序列化
應(yīng)該大家都大概知道Java中序列化和反序列化的意思,序列化就是把一個Java對象轉(zhuǎn)換成二進(jìn)制進(jìn)行磁盤上傳輸或者網(wǎng)絡(luò)流的傳輸,反序列化的意思就是把這個接受到的二進(jìn)制流重新組裝成原來的對象逆過程。它們在Java中分別是通過ObjectInputStream和 ObjectInputStream這兩個類來實現(xiàn)的(以下分別用ois和oos來簡稱)。
oos的writeObject()方法用來執(zhí)行序列化的過程,ois的readObject()用來執(zhí)行反序列化的過程,在傳輸二進(jìn)制流之前,需要講這兩個高層流對象連接到同一個Channel上,這個Channel可以是磁盤文件,也可以是socket底層流。所以無論用哪種方式,底層流對象都是以構(gòu)造函數(shù)參數(shù)的形式傳遞進(jìn)oos和ois這兩個高層流,連接完畢了才可以進(jìn)行二進(jìn)制數(shù)據(jù)傳輸?shù)。例子?br> 可以是文件流通道
file = new File(“C:/data.dat”);
oos = new ObjectOutputStream(new FileOutputStream(file));
ois = new ObjectInputStream(new FileInputStream(file));
或者網(wǎng)絡(luò)流通道
oos = new ObjectOutputStream(socket.getOutputStream());
ois = new ObjectInputStream(socket.getInputStream());
不知道大家是否注意到oos總是在ois之前定義,這里不希望大家誤解這個順序是固定的么?回答是否定的,那么有順序要求么?回答是肯定的。原則是什么呢?
原則是互相對接的輸入/輸出流之間必須是output流先初始化然后再input流初始化,否則就會拋異常。大家肯定會問為什么?只要稍微看一看這兩個類的源代碼文件就大概知道了,output流的任務(wù)很簡單,只要把對象轉(zhuǎn)換成二進(jìn)制往通道中寫就可以了,但input流需要做很多準(zhǔn)備工作來接受并最終重組這個Object,所以O(shè)bjectInputStream的構(gòu)造函數(shù)中就需要用到output初始化發(fā)送過來的header信息,這個方法叫做 readStreamHeader(),它將會去讀兩個Short值用于決定用多大的緩存來存放通道發(fā)送過來的二進(jìn)制流,這個緩存的size因jre的版本不同是不一樣的。所以output如果不先初始化,input的構(gòu)造函數(shù)首先就無法正確運(yùn)行。
對于上面兩個例子,第一個順序是嚴(yán)格的,第二個因為oos和ois連接的已經(jīng)不是對方了,而是socket另外一端的流,需要嚴(yán)格按照另外一方對接的output流先于對接的input流打開才能順利運(yùn)行。
這個writeObject和readObject本身就是線程安全的,傳輸過程中是不允許被并發(fā)訪問的。所以對象能一個一個接連不斷的傳過來,有很多人在運(yùn)行的時候會碰到EOFException, 然后百思不得其解,去各種論壇問解決方案。其實筆者這里想說,這個異常不是必須聲明的,也就是說它雖然是異常,但其實是正常運(yùn)行結(jié)束的標(biāo)志。EOF表示讀到了文件尾,發(fā)送結(jié)束自然連接也就斷開了。如果這影響到了你程序的正確性的話,請各位靜下心來看看自己程序的業(yè)務(wù)邏輯,而不要把注意力狹隘的聚集在發(fā)送和接受的方法上。因為筆者也被這樣的bug困擾了1整天,被很多論壇的帖子誤解了很多次最后得出的教訓(xùn)。如果在while循環(huán)中去readObject,本質(zhì)上是沒有問題的,有對象數(shù)據(jù)來就會讀,沒有就自動阻塞。那么拋出EOFException一定是因為連接斷了還在繼續(xù)read,什么原因?qū)е逻B接斷了呢?一定是業(yè)務(wù)邏輯哪里存在錯誤,比如NullPoint、 ClassCaseException、ArrayOutofBound,即使程序較大也沒關(guān)系,最多只要單步調(diào)適一次就能很快發(fā)現(xiàn)bug并且解決它。
應(yīng)該大家都大概知道Java中序列化和反序列化的意思,序列化就是把一個Java對象轉(zhuǎn)換成二進(jìn)制進(jìn)行磁盤上傳輸或者網(wǎng)絡(luò)流的傳輸,反序列化的意思就是把這個接受到的二進(jìn)制流重新組裝成原來的對象逆過程。它們在Java中分別是通過ObjectInputStream和 ObjectInputStream這兩個類來實現(xiàn)的(以下分別用ois和oos來簡稱)。
oos的writeObject()方法用來執(zhí)行序列化的過程,ois的readObject()用來執(zhí)行反序列化的過程,在傳輸二進(jìn)制流之前,需要講這兩個高層流對象連接到同一個Channel上,這個Channel可以是磁盤文件,也可以是socket底層流。所以無論用哪種方式,底層流對象都是以構(gòu)造函數(shù)參數(shù)的形式傳遞進(jìn)oos和ois這兩個高層流,連接完畢了才可以進(jìn)行二進(jìn)制數(shù)據(jù)傳輸?shù)。例子?br> 可以是文件流通道
file = new File(“C:/data.dat”);
oos = new ObjectOutputStream(new FileOutputStream(file));
ois = new ObjectInputStream(new FileInputStream(file));
或者網(wǎng)絡(luò)流通道
oos = new ObjectOutputStream(socket.getOutputStream());
ois = new ObjectInputStream(socket.getInputStream());
不知道大家是否注意到oos總是在ois之前定義,這里不希望大家誤解這個順序是固定的么?回答是否定的,那么有順序要求么?回答是肯定的。原則是什么呢?
原則是互相對接的輸入/輸出流之間必須是output流先初始化然后再input流初始化,否則就會拋異常。大家肯定會問為什么?只要稍微看一看這兩個類的源代碼文件就大概知道了,output流的任務(wù)很簡單,只要把對象轉(zhuǎn)換成二進(jìn)制往通道中寫就可以了,但input流需要做很多準(zhǔn)備工作來接受并最終重組這個Object,所以O(shè)bjectInputStream的構(gòu)造函數(shù)中就需要用到output初始化發(fā)送過來的header信息,這個方法叫做 readStreamHeader(),它將會去讀兩個Short值用于決定用多大的緩存來存放通道發(fā)送過來的二進(jìn)制流,這個緩存的size因jre的版本不同是不一樣的。所以output如果不先初始化,input的構(gòu)造函數(shù)首先就無法正確運(yùn)行。
對于上面兩個例子,第一個順序是嚴(yán)格的,第二個因為oos和ois連接的已經(jīng)不是對方了,而是socket另外一端的流,需要嚴(yán)格按照另外一方對接的output流先于對接的input流打開才能順利運(yùn)行。
這個writeObject和readObject本身就是線程安全的,傳輸過程中是不允許被并發(fā)訪問的。所以對象能一個一個接連不斷的傳過來,有很多人在運(yùn)行的時候會碰到EOFException, 然后百思不得其解,去各種論壇問解決方案。其實筆者這里想說,這個異常不是必須聲明的,也就是說它雖然是異常,但其實是正常運(yùn)行結(jié)束的標(biāo)志。EOF表示讀到了文件尾,發(fā)送結(jié)束自然連接也就斷開了。如果這影響到了你程序的正確性的話,請各位靜下心來看看自己程序的業(yè)務(wù)邏輯,而不要把注意力狹隘的聚集在發(fā)送和接受的方法上。因為筆者也被這樣的bug困擾了1整天,被很多論壇的帖子誤解了很多次最后得出的教訓(xùn)。如果在while循環(huán)中去readObject,本質(zhì)上是沒有問題的,有對象數(shù)據(jù)來就會讀,沒有就自動阻塞。那么拋出EOFException一定是因為連接斷了還在繼續(xù)read,什么原因?qū)е逻B接斷了呢?一定是業(yè)務(wù)邏輯哪里存在錯誤,比如NullPoint、 ClassCaseException、ArrayOutofBound,即使程序較大也沒關(guān)系,最多只要單步調(diào)適一次就能很快發(fā)現(xiàn)bug并且解決它。