1.下面是一段测试程序

 

<span style="white-space:pre">    </span>//创建一个Record对象
Integer integ2 = new Integer(1);
Record record2 = new Record();
record2.fields.add(integ2);

//创建一个Record对象
Integer integ3 = new Integer(2);
Record record3 = new Record();
record3.fields.add(integ3);

//将对象record2写入到文件
FileOutputStream fo = new FileOutputStream("redo2.log", true);
ObjectOutputStream so = new ObjectOutputStream(fo);
so.writeObject(record2);
so.close();
fo.close();

//将对象record3追加到文件
FileOutputStream fo1 = new FileOutputStream("redo2.log", true);
ObjectOutputStream so1 = new ObjectOutputStream(fo1);
so1.writeObject(record3);
so1.close();
fo1.close();

//创建一个输入流
FileInputStream fi = new FileInputStream("redo2.log");
ObjectInputStream ois = new ObjectInputStream(fi);

//读取一个对象
Record record21 = (Record) ois.readObject();
System.out.println(record21.fields);

//读取一个对象
Record record31 = (Record) ois.readObject();
System.out.println(record31.fields);

执行上边的测试程序,控制台会输出下面的内容:

[1]
Exception in thread "main" java.io.StreamCorruptedException: invalid type code: AC
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1377)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
at com.highgo.hgdbadmin.log.Log.main(Log.java:143)

出错的代码是下面这行开始:

Record record31 = (Record) ois.readObject();

2.原因

看下面的两段代码

public ObjectOutputStream(OutputStream out) throws IOException {
verifySubclass();
bout = new BlockDataOutputStream(out);
handles = new HandleTable(10, (float) 3.00);
subs = new ReplaceTable(10, (float) 3.00);
enableOverride = false;
writeStreamHeader();
bout.setBlockDataMode(true);
if (extendedDebugInfo) {
debugInfoStack = new DebugTraceInfoStack();
} else {
debugInfoStack = null;
}
}
public ObjectInputStream(InputStream in) throws IOException {
verifySubclass();
bin = new BlockDataInputStream(in);
handles = new HandleTable(10);
vlist = new ValidationList();
enableOverride = false;
readStreamHeader();
bin.setBlockDataMode(true);
}

这两个分别是ObjectOutputStream和ObjectInputStream的构造方法。可以看到,代码非常对称。其中有两个方法ObjectOutputStream#writeStreamHeader和ObjectInputStream#readStreamHeader已经牵扯到修改文件和读文件了。这时我们可以发现,在测试代码中,我们new了两个ObjectOutputStream对象,也就是说,我们往文件中写了两次StreamHeader,而我们只new了一个ObjectInputStream对象,也就是说,只读一次StreamHeader,这样在读了第一个对象之后,之后的要读的内容和写的内容就不对称了。具体可以看下面这个图,图中上边的是测试代码实际写入的内容,下面的是测试代码想要读出的内容,可以看到,只有第一个对象对上号了,第二个对象没有对上号。

ObjectOutputStream向文件尾追加对象的问题_继承

这里我们可以看下StreamHeader的内容:

protected void writeStreamHeader() throws IOException {
bout.writeShort(STREAM_MAGIC);
bout.writeShort(STREAM_VERSION);
}

很简单,一个魔数,一个版本号。两个都是short类型,占4个字节。

3.解决方法

在了解了上边的原理之后,解决的方式就多种多样了,只要让写入的内容和预期的读的内容对上号就行了。下面举个例子:

比如,可以看上边的图片,我们只要不写入StreamHeader不就可以了,这样分别继承ObjectOutputStream和ObjectInputStream,分别重写writeStreamHeader和readStreamHeader方法,让他们doNothing就Okay了。具体实现如下:

public class MyObjectOutputStream extends ObjectOutputStream {

public MyObjectOutputStream(OutputStream os) throws IOException, SecurityException {
super(os);
}

@Override
protected void writeStreamHeader() throws IOException {
super.reset();
}
}
public class MyObjectInputStream extends ObjectInputStream{

public MyObjectInputStream(InputStream in) throws IOException {
super(in);
}

@Override
protected void readStreamHeader() throws IOException, StreamCorruptedException {
}
}