Java ObjectStreamConstants 接口
最后修改时间:2025 年 4 月 16 日
java.io.ObjectStreamConstants
接口定义了 Java 对象序列化中使用的常量。这些常量表示流协议版本、魔数和类型标记。它们由 Java 的序列化机制在内部使用。
ObjectStreamConstants
由 ObjectOutputStream
和 ObjectInputStream
实现。这些常量定义了序列化对象的二进制格式。理解这些常量有助于自定义序列化和调试序列化问题。
ObjectStreamConstants 接口概述
该接口包含流头、字段类型和协议版本的常量。这些值是 Java 序列化规范的一部分。为了兼容性,它们在 JVM 实现中保持一致。
public interface ObjectStreamConstants { final static short STREAM_MAGIC = (short)0xaced; final static short STREAM_VERSION = 5; final static byte TC_BASE = 0x70; final static byte TC_NULL = (byte)0x70; final static byte TC_REFERENCE = (byte)0x71; final static byte TC_CLASSDESC = (byte)0x72; final static byte TC_OBJECT = (byte)0x73; final static byte TC_STRING = (byte)0x74; final static byte TC_ARRAY = (byte)0x75; final static byte TC_CLASS = (byte)0x76; final static byte TC_BLOCKDATA = (byte)0x77; final static byte TC_ENDBLOCKDATA = (byte)0x78; final static byte TC_RESET = (byte)0x79; final static byte TC_BLOCKDATALONG = (byte)0x7A; final static byte TC_EXCEPTION = (byte)0x7B; final static byte TC_LONGSTRING = (byte)0x7C; final static byte TC_PROXYCLASSDESC = (byte)0x7D; final static byte TC_ENUM = (byte)0x7E; final static int baseWireHandle = 0x7e0000; final static byte SC_WRITE_METHOD = 0x01; final static byte SC_BLOCK_DATA = 0x08; final static byte SC_SERIALIZABLE = 0x02; final static byte SC_EXTERNALIZABLE = 0x04; final static byte SC_ENUM = 0x10; }
上面的代码展示了 ObjectStreamConstants
中的关键常量。 这些包括魔数、类型代码和序列化标志。 这些值用于识别流中序列化数据的不同部分。
检查流魔数
STREAM_MAGIC 常量标识 Java 序列化流。 此示例演示了如何通过检查魔数来验证文件是否包含序列化的 Java 对象。 对于有效的流,该值应为 0xaced。
import java.io.DataInputStream; import java.io.FileInputStream; import java.io.IOException; public class MagicNumberChecker { public static void main(String[] args) { try (FileInputStream fis = new FileInputStream("serialized.data"); DataInputStream dis = new DataInputStream(fis)) { short magic = dis.readShort(); if (magic == java.io.ObjectStreamConstants.STREAM_MAGIC) { System.out.println("Valid Java serialization stream detected"); System.out.println("Stream version: " + dis.readShort()); } else { System.out.println("Not a Java serialization stream"); } } catch (IOException e) { System.err.println("Error reading file: " + e.getMessage()); } } }
此示例将文件的前两个字节读取为 short 值。 它将此值与 STREAM_MAGIC (0xaced) 进行比较,以验证它是否为序列化流。 如果有效,它会读取流版本号。 这对于验证序列化数据文件非常有用。
识别序列化对象类型
TC_* 常量标识序列化流中的不同类型。 此示例演示了从序列化文件中读取类型标记。 流中的每个对象前面都有一个类型代码字节。
import java.io.DataInputStream; import java.io.FileInputStream; import java.io.IOException; public class TypeMarkerReader { public static void main(String[] args) { try (FileInputStream fis = new FileInputStream("objects.ser"); DataInputStream dis = new DataInputStream(fis)) { // Skip stream header (magic + version) dis.readShort(); dis.readShort(); byte typeMarker; while ((typeMarker = dis.readByte()) != -1) { switch (typeMarker) { case java.io.ObjectStreamConstants.TC_NULL: System.out.println("TC_NULL: null reference"); break; case java.io.ObjectStreamConstants.TC_STRING: System.out.println("TC_STRING: " + dis.readUTF()); break; case java.io.ObjectStreamConstants.TC_OBJECT: System.out.println("TC_OBJECT: new object"); // Skip class description and field data dis.readInt(); // handle break; default: System.out.println("Unknown type marker: " + typeMarker); return; } } } catch (IOException e) { System.err.println("Error reading file: " + e.getMessage()); } } }
此示例在跳过标头后从序列化文件中读取类型标记。 它演示了如何使用 TC_* 常量处理不同的对象类型。 对于 TC_STRING,它读取实际的字符串值。 更完整的实现将处理所有类型代码。
检查可序列化类标志
SC_* 常量表示类序列化特征。 此示例演示了如何通过检查类的标志来检查类是否实现了 writeObject。 这些标志存储在序列化流中的类描述符中。
import java.io.ByteArrayOutputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class ClassFlagsChecker { static class TestClass implements Serializable { private static final long serialVersionUID = 1L; private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException { // Custom serialization } } public static void main(String[] args) throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(new TestClass()); oos.close(); byte[] data = baos.toByteArray(); // Find classDescFlags (after magic, version, and TC_OBJECT) int flagsOffset = 4 + 1 + 4; // Adjust based on actual stream structure byte flags = data[flagsOffset]; if ((flags & java.io.ObjectStreamConstants.SC_WRITE_METHOD) != 0) { System.out.println("Class has custom writeObject method"); } if ((flags & java.io.ObjectStreamConstants.SC_SERIALIZABLE) != 0) { System.out.println("Class implements Serializable"); } } }
此示例序列化一个测试类并检查序列化的数据。 它检查 SC_WRITE_METHOD 标志,该标志指示自定义序列化。 这些标志是可以组合的位掩码值(SC_SERIALIZABLE | SC_WRITE_METHOD)。
处理流中的块数据
TC_BLOCKDATA 和 TC_BLOCKDATALONG 标记指示原始数据块。 此示例演示了如何处理序列化流中的块数据。 块数据通常包含原始值或字符串。
import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.IOException; public class BlockDataProcessor { public static void main(String[] args) { // Simulate serialized data with block data byte[] simulatedData = { (byte)0xac, (byte)0xed, 0x00, 0x05, // header 0x77, 0x04, 0x00, 0x00, 0x00, 0x0a, // TC_BLOCKDATA, length 4, value 10 0x78 // TC_ENDBLOCKDATA }; try (ByteArrayInputStream bais = new ByteArrayInputStream(simulatedData); DataInputStream dis = new DataInputStream(bais)) { // Skip stream header dis.readShort(); dis.readShort(); byte marker = dis.readByte(); if (marker == java.io.ObjectStreamConstants.TC_BLOCKDATA) { int length = dis.readByte() & 0xff; System.out.println("Block data found, length: " + length); int value = dis.readInt(); System.out.println("Block data value: " + value); byte endMarker = dis.readByte(); if (endMarker != java.io.ObjectStreamConstants.TC_ENDBLOCKDATA) { System.out.println("Unexpected end marker"); } } } catch (IOException e) { System.err.println("Error processing data: " + e.getMessage()); } } }
此示例处理包含块数据的模拟序列化流。 它读取 TC_BLOCKDATA 标记,后跟长度和实际数据。 长度是无符号的 (0-255)。 TC_BLOCKDATALONG 类似,但使用 4 字节长度表示更大的块。
使用引用
TC_REFERENCE 标记指示对先前序列化对象的后向引用。 此示例演示了引用在序列化中的工作方式。 当同一个对象多次出现时,引用有助于避免重复。
import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class ReferenceExample { static class TestObject implements Serializable { private static final long serialVersionUID = 1L; String value; TestObject(String value) { this.value = value; } } public static void main(String[] args) throws Exception { TestObject obj1 = new TestObject("First"); TestObject obj2 = obj1; // Same object reference ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(obj1); oos.writeObject(obj2); oos.close(); byte[] data = baos.toByteArray(); DataInputStream dis = new DataInputStream(new ByteArrayInputStream(data)); // Skip header and first object dis.readShort(); // STREAM_MAGIC dis.readShort(); // STREAM_VERSION dis.readByte(); // TC_OBJECT dis.readInt(); // handle // Check if second object is a reference byte marker = dis.readByte(); if (marker == java.io.ObjectStreamConstants.TC_REFERENCE) { int handle = dis.readInt(); System.out.println("Second object is reference to handle: " + handle); } } }
此示例序列化对同一对象的两个引用。 第二次写入生成一个指向第一个对象句柄的 TC_REFERENCE 标记。 该句柄是在序列化期间分配的唯一标识符。 这演示了 Java 的对象共享机制。
来源
Java ObjectStreamConstants 接口文档
在本文中,我们介绍了 Java ObjectStreamConstants 接口中的基本常量及其用法。 理解这些常量有助于进行高级序列化任务和调试序列化流。
作者
列出所有Java教程。