diff a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java
@@ -26,11 +26,10 @@
 package com.sun.tools.javac.comp;
 
 import java.util.*;
 import java.util.function.BiConsumer;
 import java.util.function.BiPredicate;
-import java.util.function.Consumer;
 import java.util.function.Predicate;
 import java.util.function.Supplier;
 import java.util.function.ToIntBiFunction;
 import java.util.stream.Collectors;
 import java.util.stream.StreamSupport;
@@ -77,15 +76,12 @@
 import static com.sun.tools.javac.code.TypeTag.*;
 import static com.sun.tools.javac.code.TypeTag.WILDCARD;
 
 import static com.sun.tools.javac.tree.JCTree.Tag.*;
 import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
 import javax.lang.model.element.TypeElement;
 import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.ElementFilter;
 import javax.lang.model.util.ElementKindVisitor14;
 
 /** Type checking helper class for the attribution phase.
  *
  *  <p><b>This is NOT part of any supported API.
@@ -182,10 +178,12 @@
         deferredLintHandler = DeferredLintHandler.instance(context);
 
         allowModules = Feature.MODULES.allowedInSource(source);
         allowRecords = Feature.RECORDS.allowedInSource(source);
         allowSealed = Feature.SEALED_CLASSES.allowedInSource(source);
+        allowValueClasses = (!preview.isPreview(Feature.VALUE_CLASSES) || preview.isEnabled()) &&
+                Feature.VALUE_CLASSES.allowedInSource(source);
     }
 
     /** Character for synthetic names
      */
     char syntheticNameChar;
@@ -225,10 +223,14 @@
 
     /** Are sealed classes allowed
      */
     private final boolean allowSealed;
 
+    /** Are value classes allowed
+     */
+    private final boolean allowValueClasses;
+
 /* *************************************************************************
  * Errors and Warnings
  **************************************************************************/
 
     Lint setLint(Lint newLint) {
@@ -758,10 +760,35 @@
             return (t.hasTag(TYPEVAR))
                                     ? diags.fragment(Fragments.TypeParameter(t))
                                     : t;
         }
 
