< prev index next >

src/java.base/share/classes/java/io/ObjectOutputStream.java

Print this page
@@ -37,10 +37,12 @@
  import jdk.internal.util.ByteArray;
  import jdk.internal.access.JavaLangAccess;
  import jdk.internal.access.SharedSecrets;
  import sun.reflect.misc.ReflectUtil;
  
+ import static java.io.ObjectInputStream.TRACE;
+ 
  import static jdk.internal.util.ModifiedUtf.putChar;
  import static jdk.internal.util.ModifiedUtf.utfLen;
  
  /**
   * An ObjectOutputStream writes primitive data types and graphs of Java objects

@@ -131,10 +133,16 @@
   * form.  The methods of the Externalizable interface, writeExternal and
   * readExternal, are called to save and restore the objects state.  When
   * implemented by a class they can write and read their own state using all of
   * the methods of ObjectOutput and ObjectInput.  It is the responsibility of
   * the objects to handle any versioning that occurs.
+  * Value classes implementing {@link Externalizable} cannot be serialized
+  * or deserialized, the value object is immutable and the state cannot be restored.
+  * Use {@link Serializable} {@code writeReplace} to delegate to another serializable
+  * object such as a record.
+  *
+  * Value objects cannot be {@code java.io.Externalizable}.
   *
   * <p>Enum constants are serialized differently than ordinary serializable or
   * externalizable objects.  The serialized form of an enum constant consists
   * solely of its name; field values of the constant are not transmitted.  To
   * serialize an enum constant, ObjectOutputStream writes the string returned by

@@ -156,13 +164,40 @@
   * filled up to 1024 bytes, or be written whenever there is a termination of
   * block-data mode.  Calls to the ObjectOutputStream methods writeObject,
   * defaultWriteObject and writeFields initially terminate any existing
   * block-data record.
   *
+  * <a id="record-serialization"></a>
   * <p>Records are serialized differently than ordinary serializable or externalizable
   * objects, see <a href="ObjectInputStream.html#record-serialization">record serialization</a>.
   *
+  * <a id="valueclass-serialization"></a>
+  * <p>Value classes are {@linkplain Serializable} through the use of the serialization proxy pattern.
+  * The serialization protocol does not support a standard serialized form for value classes.
+  * The value class delegates to a serialization proxy by supplying an alternate
+  * record or object to be serialized instead of the value class.
+  * When the proxy is deserialized it re-constructs the value object and returns the value object.
+  * For example,
+  * {@snippet lang="java" :
+  * value class ZipCode implements Serializable {    // @highlight substring="value class"
+  *     private static final long serialVersionUID = 1L;
+  *     private int zipCode;
+  *     public ZipCode(int zip) { this.zipCode = zip; }
+  *     public int zipCode() { return zipCode; }
+  *
+  *     public Object writeReplace() {    // @highlight substring="writeReplace"
+  *         return new ZipCodeProxy(zipCode);
+  *     }
+  *
+  *     private record ZipCodeProxy(int zipCode) implements Serializable {
+  *         public Object readResolve() {    // @highlight substring="readResolve"
+  *             return new ZipCode(zipCode);
+  *         }
+  *     }
+  * }
+  * }
+  *
   * @spec serialization/index.html Java Object Serialization Specification
   * @author      Mike Warres
   * @author      Roger Riggs
   * @see java.io.DataOutput
   * @see java.io.ObjectInputStream

@@ -343,10 +378,13 @@
       * written.  Default serialization for a class can be overridden using the
       * writeObject and the readObject methods.  Objects referenced by this
       * object are written transitively so that a complete equivalent graph of
       * objects can be reconstructed by an ObjectInputStream.
       *
+      * <p>Serialization and deserialization of value classes is described in
+      * {@linkplain ObjectOutputStream##valueclass-serialization value class serialization}.
+      *
       * <p>Exceptions are thrown for problems with the OutputStream and for
       * classes that should not be serialized.  All exceptions are fatal to the
       * OutputStream, which is left in an indeterminate state, and it is up to
       * the caller to ignore or recover the stream state.
       *

@@ -412,10 +450,13 @@
       * calls to readUnshared by the receiver will not conflict.  Note that the
       * rules described above only apply to the base-level object written with
       * writeUnshared, and not to any transitively referenced sub-objects in the
       * object graph to be serialized.
       *
+      * <p>Serialization and deserialization of value classes is described in
+      * {@linkplain ObjectOutputStream##valueclass-serialization value class serialization}.
+      *
       * <p>ObjectOutputStream subclasses which override this method can only be
       * constructed in security contexts possessing the
       * "enableSubclassImplementation" SerializablePermission; any attempt to
       * instantiate such a subclass without this permission will cause a
       * SecurityException to be thrown.

@@ -1445,10 +1486,13 @@
              handles.assign(unshared ? null : obj);
  
              if (desc.isRecord()) {
                  writeRecordData(obj, desc);
              } else if (desc.isExternalizable() && !desc.isProxy()) {
+                 if (desc.isValue())
+                     throw new InvalidClassException("Externalizable not valid for value class "
+                             + desc.forClass().getName());
                  writeExternalData((Externalizable) obj);
              } else {
                  writeSerialData(obj, desc);
              }
          } finally {

@@ -1493,14 +1537,14 @@
      /** Writes the record component values for the given record object. */
      private void writeRecordData(Object obj, ObjectStreamClass desc)
          throws IOException
      {
          assert obj.getClass().isRecord();
-         ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
-         if (slots.length != 1) {
+         List<ObjectStreamClass.ClassDataSlot> slots = desc.getClassDataLayout();
+         if (slots.size() != 1) {
              throw new InvalidClassException(
-                     "expected a single record slot length, but found: " + slots.length);
+                     "expected a single record slot length, but found: " + slots.size());
          }
  
          defaultWriteFields(obj, desc);  // #### seems unnecessary to use the accessors
      }
  

@@ -1509,13 +1553,13 @@
       * superclass to subclass.
       */
      private void writeSerialData(Object obj, ObjectStreamClass desc)
          throws IOException
      {
-         ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
-         for (int i = 0; i < slots.length; i++) {
-             ObjectStreamClass slotDesc = slots[i].desc;
+        List<ObjectStreamClass.ClassDataSlot> slots = desc.getClassDataLayout();
+         for (int i = 0; i < slots.size(); i++) {
+             ObjectStreamClass slotDesc = slots.get(i).desc;
              if (slotDesc.hasWriteObjectMethod()) {
                  PutFieldImpl oldPut = curPut;
                  curPut = null;
                  SerialCallbackContext oldContext = curContext;
  
< prev index next >