< prev index next >

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

Print this page
*** 1,7 ***
  /*
!  * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved.
   * Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 only, as
--- 1,7 ---
  /*
!  * Copyright (c) 1996, 2026, Oracle and/or its affiliates. All rights reserved.
   * Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 only, as

*** 158,10 ***
--- 158,21 ---
   * block-data record.
   *
   * <p>Records are serialized differently than ordinary serializable or externalizable
   * objects, see <a href="ObjectInputStream.html#record-serialization">record serialization</a>.
   *
+  * <div class="preview-block">
+  *      <div class="preview-comment">
+  *          <p>{@linkplain Class#isValue Value classes} that are not records cannot be
+  *          serialized directly. To serialize an instance of a value class, the
+  *          <a href="{@docRoot}/../specs/serialization/output.html#the-writereplace-method">
+  *          {@code writeReplace}</a> method can provide a proxy object instead. That
+  *          object can then be serialized, and used to reconstruct the expected value
+  *          class instance at deserialization time.
+  *      </div>
+  * </div>
+  *
   * @spec serialization/index.html Java Object Serialization Specification
   * @author      Mike Warres
   * @author      Roger Riggs
   * @see java.io.DataOutput
   * @see java.io.ObjectInputStream

*** 309,10 ***
--- 320,20 ---
       * <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.
       *
+      * <div class="preview-block">
+      *      <div class="preview-comment">
+      *          <p>An object that instantiates or extends a Serializable
+      *          {@linkplain Class#isValue value class} can only be serialized if
+      *          it is a record, or it implements {@code writeReplace}, or it is
+      *          a boxed primitive value. Otherwise, {@code writeObject} throws an
+      *          {@code InvalidClassException}.
+      *      </div>
+      * </div>
+      *
       * @throws  InvalidClassException Something is wrong with a class used by
       *          serialization.
       * @throws  NotSerializableException Some object to be serialized does not
       *          implement the java.io.Serializable interface.
       * @throws  IOException Any exception thrown by the underlying

*** 1312,10 ***
--- 1333,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 {

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

*** 1376,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;
              if (slotDesc.hasWriteObjectMethod()) {
                  PutFieldImpl oldPut = curPut;
                  curPut = null;
                  SerialCallbackContext oldContext = curContext;
  
--- 1400,13 ---
       * superclass to subclass.
       */
      private void writeSerialData(Object obj, ObjectStreamClass desc)
          throws IOException
      {
!        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 >