對(duì)于同一個(gè)socket,如果調(diào)用兩次就會(huì)拋出StreamCorruptedException
如果你使用socket,并通過(guò)對(duì)象輸入/輸出流來(lái)處理的話,并且已經(jīng)對(duì)某個(gè)socket調(diào)用了一次getInputStream時(shí),但又需要把這個(gè)socket的相關(guān)信息作為參數(shù)傳遞給別的對(duì)象時(shí),應(yīng)注意:不用直接把socket傳過(guò)去,應(yīng)該把對(duì)應(yīng)的ObjectInputStream或ObjectOutputStream對(duì)象傳遞過(guò)去。
調(diào)用getInputStream方法就會(huì)讀取標(biāo)示頭信息。用缺省的serializetion的實(shí)現(xiàn)時(shí),一個(gè)ObjectOutputStream的構(gòu)造和一個(gè)ObjectInputStream的構(gòu)造必須一一對(duì)應(yīng).ObjectOutputStream的構(gòu)造函數(shù)會(huì)向輸出流中寫(xiě)入一個(gè)標(biāo)識(shí)頭,而ObjectInputStream會(huì)首先讀入這個(gè)標(biāo)識(shí)頭.因此,多次以追加方式向一個(gè)文件中寫(xiě)入object時(shí),該文件將會(huì)包含多個(gè)標(biāo)識(shí)頭.所以用ObjectInputStream來(lái)deserialize這個(gè)ObjectOutputStream時(shí),將產(chǎn)生StreamCorruptedException.
==============================
使用ObjectStream會(huì)出現(xiàn)的問(wèn)題
1. ObjectInputStream與ObjectOutputStream的順序問(wèn)題
在網(wǎng)絡(luò)通訊中,主機(jī)與客戶端若使用ObjectInputStream與ObjectOutputStream建立對(duì)象通訊,必須注意聲明此兩個(gè)對(duì)象的順序。
如:
主機(jī)端先建立ObjectInputStream后建立ObjectOutputStream,則對(duì)應(yīng)地客戶端要先建立ObjectOutputStream后建立ObjectInputStream,否則會(huì)造成兩方互相等待數(shù)據(jù)而導(dǎo)致死鎖。
原因是建立ObjectInputStream對(duì)象是需要先接收一定的header數(shù)據(jù),接收到這些數(shù)據(jù)之前會(huì)處于阻塞狀態(tài)。以下為JAVA API文檔的說(shuō)明
Creates an ObjectInputStream that reads from the specified InputStream.
A serialization stream header is read from the stream and verified.
This constructor will block until the corresponding ObjectOutputStream
has written and flushed the header.
故而為了防止這種死鎖狀態(tài),通訊兩方的ObjectInputStraem,ObjectOutputStream必須注意順序?qū)?yīng)使用。
2. ObjectInputStream接收到非ObjectOutputStream數(shù)據(jù)的問(wèn)題
在使用ObjectInputStream與ObjectOutputStream對(duì)象通訊的通訊雙方,假設(shè)客戶端程序出現(xiàn)錯(cuò)誤,發(fā)送了非ObjectOutputStream封裝發(fā)送的數(shù)據(jù)(比如發(fā)送一個(gè)數(shù)字或字符串到主機(jī)),則主機(jī)端的ObjectInputStream接收到錯(cuò)誤數(shù)據(jù)后不能自動(dòng)糾正,會(huì)一直接收數(shù)據(jù)而處于阻塞狀態(tài),從而導(dǎo)致通訊失敗。尚未找到解決方法。目前想的辦法為寫(xiě)自己的ObjectStream類。
3. 解決版本問(wèn)題
使用ObjectStream的時(shí)候會(huì)額外發(fā)送一個(gè)關(guān)于對(duì)象的序列號(hào)
static final long serialVersionUID = ....
手動(dòng)加入此域則可避免版本差異導(dǎo)致的問(wèn)題。
對(duì)象序列號(hào)的計(jì)算可用SDK的serialver計(jì)算。
======================================
ObjectInputStream ObjectOutputStream
ObjectOutputStream和ObjectInputStream
--ObjectOutputStream
ObjectInputStream 類恢復(fù)以前使用 ObjectOutputStream 類序列化后的基本類型數(shù)據(jù)和對(duì)象。
ObjectOutputStream 和 ObjectInputStream 分別利用 FileOutputStream 和 FileInputStream 能支持應(yīng)用程序?qū)崿F(xiàn)對(duì)象圖象的穩(wěn)定存儲(chǔ)。
ObjectInputStream 可用于恢復(fù)以前序列化過(guò)的對(duì)象。另外其它一些情況也使用此類,諸如使用一個(gè) Socket 在主機(jī)間傳遞對(duì)象時(shí),
或在遠(yuǎn)程通訊系統(tǒng)中為實(shí)現(xiàn)參數(shù)和參變量的通訊而進(jìn)行對(duì)象傳遞時(shí)。
ObjectInputStream 保證從流中創(chuàng)建的圖象中的所有對(duì)象的類型與 Java 虛擬機(jī)中出現(xiàn)的類匹配。使用標(biāo)準(zhǔn)機(jī)制按需裝載相應(yīng)類。
只有支持 java.io.Serializable 或 java.io.Externalizable 接口的對(duì)象才能從流中讀取。使用 readObject 方法從該流中
讀取一個(gè)對(duì)象。 Java 的安全造型應(yīng)該用于獲取期望類型。在 Java 中, 串和數(shù)組都是對(duì)象且可當(dāng)作是序列化過(guò)程中的對(duì)象。
讀取時(shí),它們需要轉(zhuǎn)換為所需類型。
另外基類型也可使用 DataInput 中的正確方法從該流中讀取。
對(duì)象的缺省逆序列化機(jī)制將每個(gè)域的內(nèi)容恢復(fù)為它被寫(xiě)入時(shí)的值和類型。逆序列化過(guò)程中忽略申明為暫時(shí)的或靜態(tài)的域。
對(duì)其它對(duì)象的引用促使那些對(duì)象必須從流中讀取。使用引用共享機(jī)制正確地恢復(fù)對(duì)象的圖象。逆序列化時(shí)總是分配新對(duì)象,
防止重寫(xiě)已存在的對(duì)象。
讀取一個(gè)對(duì)象同運(yùn)行一個(gè)新對(duì)象的構(gòu)造子類似。為該對(duì)象分配的內(nèi)存初始化為空(NULL)。為非序列化類調(diào)用無(wú)參構(gòu)造子,
然后將序列化類的域從該流中恢復(fù),恢復(fù)從最接近 java.lang.object 的序列化對(duì)象開(kāi)始,到指定對(duì)象結(jié)束。
例如讀取在示例中寫(xiě)入 ObjectOutputStream 中的流:
FileInputStream istream = new FileInputStream("t.tmp");
ObjectInputStream p = new ObjectInputStream(istream);
int i = p.readInt();
String today = (String)p.readObject();
Date date = (Date)p.readObject();
istream.close();
類通過(guò)實(shí)現(xiàn) java.io.Serializable 或 java.io.Externalizable 接口來(lái)控制它們的序列化。
實(shí)現(xiàn)序列化接口可以使對(duì)象能保存和恢復(fù)它的完整狀態(tài),可以使類在寫(xiě)入流和從流中讀取的期間內(nèi)進(jìn)行改進(jìn)。
它自動(dòng)地遍歷對(duì)象間的引用,保存和恢復(fù)完整圖象。在序列化和逆序列化處理過(guò)程中需要特定句柄的可序列化類,
必須實(shí)現(xiàn)如下這兩個(gè)方法:
private void writeObject(java.io.ObjectOutputStream stream)
throws IOException;
private void readObject(java.io.ObjectInputStream stream)
throws IOException, ClassNotFoundException;
利用 writeObjectmethod 方法將一個(gè)特殊類的對(duì)象的狀態(tài)寫(xiě)入某流后,相應(yīng)的 readObject 方法將負(fù)責(zé)讀取和恢復(fù)這些數(shù)據(jù)。
此方法不必關(guān)心狀態(tài)是屬于它的父類還是子類。 從 ObjectInputStream 讀取數(shù)據(jù)恢復(fù)單個(gè)域的狀態(tài),并將之賦給該對(duì)象的恰當(dāng)域。
使用 DataInput 方法讀取基本數(shù)據(jù)類型。
序列化操作對(duì)沒(méi)有實(shí)現(xiàn) java.io.Serializable 接口的對(duì)象,不讀取或分配它的域值。非序列化對(duì)象的子類可以是序列化的。
在這種情況下,非序列化類必須有一個(gè)無(wú)參構(gòu)造子,使它的域能使用此構(gòu)造子完成初始化。 在此情況下,
子類負(fù)責(zé)保存和恢復(fù)非序列化類的狀態(tài)。通常情況父類的域是可存儲(chǔ)的(公有的、包或保護(hù)的),
或存在用于恢復(fù)它的狀態(tài)的可使用的獲取或設(shè)置方法。
ObjectInputStream 能獲取逆序列化一個(gè)對(duì)象期間出現(xiàn)的任一異常,一旦出現(xiàn)異常,則放棄讀過(guò)程。
實(shí)現(xiàn)外部接口可以使對(duì)象完全控制此對(duì)象序列化形式的內(nèi)容和格式。
調(diào)用外部接口的方法:writeExternal 和 readExternal 保存和恢復(fù)對(duì)象狀態(tài)。當(dāng)一個(gè)類實(shí)現(xiàn)了這些方法時(shí),
它們就能使用 ObjectOutput 和 ObjectInput 方法的所有方法寫(xiě)入或讀取它們自己的狀態(tài)。對(duì)象負(fù)責(zé)管理它出現(xiàn)的相應(yīng)版本。
ObjectOutputStream
public class ObjectOutputStream
extends OutputStream
implements ObjectOutput, ObjectStreamConstants
類 ObjectOutputStream 將 Java 對(duì)象中的基本數(shù)據(jù)類型和圖元寫(xiě)入到一個(gè) OutputStream 對(duì)象中。可使用 ObjectInputStream 讀取這些對(duì)象。
另外使用此流對(duì)應(yīng)的文件能存儲(chǔ)這些對(duì)象。如果該流是一個(gè)網(wǎng)絡(luò)通訊流,則在另一臺(tái)主機(jī)或另一個(gè)處理機(jī)上可重建這些對(duì)象。
只有支持 java.io.Serializable 接口的對(duì)象才能被寫(xiě)入該流。對(duì)每個(gè)可序列化的對(duì)象進(jìn)行編碼,包括相應(yīng)類的名稱和標(biāo)記,
對(duì)象的屬性和數(shù)組值,以及初始化對(duì)象時(shí)引用的任何其它對(duì)象等。
使用 writeObject 將一個(gè)對(duì)象寫(xiě)入該流。任一對(duì)象,包括串和數(shù)組,均采用 writeObject 方法被寫(xiě)入。
也能將多個(gè)對(duì)象或基類型對(duì)象寫(xiě)入此流。反過(guò)來(lái),必須以這些對(duì)象被寫(xiě)入的相同類型和相同順序,
從相應(yīng)的 ObjectInputstream 流中讀回這些對(duì)象。
基類型也可使用 DataOutput 中的正確方法寫(xiě)入此流。串對(duì)象也可使用 writeUTF 方法寫(xiě)入。
一個(gè)對(duì)象的缺省序列化機(jī)制將寫(xiě)入對(duì)象的類,類標(biāo)記和所有的非暫時(shí)的和非靜態(tài)的屬性值。
其它對(duì)象(除暫時(shí)的或靜態(tài)的屬性)的引用也將促使以上這些對(duì)象被寫(xiě)入。 使用共享機(jī)制,對(duì)單一對(duì)象的多次引用進(jìn)行編碼,
以至對(duì)象的圖元能被存儲(chǔ)為與它原來(lái)寫(xiě)入時(shí)有相同的形狀。
例如寫(xiě)入一個(gè)對(duì)象,此對(duì)象能從 ObjectInputStream 中讀出:
FileOutputStream ostream = new FileOutputStream("t.tmp");
ObjectOutputStream p = new ObjectOutputStream(ostream);
p.writeInt(12345);
p.writeObject("Today");
p.writeObject(new Date());
p.flush();
ostream.close();
在序列化處理過(guò)程中需要特定句柄的類,必須使用如下這些恰當(dāng)?shù)臉?biāo)記實(shí)現(xiàn)特定的方法:
private void readObject(java.io.ObjectInputStream stream)
throws IOException, ClassNotFoundException;
private void writeObject(java.io.ObjectOutputStream stream)
throws IOException
writeObject 方法負(fù)責(zé)寫(xiě)特定類的對(duì)象的狀態(tài),以使相應(yīng)的 readObject 方法能存儲(chǔ)它。
此方法不必關(guān)心寫(xiě)入對(duì)象的父類或子類的狀態(tài)。使用 writeObject 方法或基本類型支持的 DataOutput
方法將每個(gè)域的狀態(tài)保存到 ObjectOutputStream 中。
序列化操作不能輸出沒(méi)有實(shí)現(xiàn) java.io.Serializable 接口的任一對(duì)象的域。非序列化對(duì)象的子類可以是序列化的。
在這種情況下,非序列化類必須有一個(gè)無(wú)參構(gòu)造子,使它的域能被初始化。 在此情況下,子類負(fù)責(zé)保存和恢復(fù)非序列化類的狀態(tài)。
通常情況父類的域是可存儲(chǔ)的(公有的、包或保護(hù)的),或存在用于恢復(fù)它的狀態(tài)的可使用的獲取或設(shè)置方法。
實(shí)現(xiàn)拋出 NotSerializableException 異常的 writeObject 和 readObject 方法能阻止一個(gè)對(duì)象的序列化。
ObjectOutputStream 將獲取這個(gè)異常,并放棄這個(gè)序列化過(guò)程。實(shí)現(xiàn)外部接口可以使對(duì)象完全控制此對(duì)象序列化形式的內(nèi)容和格式。
調(diào)用外部接口的方法:writeExternal 和 readExternal 保存和恢復(fù)對(duì)象狀態(tài)。當(dāng)一個(gè)類實(shí)現(xiàn)了這些方法時(shí),
它們就能使用 ObjectOutput 和 ObjectInput 方法的所有方法寫(xiě)入或讀取它們自己的狀態(tài)。對(duì)象負(fù)責(zé)管理它出現(xiàn)的相應(yīng)版本。
import java.io.*;
import java.util.*;
public class Logon implements Serializable {
private Date date = new Date();
private String username;
private transient String password;
Logon(String name, String pwd) {
username = name;
password = pwd;
}
public String toString() {
String pwd = (password == null) ? "(n/a)" : password;
return "logon info: \n " + "username: " + username + "\n date: " + date + "\n password: " + pwd;
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
Logon a = new Logon("Morgan", "morgan83");
System.out.println( "logon a = " + a);
ObjectOutputStream o = new ObjectOutputStream( new FileOutputStream("Logon.out"));
o.writeObject(a);
o.close();
int seconds = 5;
long t = System.currentTimeMillis() + seconds * 1000;
while(System.currentTimeMillis() < t) ;
ObjectInputStream in = new ObjectInputStream( new FileInputStream("Logon.out"));
System.out.println( "Recovering object at " + new Date());
a = (Logon)in.readObject();
System.out.println("logon a = " + a);
}
}
類Logon是一個(gè)記錄登錄信息的類,包括用戶名和密碼。首先它實(shí)現(xiàn)了接口Serializable,這就標(biāo)志著它可以被序列化。
之后再main方法里ObjectOutputStream o = new ObjectOutputStream( new FileOutputStream("Logon.out"));
新建一個(gè)對(duì)象輸出流包裝一個(gè)文件流,表示對(duì)象序列化的目的地是文件Logon.out。然后用方法writeObject開(kāi)始寫(xiě)入。
想要還原的時(shí)候也很簡(jiǎn)單ObjectInputStream in = new ObjectInputStream( new FileInputStream("Logon.out"));
新建一個(gè)對(duì)象輸入流以文件流Logon.out為參數(shù),之后調(diào)用readObject方法就可以了。