使用ois.readObject()读取时的边界判定及相关技术咨询
关于Java对象序列化传输的三个问题解答
1. 连续发送两个同类对象,接收方会不会视为一个?
不会。ObjectOutputStream.writeObject()每次调用都会将一个对象完整序列化并写入流中,生成独立的序列化数据单元;对应的ObjectInputStream.readObject()每次调用会读取一个完整的序列化单元,反序列化为一个对象返回。不管两个对象是不是同类,都是独立的读取单元,不会被合并成一个。
2. 大体积序列化类,接收方如何判断接收完成?
readObject()本身是阻塞方法:它会一直等待,直到完整读取一个对象的所有序列化数据后才会返回,返回时就表示这个大对象已经接收完成。- 判断流的结束:如果发送方不再发送对象并关闭输出流,接收方调用
readObject()时会抛出EOFException,此时就可以确定没有更多对象需要接收了。 - 自定义长度标记(可选):如果需要更精细的控制,可以在发送每个大对象前,先用
writeInt()写入该对象序列化后的字节长度,接收方先读取长度,再读取对应字节数的数据流,最后反序列化。示例代码如下:// 发送方 ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(largeObj); oos.flush(); byte[] data = baos.toByteArray(); out.writeInt(data.length); // 先写对象长度 out.write(data); // 再写对象序列化数据 // 接收方 int length = in.readInt(); byte[] data = new byte[length]; in.readFully(data); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data)); Object largeObj = ois.readObject();
3. 用Object作为边界标记的方式是否正确?
完全错误。因为所有Java对象都是Object的实例,所以原代码中的if (obj instanceof Object)判断永远为true,根本无法区分业务对象和边界标记,逻辑完全失效。
正确的做法是使用一个唯一的标记对象,比如自定义单例标记:
// 定义全局边界标记 public class SerializationMarker { public static final Object BOUNDARY = new Object(); } // 发送方 out.writeObject(obj1); out.writeObject(SerializationMarker.BOUNDARY); out.writeObject(obj2); out.writeObject(SerializationMarker.BOUNDARY); // 接收方 Object obj; try { while ((obj = in.readObject()) != null) { if (obj == SerializationMarker.BOUNDARY) { // 识别到边界,处理下一个对象或结束当前批次 System.out.println("到达边界"); } else { // 处理业务对象 System.out.println("处理业务对象: " + obj.getClass().getName()); } } } catch (EOFException e) { // 流已结束,停止读取 }
这里利用了Java序列化对单例对象的引用保留特性:同一个BOUNDARY对象多次序列化后,反序列化得到的还是同一个引用,所以可以用==直接判断。也可以自定义一个空的BoundaryMarker类,通过instanceof BoundaryMarker来判断,效果一致。
内容的提问来源于stack exchange,提问作者Mr Right
相关产品推荐
相关产品推荐

