問題:
若通過ObjectOutputStream向一個文件中多次以追加的方式寫入Object,為什麼用ObjectInputStream讀取這些Object時,會產生StreamCorruptedException
原因:
使用缺省的serializetion的實現時,一個ObjectOutputStream的構造和一個ObjectInputStream的構造必須一一對應,ObjectOutputStream的構造函數會向輸出流中寫入一個標識頭,而ObjectInputStream會首先讀入這個標誌頭,因此,多次以追加方式向一個文件寫入Object時,該文件將會包含多個標誌頭,所以用ObjectInputStream來deserialize這個ObjectOutputStream時,將產生StreamCorruptedException。
解決方案:
構造一個ObjectOutputStream的子類,並覆蓋writeStreamHeader()方法,被覆蓋後的writeStreamHeader()方法應判斷是否為首次向文件中寫入Object,是則調用super.writeStreamHeader();若否,即以追加方式寫入Object時,則應調用ObjectOutputStream.reset()方法。
案例:
創建測試類,在控制台上顯示添加學生信息,要求程序循環運行,並依次提示接收學生類的所有屬性值,保存到學生對象中,再將學生對象保存到集合對象中,並提示“是否繼續添加(y/n):”,如果選擇“y”則繼續添加,否則退出循環,並將保存學生數據的集合對象通過序列化保存到“student.dat”文件中。然後實現從“student.dat”文件中反序列化保存學生數據的集合對象,並遍歷打印輸出學生信息。
解決StreamCorruptedException的錯誤:
/** * 這個類可以避免StreamCorruptedException * @author Liao * */ public class MyObjectOutputStream extends ObjectOutputStream { //無參構造函數 public MyObjectOutputStream() throws IOException, SecurityException { super(); } //有參構造函數 public MyObjectOutputStream(OutputStream out) throws IOException { super(out); } /** * 重寫writeStreamHeader()方法 */ @Override protected void writeStreamHeader() throws IOException { // TODO Auto-generated method stub return ; } }
工具類:
public class SerializableUtil implements Serializable{ private static final long serialVersionUID = 1L; /** * 序列化對象 * 用戶傳一個Object類型對象,和要序列化到的文件路徑即可進行序列化 * @param obj對象 * @param path 對象要被序列化到的文件路徑 */ public static void serializObject(Object obj,String path){ //創建文件 File file = new File(path); //判斷文件是否存在 if (!file.exists()){ //如果文件不存在,就創建文件 try { file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } //創建ObjectOutputStream ObjectOutputStream objectOutputStream = null; try { //定義FileOutputStream,用於創建ObjectOutputStream OutputStream outputStream = new FileOutputStream(file, true); if (file.length() < 1){ objectOutputStream = new ObjectOutputStream(outputStream); } else { objectOutputStream = new MyObjectOutputStream(outputStream); } //把對象序列化到文件中 objectOutputStream.writeObject(obj); } catch (Exception e) { e.printStackTrace(); } finally {//強制關閉ObjectOutputStream try { objectOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 反序列化一個對象 * * @param path 要反序列的文件的路徑 * @return 返回一個被反序列化的對象 */ @SuppressWarnings("resource") public static Object unserializObject(String path){ //定義一個對象 Object obj = null; //創建一個文件 File file = new File(path); try { //創建文件輸入流 InputStream inputStream = new FileInputStream(file); //通過文件輸入流創建ObjectInputStream ObjectInputStream objectInputStream = new ObjectInputStream(inputStream); //獲取一個對象 obj = objectInputStream.readObject(); } catch (Exception e) { e.printStackTrace(); } //返回一個對象 return obj; } /** * 序列化一個集合 * 用戶傳一個集合和要序列化到的路徑即可進行序列化 * @param collections 集合 * @param path 要序列化的路徑 */ public static <T> void serializList(List<T> collections,String path){ //創建文件 File file = new File(path); //判斷文件是否存在 if (!file.exists()){ //如果文件不存在,就創建文件 try { file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } //創建ObjectOutputStream ObjectOutputStream objectOutputStream = null; try { //定義FileOutputStream,用於創建ObjectOutputStream OutputStream outputStream = new FileOutputStream(file, true); if (file.length() < 1){ objectOutputStream = new ObjectOutputStream(outputStream); } else { objectOutputStream = new MyObjectOutputStream(outputStream); } //遍歷集合,然後把對象序列化到文件中 for (T collection : collections){ objectOutputStream.writeObject(collection); } } catch (Exception e) { e.printStackTrace(); } finally {//強制關閉ObjectOutputStream try { objectOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 反序列化一個List集合 * 因為文件中有多個對象,如何在反序列化到末尾時,終止反序列化呢? * 這裡有一個技巧,我們可以通過一個死循環,一直讀知道拋出EOFException時就break退出循環 * @param path路徑 * @return 返回一個集合 */ @SuppressWarnings("unchecked") public static <T> List<T> unserializList(String path){ //創建一個文件 File file = new File(path); //創建一個List集合用於存儲反序列化後的對象 List<T> collections = new ArrayList<T>(); //定義ObjectInputStream ObjectInputStream objectInputStream = null; try { //創建文件輸入流 InputStream inputStream = new FileInputStream(file); objectInputStream = new ObjectInputStream(inputStream); //獲取一個對象 while (true){ try { //反序列化一個對象 T t = (T) objectInputStream.readObject(); //把該對象添加到集合中 collections.add(t); } catch (EOFException e) { break; } } } catch (Exception e) { e.printStackTrace(); } finally { try { objectInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } //返回一個對象 return collections; } }
測試類:
/** * 測試類 * @author Liao * */ public class SerializationTest { public static void main(String[] args) { Scanner in = new Scanner(System.in); boolean flag = true; //創建集合用於存放實體對象 List<Person> persons = new ArrayList<Person>(); while (flag) { // 輸入用戶ID System.out.print("請輸入UID:"); Integer uid = in.nextInt(); // 輸入用戶名 System.out.print("請輸入用戶名:"); String uname = in.next(); // 輸入性別 System.out.print("請輸入性別:"); String gender = in.next(); //把Person對象存到集合中 persons.add(new Person(uid, uname, gender.equals("男")? Gender.MAN : Gender.WOMEN)); // 是否繼續 System.out.print("是否繼續? y/n:"); String ifContinue = in.next(); if (ifContinue.equals("n")) { flag = false; } } //序列化 SerializableUtil.serializList(persons, "D:\\liaozhongmin.txt"); //反序列化 List<Person> collections = SerializableUtil.unserializList("D:\\liaozhongmin.txt"); System.out.println(collections); } }