为什么要将对象序列化
假如有兩個類,分別是A和B,B類中含有一個指向A類對象的引用,現在我們對兩個類進行實例化{ A a = new A(); B b = new B(); },這時在內存中實際上分配了兩個空間,一個存儲對象a,一個存儲對象b,接下來我們想將它們寫入到磁盤的一個文件中去,就在寫入文件時出現了問題!因為對象b包含對對象a的引用,所以系統會自動的將a的數據復制一份到b中,這樣的話當我們從文件中恢復對象時(也就是重新加載到內存中)時,內存分配了三個空間,而對象a同時在內存中存在兩份,如果我想修改對象a的數據的話,那不是還要搜索它的每一份拷貝來達到對象數據的一致性,這不是我們所希望的!
??? 以下序列化機制的解決方案:
?1.保存到磁盤的所有對象都獲得一個序列號(1, 2, 3等等)
? ?2.當要保存一個對象時,先檢查該對象是否被保存了。
? 3.如果以前保存過,只需寫入"與已經保存的具有序列號x的對象相同"的標記,否則,保存該對象
但不是每一個類都能序列化,例如java.awt.geom包中的Point2D.Double類就是不可序列化的,因為該類沒有實現Serializable接口java.io包有兩個序列化對象的類。ObjectOutputStream負責將對象寫入字節流,ObjectInputStream從字節流重構對象。
API描述
ObjectOutputStream 將 Java 對象的基本數據類型和圖形寫入 OutputStream。可以使用 ObjectInputStream 讀取(重構)對象。通過在流中使用文件可以實現對象的持久存儲。如果流是網絡套接字流,則可以在另一臺主機上或另一個進程中重構對象。
只能將支持 java.io.Serializable 接口的對象寫入流中。每個 serializable 對象的類都被編碼,編碼內容包括類名和類簽名、對象的字段值和數組值,以及從初始對象中引用的其他所有對象的閉包。
writeObject 方法用于將對象寫入流中。所有對象(包括 String 和數組)都可以通過 writeObject 寫入。可將多個對象或基元寫入流中。必須使用與寫入對象時相同的類型和順序從相應 ObjectInputstream 中讀回對象。?
我們先了解ObjectOutputStream類吧。ObjectOutputStream類擴展DataOutput接口。
writeObject()方法是最重要的方法,用于對象序列化。如果對象包含其他對象的引用,則writeObject()方法遞歸序列化這些對象。每個 ObjectOutputStream維護序列化的對象引用表,防止發送同一對象的多個拷貝。(這點很重要)
下面,讓我們從例子中來了解ObjectOutputStream這個類
)~#U2F'{3}#l?
bbs.spoto.net9k({%k%r3{7u,?+p1q?
// 序列化 today's date 到一個文件中.?3c7c*`!h-H2W |&l?
FileOutputStream f = new FileOutputStream("tmp");?1M;E&R,f.o1A8[+w?
ObjectOutputStream s = new ObjectOutputStream(f);?
s.writeObject("Today");?
s.writeObject(new Date());?
s.flush();?,f9Q5`2[:t Y?
ObjectInputStream這個類。它與ObjectOutputStream相似。它擴展DataInput接口。 ObjectInputStream中的方法鏡像DataInputStream中讀取Java基本數據類型的公開方法。readObject()方法從字節流中反序列化對象。每次調用readObject()方法都返回流中下一個Object。對象字節流并不傳輸類的字節碼,而是包括類名及其簽名。 readObject()收到對象時,JVM裝入頭中指定的類。如果找不到這個類,則readObject()拋出 ClassNotFoundException,如果需要傳輸對象數據和字節碼,則可以用RMI框架。ObjectInputStream的其余方法用于定制反序列化過程。:h2z(J+h,d;S5R?
例子如下:IT雛鷹部落(N,V1M&~$w?
//從文件中反序列化 string 對象和 date 對象?bbs.spoto.net)/1_8[6Z H8V5|?
FileInputStream in = new FileInputStream("tmp");?
ObjectInputStream s = new ObjectInputStream(in);?
String today = (String)s.readObject();?
Date date = (Date)s.readObject();?;w&d'}+n"I5r8o&c:R @
例子,來自開源中國客戶端代碼
/**
* 保存對象
* @param ser
* @param file
* @throws IOException
*/
//保存對象就是序列化
public boolean saveObject(Serializable ser, String file) {
FileOutputStream fos = null;
ObjectOutputStream oos = null;
try{
fos = openFileOutput(file, MODE_PRIVATE);
oos = new ObjectOutputStream(fos);
oos.writeObject(ser);
oos.flush();
return true;
}catch(Exception e){
e.printStackTrace();
return false;
}finally{
try {
oos.close();
} catch (Exception e) {}
try {
fos.close();
} catch (Exception e) {}
}
}
/**
* 讀取對象
* @param file
* @return
* @throws IOException
*/
//反序列化
public Serializable readObject(String file){
if(!isExistDataCache(file))
return null;
FileInputStream fis = null;
ObjectInputStream ois = null;
try{
fis = openFileInput(file);
ois = new ObjectInputStream(fis);
return (Serializable)ois.readObject();
}catch(FileNotFoundException e){
}catch(Exception e){
e.printStackTrace();
//反序列化失敗 - 刪除緩存文件
if(e instanceof InvalidClassException){
File data = getFileStreamPath(file);
data.delete();
}
}finally{
try {
ois.close();
} catch (Exception e) {}
try {
fis.close();
} catch (Exception e) {}
}
return null;
}
轉載于:https://my.oschina.net/wuyiwu/blog/87154
總結
以上是生活随笔為你收集整理的为什么要将对象序列化的全部內容,希望文章能夠幫你解決所遇到的問題。