< prev index next >

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

Print this page
*** 1,7 ***
  /*
!  * Copyright (c) 1996, 2025, Oracle and/or its affiliates. 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
   * published by the Free Software Foundation.  Oracle designates this
--- 1,7 ---
  /*
!  * Copyright (c) 1996, 2026, Oracle and/or its affiliates. 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
   * published by the Free Software Foundation.  Oracle designates this

*** 24,11 ***
   */
  
  package java.io;
  
  import java.io.ObjectInputFilter.Config;
! import java.io.ObjectStreamClass.RecordSupport;
  import java.lang.System.Logger;
  import java.lang.invoke.MethodHandle;
  import java.lang.reflect.Array;
  import java.lang.reflect.InvocationHandler;
  import java.lang.reflect.Modifier;
--- 24,11 ---
   */
  
  package java.io;
  
  import java.io.ObjectInputFilter.Config;
! import java.io.ObjectStreamClass.AlternativeDeserialization;
  import java.lang.System.Logger;
  import java.lang.invoke.MethodHandle;
  import java.lang.reflect.Array;
  import java.lang.reflect.InvocationHandler;
  import java.lang.reflect.Modifier;

*** 233,10 ***
--- 233,21 ---
   * as readObject and writeObject, are ignored for serializable records. See
   * <a href="{@docRoot}/../specs/serialization/serial-arch.html#serialization-of-records">
   * <cite>Java Object Serialization Specification,</cite> Section 1.13,
   * "Serialization of Records"</a> for additional information.
   *
+  * <div class="preview-block">
+  *      <div class="preview-comment">
+  *          <p>{@linkplain Class#isValue Value classes} that are not records cannot be
+  *          deserialized directly. To serialize an instance of a value class, a proxy
+  *          object should be used instead. That object can then implement
+  *          <a href="{@docRoot}/../specs/serialization/input.html#the-readresolve-method">
+  *          {@code readResolve}</a> to construct and return the expected value class
+  *          instance.
+  *      </div>
+  * </div>
+  *
   * @spec serialization/index.html Java Object Serialization Specification
   * @author      Mike Warres
   * @author      Roger Riggs
   * @see java.io.DataInput
   * @see java.io.ObjectOutputStream

*** 428,10 ***
--- 439,21 ---
       * <p>Exceptions are thrown for problems with the InputStream and for
       * classes that should not be deserialized.  All exceptions are fatal to
       * the InputStream and leave it in an indeterminate state; it is up to the
       * caller to ignore or recover the stream state.
       *
+      * <div class="preview-block">
+      *      <div class="preview-comment">
+      *          <p>An object in the stream that instantiates a concrete
+      *          {@linkplain Class#isValue value class}, or that extends a
+      *          Serializable abstract value class that declares instance fields,
+      *          can only be deserialized if it is a record or a boxed primitive
+      *          value. Otherwise, {@code readObject} throws an
+      *          {@code InvalidClassException}.
+      *      </div>
+      * </div>
+      *
       * @throws  ClassNotFoundException Class of a serialized object cannot be
       *          found.
       * @throws  InvalidClassException Something is wrong with a class used by
       *          deserialization.
       * @throws  StreamCorruptedException Control information in the

*** 2120,13 ***
          if (resolveEx != null) {
              handles.markException(passHandle, resolveEx);
          }
  
          final boolean isRecord = desc.isRecord();
!         if (isRecord) {
              assert obj == null;
!             obj = readRecord(desc);
              if (!unshared)
                  handles.setObject(passHandle, obj);
          } else if (desc.isExternalizable()) {
              readExternalData((Externalizable) obj, desc);
          } else {
--- 2142,13 ---
          if (resolveEx != null) {
              handles.markException(passHandle, resolveEx);
          }
  
          final boolean isRecord = desc.isRecord();
!         if (isRecord || desc.hasDeserializer()) {
              assert obj == null;
!             obj = readAlternative(desc);
              if (!unshared)
                  handles.setObject(passHandle, obj);
          } else if (desc.isExternalizable()) {
              readExternalData((Externalizable) obj, desc);
          } else {

*** 2212,17 ***
           * most likely throw a StreamCorruptedException.
           */
      }
  
      /**
!      * Reads and returns a record.
       * If an exception is marked for any of the fields, the dependency
!      * mechanism marks the record as having an exception.
!      * Null is returned from readRecord and later the exception is thrown at
       * the exit of {@link #readObject(Class)}.
       */
!     private Object readRecord(ObjectStreamClass desc) throws IOException {
          ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
          if (slots.length != 1) {
              // skip any superclass stream field values
              for (int i = 0; i < slots.length-1; i++) {
                  if (slots[i].hasData) {
--- 2234,18 ---
           * most likely throw a StreamCorruptedException.
           */
      }
  
      /**
!      * Reads and returns an alternatively-deserialized object, such as a record
+      * or a wrapper class as a value class.
       * If an exception is marked for any of the fields, the dependency
!      * mechanism marks the object as having an exception.
!      * Null is returned from readFromFactory and later the exception is thrown at
       * the exit of {@link #readObject(Class)}.
       */
!     private Object readAlternative(ObjectStreamClass desc) throws IOException {
          ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
          if (slots.length != 1) {
              // skip any superclass stream field values
              for (int i = 0; i < slots.length-1; i++) {
                  if (slots[i].hasData) {

*** 2231,21 ***
              }
          }
  
          FieldValues fieldValues = new FieldValues(desc, true);
          if (handles.lookupException(passHandle) != null) {
!             return null;     // slot marked with exception, don't create record
          }
  
!         // get canonical record constructor adapted to take two arguments:
          // - byte[] primValues
          // - Object[] objValues
          // and return Object
!         MethodHandle ctrMH = RecordSupport.deserializationCtr(desc);
  
          try {
!             return (Object) ctrMH.invokeExact(fieldValues.primValues, fieldValues.objValues);
          } catch (Exception e) {
              throw new InvalidObjectException(e.getMessage(), e);
          } catch (Error e) {
              throw e;
          } catch (Throwable t) {
--- 2254,21 ---
              }
          }
  
          FieldValues fieldValues = new FieldValues(desc, true);
          if (handles.lookupException(passHandle) != null) {
!             return null;     // slot marked with exception, don't create object
          }
  
!         // get the factory adapted to take two arguments:
          // - byte[] primValues
          // - Object[] objValues
          // and return Object
!         MethodHandle factoryMH = AlternativeDeserialization.getFactory(desc);
  
          try {
!             return (Object) factoryMH.invokeExact(fieldValues.primValues, fieldValues.objValues);
          } catch (Exception e) {
              throw new InvalidObjectException(e.getMessage(), e);
          } catch (Error e) {
              throw e;
          } catch (Throwable t) {
< prev index next >