diff a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java
--- a/src/java.base/share/classes/java/lang/Class.java
+++ b/src/java.base/share/classes/java/lang/Class.java
@@ -37,10 +37,11 @@
 import java.io.ObjectStreamField;
 import java.lang.reflect.AnnotatedElement;
 import java.lang.reflect.AnnotatedType;
 import java.lang.reflect.AccessFlag;
 import java.lang.reflect.Array;
+import java.lang.reflect.ClassFileFormatVersion;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Executable;
 import java.lang.reflect.Field;
 import java.lang.reflect.GenericArrayType;
 import java.lang.reflect.GenericDeclaration;
@@ -219,13 +220,13 @@
                               GenericDeclaration,
                               Type,
                               AnnotatedElement,
                               TypeDescriptor.OfField<Class<?>>,
                               Constable {
-    private static final int ANNOTATION= 0x00002000;
-    private static final int ENUM      = 0x00004000;
-    private static final int SYNTHETIC = 0x00001000;
+    private static final int ANNOTATION = 0x00002000;
+    private static final int ENUM       = 0x00004000;
+    private static final int SYNTHETIC  = 0x00001000;
 
     private static native void registerNatives();
     static {
         registerNatives();
     }
@@ -308,10 +309,12 @@
                 } while (component.isArray());
                 sb.append(component.getName());
             } else {
                 // Class modifiers are a superset of interface modifiers
                 int modifiers = getModifiers() & Modifier.classModifiers();
+                // Modifier.toString() below mis-interprets SYNCHRONIZED, STRICT, and VOLATILE bits
+                modifiers &= ~(Modifier.SYNCHRONIZED | Modifier.STRICT | Modifier.VOLATILE);
                 if (modifiers != 0) {
                     sb.append(Modifier.toString(modifiers));
                     sb.append(' ');
                 }
 
@@ -321,10 +324,13 @@
                 addSealingInfo(modifiers, sb);
 
                 if (isAnnotation()) {
                     sb.append('@');
                 }
+                if (isValue()) {
+                    sb.append("value ");
+                }
                 if (isInterface()) { // Note: all annotation interfaces are interfaces
                     sb.append("interface");
                 } else {
                     if (isEnum())
                         sb.append("enum");
@@ -670,10 +676,43 @@
         } else {
             return BootLoader.loadClass(module, name);
         }
     }
 
+    /**
+     * {@return {@code true} if this {@code Class} object represents an identity
+     * class or interface; otherwise {@code false}}
+     *
+     * If this {@code Class} object represents an array type, then this method
+     * returns {@code true}.
+     * If this {@code Class} object represents a primitive type, or {@code void},
+     * then this method returns {@code false}.
+     *
+     * @since Valhalla
+     */
+    @PreviewFeature(feature = PreviewFeature.Feature.VALUE_OBJECTS, reflective=true)
+    public native boolean isIdentity();
+
+    /**
+     * {@return {@code true} if this {@code Class} object represents a value
+     * class; otherwise {@code false}}
+     *
+     * If this {@code Class} object represents an array type, an interface,
+     * a primitive type, or {@code void}, then this method returns {@code false}.
+     *
+     * @since Valhalla
+     */
+    @PreviewFeature(feature = PreviewFeature.Feature.VALUE_OBJECTS, reflective=true)
+    public boolean isValue() {
+        if (!PreviewFeatures.isEnabled()) {
+            return false;
+        }
+         if (isPrimitive() || isArray() || isInterface())
+             return false;
+        return ((getModifiers() & Modifier.IDENTITY) == 0);
+    }
+
     /**
      * {@return the {@code Class} object associated with the
      * {@linkplain #isPrimitive() primitive type} of the given name}
      * If the argument is not the name of a primitive type, {@code
      * null} is returned.
@@ -1436,10 +1475,11 @@
      *      modifiers are the same as those of its component type
      * <li> its {@code abstract} and {@code final} modifiers are always
      *      {@code true}
      * <li> its interface modifier is always {@code false}, even when
      *      the component type is an interface
+     * <li> its {@code identity} modifier is always true
      * </ul>
      * If this {@code Class} object represents a primitive type or
      * void, its {@code public}, {@code abstract}, and {@code final}
      * modifiers are always {@code true}.
      * For {@code Class} objects representing void, primitive types, and
@@ -1461,21 +1501,23 @@
      * @jvms 4.1 The {@code ClassFile} Structure
      */
     @IntrinsicCandidate
     public native int getModifiers();
 