+    void checkConstraintsOfValueClass(JCClassDecl tree, ClassSymbol c) {
+        DiagnosticPosition pos = tree.pos();
+        for (Type st : types.closure(c.type)) {
+            if (st == null || st.tsym == null || st.tsym.kind == ERR)
+                continue;
+            if  (st.tsym == syms.objectType.tsym || st.tsym == syms.recordType.tsym || st.isInterface())
+                continue;
+            if (!st.tsym.isAbstract()) {
+                if (c != st.tsym) {
+                    log.error(pos, Errors.ConcreteSupertypeForValueClass(c, st));
+                }
+                continue;
+            }
+            // dealing with an abstract value or value super class below.
+            for (Symbol s : st.tsym.members().getSymbols(NON_RECURSIVE)) {
+                if (s.kind == MTH) {
+                    if ((s.flags() & (SYNCHRONIZED | STATIC)) == SYNCHRONIZED) {
+                        log.error(pos, Errors.SuperClassMethodCannotBeSynchronized(s, c, st));
+                    }
+                    break;
+                }
+            }
+        }
+    }
+
     /** Check that type is a valid qualifier for a constructor reference expression
      */
     Type checkConstructorRefType(DiagnosticPosition pos, Type t) {
         t = checkClassOrArrayType(pos, t);
         if (t.hasTag(CLASS)) {
@@ -815,10 +842,36 @@
             return typeTagError(pos,
                                 diags.fragment(Fragments.TypeReqRef),
                                 t);
     }
 
+    /** Check that type is an identity type, i.e. not a value type.
+     *  When not discernible statically, give it the benefit of doubt
+     *  and defer to runtime.
+     *
+     *  @param pos           Position to be used for error reporting.
+     *  @param t             The type to be checked.
+     */
+    boolean checkIdentityType(DiagnosticPosition pos, Type t) {
+        if (t.hasTag(TYPEVAR)) {
+            t = types.skipTypeVars(t, false);
+        }
+        if (t.isIntersection()) {
+            IntersectionClassType ict = (IntersectionClassType)t;
+            boolean result = true;
+            for (Type component : ict.getExplicitComponents()) {
+                result &= checkIdentityType(pos, component);
+            }
+            return result;
+        }
+        if (t.isPrimitive() || (t.isValueClass() && !t.tsym.isAbstract())) {
+            typeTagError(pos, diags.fragment(Fragments.TypeReqIdentity), t);
+            return false;
+        }
+        return true;
+    }
+
     /** Check that each type is a reference type, i.e. a class, interface or array type
      *  or a type variable.
      *  @param trees         Original trees, used for error reporting.
      *  @param types         The types to be checked.
      */
@@ -1205,12 +1258,17 @@
                 mask = ReceiverParamFlags;
             else if (sym.owner.kind != TYP)
                 mask = LocalVarFlags;
             else if ((sym.owner.flags_field & INTERFACE) != 0)
                 mask = implicit = InterfaceVarFlags;
-            else
-                mask = VarFlags;
+            else {
+                boolean isInstanceFieldOfValueClass = sym.owner.type.isValueClass() && (flags & STATIC) == 0;
+                mask = !isInstanceFieldOfValueClass ? VarFlags : ValueFieldFlags;
+                if (isInstanceFieldOfValueClass) {
+                    implicit |= FINAL | STRICT;
+                }
+            }
             break;
         case MTH:
             if (sym.name == names.init) {
                 if ((sym.owner.flags_field & ENUM) != 0) {
                     // enum constructors cannot be declared public or
@@ -1232,13 +1290,16 @@
                     }
                 } else {
                     mask = implicit = InterfaceMethodFlags;
                 }
             } else if ((sym.owner.flags_field & RECORD) != 0) {
-                mask = RecordMethodFlags;
+                mask = ((sym.owner.flags_field & VALUE_CLASS) != 0 && (flags & Flags.STATIC) == 0) ?
+                        RecordMethodFlags & ~SYNCHRONIZED : RecordMethodFlags;
             } else {
-                mask = MethodFlags;
+                // value objects do not have an associated monitor/lock
+                mask = ((sym.owner.flags_field & VALUE_CLASS) != 0 && (flags & Flags.STATIC) == 0) ?
+                        MethodFlags & ~SYNCHRONIZED : MethodFlags;
             }
             if ((flags & STRICTFP) != 0) {
                 warnOnExplicitStrictfp(pos);
             }
             // Imply STRICTFP if owner has STRICTFP set.
