現在的位置: 首頁 > 綜合 > 正文

Java 序列化和反序列化工具類並解決StreamCorruptedException問題

2018年06月05日 ⁄ 綜合 ⁄ 共 4842字 ⁄ 字號 評論關閉

問題:

若通過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);
	}

}

抱歉!評論已關閉.