This page follows chapter 6,
“Object
Serialization Stream Protocol”, of Oracle’s Java Object Serialization
Specification (Java SE 21), read 2026-11-12, with the constants in OpenJDK’s
java.io.ObjectStreamConstants,
read 2026-11-12, which match the chapter. Primitive sizes and byte order follow
OpenJDK’s
java.io.DataOutput,
and field order follows
ObjectStreamField
and ObjectStreamClass,
all read 2026-11-12. Numbers are big-endian (high-order byte first), as the chapter’s
example stream shows: the int 17 is 00 00 00 11.
Stream
AC ED (STREAM_MAGIC), 00 05
(STREAM_VERSION), then contents: objects and blocks of primitive data,
in the order the program wrote them. Each object starts with a type code.
Type codes
| Code | Name | Followed by |
|---|---|---|
70 | TC_NULL | Nothing: a null reference. |
71 | TC_REFERENCE | A 4-byte handle of an object already in the stream. |
72 | TC_CLASSDESC | A class descriptor. |
73 | TC_OBJECT | Class descriptor, then the object’s data for each class from the top superclass down. |
74 | TC_STRING | 2-byte unsigned length and modified UTF-8 (for strings under 65536 bytes). |
75 | TC_ARRAY | Class descriptor, 4-byte size, the elements. |
76 | TC_CLASS | Class descriptor: a Class object. |
77 | TC_BLOCKDATA | 1-byte size and that much primitive data. |
78 | TC_ENDBLOCKDATA | End of optional data written by writeObject or an annotation. |
79 | TC_RESET | Forget all known objects; handles restart at 0x7E0000. |
7A | TC_BLOCKDATALONG | 4-byte size and the data. |
7B | TC_EXCEPTION | A Throwable object, with a reset before and after it so that its objects do not overlap those sent before or after the exception. |
7C | TC_LONGSTRING | 8-byte signed length and modified UTF-8, for strings of 65536 bytes or more (since Java 2 v1.3). |
7D | TC_PROXYCLASSDESC | Dynamic proxy class: a 4-byte interface count, the interface names (each 2-byte length and modified UTF-8), annotation, superclass descriptor (java.lang.reflect.Proxy). |
7E | TC_ENUM | Class descriptor, then the constant’s name as a string object. |
Each new object, string, class, descriptor, array and enum gets a handle: the first
is 0x7E0000 (baseWireHandle) and they count up. A
descriptor’s handle is assigned after its class name and serialVersionUID.
Class descriptors
| Item | Encoding |
|---|---|
| className | 2-byte length and modified UTF-8, such as List in the specification’s example. |
| serialVersionUID | 8 bytes (a long), the Stream Unique Identifier. |
| flags | 1 byte, below. |
| fields | 2-byte count, then each: a type code byte, the name (2-byte length and modified UTF-8), and for object and array fields the type in field descriptor format as a string object (such as Ljava/lang/Object;). |
| classAnnotation | Contents written by annotateClass, then TC_ENDBLOCKDATA. |
| superClassDesc | The superclass’s descriptor, or TC_NULL if the superclass is not serialisable. |
| Flag | Name | Meaning |
|---|---|---|
01 | SC_WRITE_METHOD | The class has writeObject: its data is followed by optional data ending in TC_ENDBLOCKDATA. |
02 | SC_SERIALIZABLE | Serializable. |
04 | SC_EXTERNALIZABLE | Externalizable: the class writes all of its data itself. |
08 | SC_BLOCK_DATA | Externalizable data in block-data mode (protocol version 2, the default since JDK 1.2). |
10 | SC_ENUM | An enum type. |
Field type codes
| Code | Type | Size |
|---|---|---|
B | byte | 1 |
C | char | 2 |
D | double | 8 |
F | float | 4 |
I | int | 4 |
J | long | 8 |
S | short | 2 |
Z | boolean | 1 |
[ | array | An object |
L | object | An object |
A class’s field values are written in the order of its descriptor. OpenJDK sorts
the descriptor’s fields with primitive fields before object fields, and by name
within each group, so the primitive values come first. A boolean is one byte,
01 or 00; char and short two bytes; float four bytes, as
Float.floatToIntBits.
Example
AC ED 00 05 magic, version 5
74 00 05 68 65 6C 6C 6F TC_STRING "hello" (handle 7E0000)