Summary
Expand javac
's Xlint:serial
checking from checking the declaration of serialVersionUID
fields to checking the declarations of all serialization-related fields and methods.
Problem
Mis-declaring serialization-related fields and methods can lead to runtime exceptions and silent failures.
Solution
Add checks for all the serialization and externalization related fields and methods:
Serialization-related methods:
private void writeObject(java.io.ObjectOutputStream stream) throws IOException;
private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException;
private void readObjectNoData() throws ObjectStreamException;
ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;
ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
Serialization-related fields:
private static final long serialVersionUID
private static final ObjectStreamField[] serialPersistentFields
The checks are triggered when a field or method in a serializable type has a name matching one of the serialization-related fields or methods, respectively.
Additional checks are done for Externalizable types as some serialization-related methods are ineffectual there.
The semantics of serialization are covered in the "Java Object Serialization Specification" (https://docs.oracle.com/en/java/javase/17/docs/specs/serialization/index.html), abbreviated as JOSS in the remainder of this CSR, as well as in portions of the core library specification, including the classes java.io.ObjectOutputStream
and java.io.ObjectInputStream
.
The overall objective is to warn for cases where declarations cause the runtime serialization mechanism to silently ignore a mis-declared entity, rendering it ineffectual. Also, the checks include several compile-time patterns that could lead to runtime failures.
The checks are specialized for each of serializable class
es, interface
s, enum
classes, and record
classes. (Per the JLS annotation interfaces are not serializable.)
For fields and methods in Serializable classes (not enum
s or record
s), the checks make sure:
- mandatory modifiers are present (e.g.
private
onreadObject
andwriteObject
) - modifiers that should not be present are absent (e.g.
static
onreadObject
andwriteObject
; those methods must be instance methods) - for fields, the field is declared with the proper type
- for methods, the return type, number and of type of parameters match and the throws clause is compatible (methods are not declared to throw any checked exceptions that are not subtypes of those thrown by the canonical method's signature)
Existing checks that the serialVersionUID
field is declared static final
and of the right type, etc. are preserved.
If serialPersistentFields
is initialized to a literal null, a distinct warning is issued since a null serialPersistentFields
field is ignored, JOSS 1.5.
A serializable class is checked that it has access to a no-arg constructor in the first non-serializable class up its superclass chain (required in JOSS section 1.10). Note that java.lang.Object
is not serializable and has a public non-arg constructor so the chain will terminate. An externalizable class is checked to have a public no-arg constructor (JOSS section 1.11).
Warnings are issued if an externalizable class has a serialPersistentFields
field, writeObject
method, readObject
method, or readObjectNoData
method; those declarations are ineffectual for an externalizable class (JOSS 1.11 "An Externalizable class can optionally define the following methods: ... A writeReplace method to allow a class to nominate a replacement object to be written to the stream ... A readResolve method to allow a class to designate a replacement object for the object just read from the stream").
For enum
classes, since the serialization spec states the five serialization-related methods and two fields are all ignored (JOSS 1.12), the presence of any of those in an enum
class will generate a warning.
For record
classes, from JOSS 1.13 "any class-specific writeObject, readObject, readObjectNoData, writeExternal, and readExternal methods defined by record classes are ignored during serialization and deserialization." Therefore, warnings are generated for the ineffectual declarations that serialization would ignore.
For interface
s, if a public
readObject
, readObjectNoData
, or writeObject
method is defined, a warning is issued since a class implementing the interface will be unable to declare private
versions of those methods and the methods must be private to be effective. If an interface defines default writeReplace
or readResolve
methods, a warning will be issued since serialization only looks up the super*class* chain for those methods and not for default methods from interfaces. Since a serialPersistentFields
field must be private
to be effective, the presence of such a (non-private
) field an in interface generates a warning. The presence of a serialVersionUID
field does not generate a warning, but the declaration is checked to conform to the field's requirements to be effective, having the proper type, etc. There are limited circumstances where the serialVersionUID
of an interface are used.
Specification
The behavior in the Solution section above is intended to be a full and accurate description of the expansion in -Xlint:serial
checking. There is not a specification document per se for the lint behavior, other than summary statements in the javac
help messages and similar usage documentation.
The description of -Xlint:serial
is updated as follows:
- Warn about Serializable classes that do not provide a serial version ID. \n\
-\ Also warn about access to non-public members from a serializable element.
+ Warn about Serializable classes that do not have a serialVersionUID field. \n\
+\ Also warn about other suspect declarations in Serializable and Externalizable classes and interfaces.
- csr of
-
JDK-8202056 Expand serial warning to check for bad overloads of serial-related methods and ineffectual fields
- Resolved
- relates to
-
JDK-8275338 Add JFR events for notable serialization situations
- Resolved