-    /**
+   /**
      * {@return an unmodifiable set of the {@linkplain AccessFlag access
      * flags} for this class, possibly empty}
+     * The {@code AccessFlags} may depend on the class file format version of the class.
      *
      * <p> If the underlying class is an array class:
      * <ul>
      * <li> its {@code PUBLIC}, {@code PRIVATE} and {@code PROTECTED}
      *      access flags are the same as those of its component type
      * <li> its {@code ABSTRACT} and {@code FINAL} flags are present
      * <li> its {@code INTERFACE} flag is absent, even when the
      *      component type is an interface
+    * <li> its {@code identity} modifier is always true
      * </ul>
      * If this {@code Class} object represents a primitive type or
      * void, the flags are {@code PUBLIC}, {@code ABSTRACT}, and
      * {@code FINAL}.
      * For {@code Class} objects representing void, primitive types, and
@@ -1493,33 +1535,38 @@
         // Use getClassAccessFlagsRaw to expose SUPER status.
         var location = (isMemberClass() || isLocalClass() ||
                         isAnonymousClass() || isArray()) ?
             AccessFlag.Location.INNER_CLASS :
             AccessFlag.Location.CLASS;
-        return AccessFlag.maskToAccessFlags((location == AccessFlag.Location.CLASS) ?
-                                            getClassAccessFlagsRaw() :
-                                            getModifiers(),
-                                            location);
+        int accessFlags = (location == AccessFlag.Location.CLASS) ?
+                getClassAccessFlagsRaw() : getModifiers();
+        if (isArray() && PreviewFeatures.isEnabled()) {
+            accessFlags |= Modifier.IDENTITY;
+        }
+        var cffv = ClassFileFormatVersion.fromMajor(getClassFileVersion() & 0xffff);
+        if (cffv.compareTo(ClassFileFormatVersion.latest()) >= 0) {
+            // Ignore unspecified (0x0800) access flag for current version
+            accessFlags &= ~0x0800;
+        }
+        return AccessFlag.maskToAccessFlags(accessFlags, location, cffv);
     }
 
-    /**
+   /**
      * Gets the signers of this class.
      *
      * @return  the signers of this class, or null if there are no signers.  In
      *          particular, this method returns null if this {@code Class} object represents
      *          a primitive type or void.
      * @since   1.1
      */
     public native Object[] getSigners();
 
-
     /**
      * Set the signers of this class.
      */
     native void setSigners(Object[] signers);
 
-
     /**
      * If this {@code Class} object represents a local or anonymous
      * class within a method, returns a {@link
      * java.lang.reflect.Method Method} object representing the
      * immediately enclosing method of the underlying class. Returns
@@ -4428,11 +4475,11 @@
      *
      * @return an array representing the superinterfaces
      * @since 1.8
      */
     public AnnotatedType[] getAnnotatedInterfaces() {
-         return TypeAnnotationParser.buildAnnotatedInterfaces(getRawTypeAnnotations(), getConstantPool(), this);
+        return TypeAnnotationParser.buildAnnotatedInterfaces(getRawTypeAnnotations(), getConstantPool(), this);
     }
 
     private native Class<?> getNestHost0();
 
     /**
@@ -4829,11 +4876,12 @@
      *
      * If the class is an array type then the class file version of its element
      * type is returned.  If the class is a primitive type then the latest class
      * file major version is returned and zero is returned for the minor version.
      */
-    private int getClassFileVersion() {
+    /* package-private */
+    int getClassFileVersion() {
         Class<?> c = isArray() ? elementType() : this;
         return c.getClassFileVersion0();
     }
 
     private native int getClassFileVersion0();
