Why is this an issue?

In Records, serialization is not done the same way as for ordinary serializable or externalizable classes. The serialized representation of a record object will be a sequence of values (record components). During the deserialization of records, the stream of components is read and components are constructed. Then the record object is recreated by invoking the record’s canonical constructor with the component values serving as arguments (or default values for absent arguments).

This process cannot be customized, so any class-specific writeObject, readObject, readObjectNoData, writeExternal, and readExternal methods or serialPersistentFields fields in record classes are ignored during serialization and deserialization.

However, there is a way to substitute serialized/deserialized objects in writeReplace and readResolve.

This rule raises an issue when any of writeObject, readObject, readObjectNoData, writeExternal, readExternal or serialPersistentFields are present as members in a Record class.

Noncompliant code example

record Record() implements Serializable {
  @Serial
  private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0]; // Noncompliant
  @Serial
  private void writeObject(ObjectOutputStream out) throws IOException { // Noncompliant
    ...
  }
}
record Record() implements Externalizable {
  @Override
  public void writeExternal(ObjectOutput out) throws IOException { // Noncompliant
    ...
  }
  @Override
  public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { // Noncompliant
    ...
  }
}

Compliant solution

record Record() implements Serializable {}

record Record() implements Serializable {
  private Object writeReplace() throws ObjectStreamException {
    ...
  }
  private Object readResolve() throws ObjectStreamException {
    ...
  }
}

Resources