问题:
若通过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); } }