@@ -1251,11 +1312,11 @@
                     (sym.isDirectlyOrIndirectlyLocal() && (flags & ANNOTATION) != 0)) {
                 boolean implicitlyStatic = !sym.isAnonymous() &&
                         ((flags & RECORD) != 0 || (flags & ENUM) != 0 || (flags & INTERFACE) != 0);
                 boolean staticOrImplicitlyStatic = (flags & STATIC) != 0 || implicitlyStatic;
                 // local statics are allowed only if records are allowed too
-                mask = staticOrImplicitlyStatic && allowRecords && (flags & ANNOTATION) == 0 ? StaticLocalFlags : LocalClassFlags;
+                mask = staticOrImplicitlyStatic && allowRecords && (flags & ANNOTATION) == 0 ? ExtendedStaticLocalClassFlags : ExtendedLocalClassFlags;
                 implicit = implicitlyStatic ? STATIC : implicit;
             } else if (sym.owner.kind == TYP) {
                 // statics in inner classes are allowed only if records are allowed too
                 mask = ((flags & STATIC) != 0) && allowRecords && (flags & ANNOTATION) == 0 ? ExtendedMemberStaticClassFlags : ExtendedMemberClassFlags;
                 if (sym.owner.owner.kind == PCK ||
@@ -1267,16 +1328,24 @@
                 // Nested interfaces and enums are always STATIC (Spec ???)
                 if ((flags & (INTERFACE | ENUM | RECORD)) != 0 ) implicit = STATIC;
             } else {
                 mask = ExtendedClassFlags;
             }
+            if ((flags & (VALUE_CLASS | SEALED | ABSTRACT)) == (VALUE_CLASS | SEALED) ||
+                (flags & (VALUE_CLASS | NON_SEALED | ABSTRACT)) == (VALUE_CLASS | NON_SEALED)) {
+                log.error(pos, Errors.NonAbstractValueClassCantBeSealedOrNonSealed);
+            }
             // Interfaces are always ABSTRACT
             if ((flags & INTERFACE) != 0) implicit |= ABSTRACT;
 
+            if ((flags & (INTERFACE | VALUE_CLASS)) == 0) {
+                implicit |= IDENTITY_TYPE;
+            }
+
             if ((flags & ENUM) != 0) {
-                // enums can't be declared abstract, final, sealed or non-sealed
-                mask &= ~(ABSTRACT | FINAL | SEALED | NON_SEALED);
+                // enums can't be declared abstract, final, sealed or non-sealed or value
+                mask &= ~(ABSTRACT | FINAL | SEALED | NON_SEALED | VALUE_CLASS);
                 implicit |= implicitEnumFinalFlag(tree);
             }
             if ((flags & RECORD) != 0) {
                 // records can't be declared abstract
                 mask &= ~ABSTRACT;
@@ -1285,10 +1354,15 @@
             if ((flags & STRICTFP) != 0) {
                 warnOnExplicitStrictfp(pos);
             }
             // Imply STRICTFP if owner has STRICTFP set.
             implicit |= sym.owner.flags_field & STRICTFP;
+
+            // concrete value classes are implicitly final
+            if ((flags & (ABSTRACT | INTERFACE | VALUE_CLASS)) == VALUE_CLASS) {
+                implicit |= FINAL;
+            }
             break;
         default:
             throw new AssertionError();
         }
         long illegal = flags & ExtendedStandardFlags & ~mask;
@@ -1299,12 +1373,11 @@
             }
             else {
                 log.error(pos,
                         Errors.ModNotAllowedHere(asFlagSet(illegal)));
             }
-        }
-        else if ((sym.kind == TYP ||
+        } else if ((sym.kind == TYP ||
                   // ISSUE: Disallowing abstract&private is no longer appropriate
                   // in the presence of inner classes. Should it be deleted here?
                   checkDisjoint(pos, flags,
                                 ABSTRACT,
                                 PRIVATE | STATIC | DEFAULT))
@@ -1323,11 +1396,12 @@
                  &&
                  checkDisjoint(pos, flags,
                                PRIVATE,
                                PUBLIC | PROTECTED)
                  &&
-                 checkDisjoint(pos, flags,
+                 // we are using `implicit` here as instance fields of value classes are implicitly final
+                 checkDisjoint(pos, flags | implicit,
                                FINAL,
                                VOLATILE)
                  &&
                  (sym.kind == TYP ||
                   checkDisjoint(pos, flags,
@@ -1339,11 +1413,17 @@
                  && checkDisjoint(pos, flags,
                                 SEALED,
                            FINAL | NON_SEALED)
                  && checkDisjoint(pos, flags,
                                 SEALED,
-                                ANNOTATION)) {
+                                ANNOTATION)
+                && checkDisjoint(pos, flags,
+                                VALUE_CLASS,
+                                ANNOTATION)
+                && checkDisjoint(pos, flags,
+                                VALUE_CLASS,
+                                INTERFACE) ) {
             // skip
         }
         return flags & (mask | ~ExtendedStandardFlags) | implicit;
     }
 
@@ -2156,10 +2236,15 @@
             if (m.overrides(syms.enumFinalFinalize, origin, types, false)) {
                 log.error(tree.pos(), Errors.EnumNoFinalize);
                 return;
             }
         }
+        if (allowValueClasses && origin.isValueClass() && names.finalize.equals(m.name)) {
+            if (m.overrides(syms.objectFinalize, origin, types, false)) {
+                log.warning(tree.pos(), Warnings.ValueFinalize);
+            }
+        }
         if (allowRecords && origin.isRecord()) {
             // let's find out if this is a user defined accessor in which case the @Override annotation is acceptable
             Optional<? extends RecordComponent> recordComponent = origin.getRecordComponents().stream()
                     .filter(rc -> rc.accessor == tree.sym && (rc.accessor.flags_field & GENERATED_MEMBER) == 0).findFirst();
             if (recordComponent.isPresent()) {
@@ -2584,10 +2669,22 @@
             for (List<Type> m = supertypes; m != l; m = m.tail)
                 if (!checkCompatibleAbstracts(pos, l.head, m.head, c))
                     return;
         }
         checkCompatibleConcretes(pos, c);
+
+        Type identitySuper = null;
+        for (Type t : types.closure(c)) {
+            if (t != c) {
+                if (t.isIdentityClass() && (t.tsym.flags() & VALUE_BASED) == 0)
+                    identitySuper = t;
+                if (c.isValueClass() && identitySuper != null && identitySuper.tsym != syms.objectType.tsym) { // Object is special
+                    log.error(pos, Errors.ValueTypeHasIdentitySuperType(c, identitySuper));
+                    break;
+                }
+            }
+        }
     }
 
     /** Check that all non-override equivalent methods accessible from 'site'
      *  are mutually compatible (JLS 8.4.8/9.4.1).
      *
@@ -4896,12 +4993,12 @@
     }
 
     /**
      * Check structure of serialization declarations.
      */
-    public void checkSerialStructure(JCClassDecl tree, ClassSymbol c) {
-        (new SerialTypeVisitor()).visit(c, tree);
+    public void checkSerialStructure(Env<AttrContext> env, JCClassDecl tree, ClassSymbol c) {
+        (new SerialTypeVisitor(env)).visit(c, tree);
     }
 
     /**
      * This visitor will warn if a serialization-related field or
      * method is declared in a suspicious or incorrect way. In
@@ -4928,12 +5025,14 @@
      * Externalizable: methods defined on the interface
      * public void writeExternal(ObjectOutput) throws IOException
      * public void readExternal(ObjectInput) throws IOException
      */
     private class SerialTypeVisitor extends ElementKindVisitor14<Void, JCClassDecl> {
-        SerialTypeVisitor() {
+        Env<AttrContext> env;
+        SerialTypeVisitor(Env<AttrContext> env) {
             this.lint = Check.this.lint;
+            this.env = env;
         }
 
         private static final Set<String> serialMethodNames =
             Set.of("writeObject", "writeReplace",
                    "readObject",  "readObjectNoData",
@@ -4989,10 +5088,11 @@
                      .iterator()
                      .hasNext();
 
             // Check declarations of serialization-related methods and
             // fields
+            final boolean[] hasWriteReplace = {false};
             for(Symbol el : c.getEnclosedElements()) {
                 runUnderLint(el, p, (enclosed, tree) -> {
                     String name = null;
                     switch(enclosed.getKind()) {
                     case FIELD -> {
@@ -5063,29 +5163,64 @@
                         var method = (MethodSymbol)enclosed;
                         name = method.getSimpleName().toString();
                         if (serialMethodNames.contains(name)) {
                             switch (name) {
                             case "writeObject"      -> checkWriteObject(tree, e, method);
-                            case "writeReplace"     -> checkWriteReplace(tree,e, method);
+                            case "writeReplace"     -> {hasWriteReplace[0] = true; hasAppropriateWriteReplace(tree, method, true);}
                             case "readObject"       -> checkReadObject(tree,e, method);
                             case "readObjectNoData" -> checkReadObjectNoData(tree, e, method);
                             case "readResolve"      -> checkReadResolve(tree, e, method);
                             default ->  throw new AssertionError();
                             }
                         }
                     }
                     }
                 });
             }
-
+            if (!hasWriteReplace[0] &&
+                    (c.isValueClass() || hasAbstractValueSuperClass(c, Set.of(syms.numberType.tsym))) &&
+                    !c.isAbstract() && !c.isRecord() &&
+                    types.unboxedType(c.type) == Type.noType) {
+                // we need to check if the class is inheriting an appropriate writeReplace method
+                MethodSymbol ms = null;
+                Log.DiagnosticHandler discardHandler = new Log.DiscardDiagnosticHandler(log);
+                try {
+                    ms = rs.resolveInternalMethod(env.tree, env, c.type, names.writeReplace, List.nil(), List.nil());
+                } catch (FatalError fe) {
+                    // ignore no method was found
+                } finally {
+                    log.popDiagnosticHandler(discardHandler);
+                }
+                if (ms == null || !hasAppropriateWriteReplace(p, ms, false)) {
+                    log.warning(LintCategory.SERIAL, p,
+                            c.isValueClass() ? Warnings.SerializableValueClassWithoutWriteReplace1 :
+                                    Warnings.SerializableValueClassWithoutWriteReplace2);
+                }
+            }
             return null;
         }
 
         boolean canBeSerialized(Type type) {
             return type.isPrimitive() || rs.isSerializable(type);
         }
 
+        private boolean hasAbstractValueSuperClass(Symbol c, Set<Symbol> excluding) {
+            while (c.getKind() == ElementKind.CLASS) {
+                Type sup = ((ClassSymbol)c).getSuperclass();
+                if (!sup.hasTag(CLASS) || sup.isErroneous() ||
+                        sup.tsym == syms.objectType.tsym) {
+                    return false;
+                }
+                // if it is a value super class it has to be abstract
+                if (sup.isValueClass() && !excluding.contains(sup.tsym)) {
+                    return true;
+                }
+                c = sup.tsym;
+            }
+            return false;
+        }
+
         /**
          * Check that Externalizable class needs a public no-arg
          * constructor.
          *
          * Check that a Serializable class has access to the no-arg
@@ -5205,61 +5340,61 @@
             // readObject and writeObject methods and is generally
             // innocuous.
 
             // private void writeObject(ObjectOutputStream stream) throws IOException
             checkPrivateNonStaticMethod(tree, method);
-            checkReturnType(tree, e, method, syms.voidType);
+            isExpectedReturnType(tree, method, syms.voidType, true);
             checkOneArg(tree, e, method, syms.objectOutputStreamType);
-            checkExceptions(tree, e, method, syms.ioExceptionType);
+            hasExpectedExceptions(tree, method, true, syms.ioExceptionType);
             checkExternalizable(tree, e, method);
         }
 
-        private void checkWriteReplace(JCClassDecl tree, Element e, MethodSymbol method) {
+        private boolean hasAppropriateWriteReplace(JCClassDecl tree, MethodSymbol method, boolean warn) {
             // ANY-ACCESS-MODIFIER Object writeReplace() throws
             // ObjectStreamException
 
             // Excluding abstract, could have a more complicated
             // rule based on abstract-ness of the class
-            checkConcreteInstanceMethod(tree, e, method);
-            checkReturnType(tree, e, method, syms.objectType);
-            checkNoArgs(tree, e, method);
-            checkExceptions(tree, e, method, syms.objectStreamExceptionType);
+            return isConcreteInstanceMethod(tree, method, warn) &&
+                    isExpectedReturnType(tree, method, syms.objectType, warn) &&
+                    hasNoArgs(tree, method, warn) &&
+                    hasExpectedExceptions(tree, method, warn, syms.objectStreamExceptionType);
         }
 
         private void checkReadObject(JCClassDecl tree, Element e, MethodSymbol method) {
             // The "synchronized" modifier is seen in the wild on
             // readObject and writeObject methods and is generally
             // innocuous.
 
             // private void readObject(ObjectInputStream stream)
             //   throws IOException, ClassNotFoundException
             checkPrivateNonStaticMethod(tree, method);
-            checkReturnType(tree, e, method, syms.voidType);
+            isExpectedReturnType(tree, method, syms.voidType, true);
             checkOneArg(tree, e, method, syms.objectInputStreamType);
-            checkExceptions(tree, e, method, syms.ioExceptionType, syms.classNotFoundExceptionType);
+            hasExpectedExceptions(tree, method, true, syms.ioExceptionType, syms.classNotFoundExceptionType);
             checkExternalizable(tree, e, method);
         }
 
         private void checkReadObjectNoData(JCClassDecl tree, Element e, MethodSymbol method) {
             // private void readObjectNoData() throws ObjectStreamException
             checkPrivateNonStaticMethod(tree, method);
-            checkReturnType(tree, e, method, syms.voidType);
-            checkNoArgs(tree, e, method);
-            checkExceptions(tree, e, method, syms.objectStreamExceptionType);
+            isExpectedReturnType(tree, method, syms.voidType, true);
+            hasNoArgs(tree, method, true);
+            hasExpectedExceptions(tree, method, true, syms.objectStreamExceptionType);
             checkExternalizable(tree, e, method);
         }
 
         private void checkReadResolve(JCClassDecl tree, Element e, MethodSymbol method) {
             // ANY-ACCESS-MODIFIER Object readResolve()
             // throws ObjectStreamException
 
             // Excluding abstract, could have a more complicated
             // rule based on abstract-ness of the class
-            checkConcreteInstanceMethod(tree, e, method);
-            checkReturnType(tree,e, method, syms.objectType);
-            checkNoArgs(tree, e, method);
-            checkExceptions(tree, e, method, syms.objectStreamExceptionType);
+            isConcreteInstanceMethod(tree, method, true);
+            isExpectedReturnType(tree, method, syms.objectType, true);
+            hasNoArgs(tree, method, true);
+            hasExpectedExceptions(tree, method, true, syms.objectStreamExceptionType);
         }
 
         private void checkWriteExternalRecord(JCClassDecl tree, Element e, MethodSymbol method, boolean isExtern) {
             //public void writeExternal(ObjectOutput) throws IOException
             checkExternMethodRecord(tree, e, method, syms.objectOutputType, isExtern);
@@ -5505,11 +5640,11 @@
                     }
 
                     case METHOD -> {
                         var method = (MethodSymbol)enclosed;
                         switch(name) {
-                        case "writeReplace" -> checkWriteReplace(tree, e, method);
+                        case "writeReplace" -> hasAppropriateWriteReplace(tree, method, true);
                         case "readResolve"  -> checkReadResolve(tree, e, method);
 
                         case "writeExternal" -> checkWriteExternalRecord(tree, e, method, isExtern);
                         case "readExternal"  -> checkReadExternalRecord(tree, e, method, isExtern);
 
@@ -5523,36 +5658,44 @@
                     }}});
             }
             return null;
         }
 
-        void checkConcreteInstanceMethod(JCClassDecl tree,
-                                         Element enclosing,
-                                         MethodSymbol method) {
+        boolean isConcreteInstanceMethod(JCClassDecl tree,
+                                         MethodSymbol method,
+                                         boolean warn) {
             if ((method.flags() & (STATIC | ABSTRACT)) != 0) {
+                if (warn) {
                     log.warning(LintCategory.SERIAL,
-                                TreeInfo.diagnosticPositionFor(method, tree),
-                                Warnings.SerialConcreteInstanceMethod(method.getSimpleName()));
+                            TreeInfo.diagnosticPositionFor(method, tree),
+                            Warnings.SerialConcreteInstanceMethod(method.getSimpleName()));
+                }
+                return false;
             }
+            return true;
         }
 
-        private void checkReturnType(JCClassDecl tree,
-                                     Element enclosing,
-                                     MethodSymbol method,
-                                     Type expectedReturnType) {
+        private boolean isExpectedReturnType(JCClassDecl tree,
+                                          MethodSymbol method,
+                                          Type expectedReturnType,
+                                          boolean warn) {
             // Note: there may be complications checking writeReplace
             // and readResolve since they return Object and could, in
             // principle, have covariant overrides and any synthetic
             // bridge method would not be represented here for
             // checking.
             Type rtype = method.getReturnType();
             if (!types.isSameType(expectedReturnType, rtype)) {
-                log.warning(LintCategory.SERIAL,
+                if (warn) {
+                    log.warning(LintCategory.SERIAL,
                             TreeInfo.diagnosticPositionFor(method, tree),
                             Warnings.SerialMethodUnexpectedReturnType(method.getSimpleName(),
-                                                                      rtype, expectedReturnType));
+                                    rtype, expectedReturnType));
+                }
+                return false;
             }
+            return true;
         }
 
         private void checkOneArg(JCClassDecl tree,
                                  Element enclosing,
                                  MethodSymbol method,
@@ -5586,17 +5729,21 @@
             return (parameters.size() == 1) &&
                 types.isSameType(parameters.get(0).asType(), expectedType);
         }
 
 
-        private void checkNoArgs(JCClassDecl tree, Element enclosing, MethodSymbol method) {
+        boolean hasNoArgs(JCClassDecl tree, MethodSymbol method, boolean warn) {
             var parameters = method.getParameters();
             if (!parameters.isEmpty()) {
-                log.warning(LintCategory.SERIAL,
+                if (warn) {
+                    log.warning(LintCategory.SERIAL,
                             TreeInfo.diagnosticPositionFor(parameters.get(0), tree),
                             Warnings.SerialMethodNoArgs(method.getSimpleName()));
+                }
+                return false;
             }
+            return true;
         }
 
         private void checkExternalizable(JCClassDecl tree, Element enclosing, MethodSymbol method) {
             // If the enclosing class is externalizable, warn for the method
             if (isExternalizable((Type)enclosing.asType())) {
@@ -5605,14 +5752,14 @@
                             Warnings.IneffectualSerialMethodExternalizable(method.getSimpleName()));
             }
             return;
         }
 
-        private void checkExceptions(JCClassDecl tree,
-                                     Element enclosing,
-                                     MethodSymbol method,
-                                     Type... declaredExceptions) {
+        private boolean hasExpectedExceptions(JCClassDecl tree,
+                                              MethodSymbol method,
+                                              boolean warn,
+                                              Type... declaredExceptions) {
             for (Type thrownType: method.getThrownTypes()) {
                 // For each exception in the throws clause of the
                 // method, if not an Error and not a RuntimeException,
                 // check if the exception is a subtype of a declared
                 // exception from the throws clause of the
@@ -5627,18 +5774,21 @@
                             declared = true;
                             continue;
                         }
                     }
                     if (!declared) {
-                        log.warning(LintCategory.SERIAL,
+                        if (warn) {
+                            log.warning(LintCategory.SERIAL,
                                     TreeInfo.diagnosticPositionFor(method, tree),
                                     Warnings.SerialMethodUnexpectedException(method.getSimpleName(),
-                                                                             thrownType));
+                                            thrownType));
+                        }
+                        return false;
                     }
                 }
             }
-            return;
+            return true;
         }
 
         private <E extends Element> Void runUnderLint(E symbol, JCClassDecl p, BiConsumer<E, JCClassDecl> task) {
             Lint prevLint = lint;
             try {
