This document describes changes to the Java Virtual Machine Specification to support sealed classes and interfaces, a preview feature of Java SE 16. A sealed class or interface restricts extension and implementation to an enumerated set of authorized subclasses or subinterfaces. See the JEP for an overview of the feature.
Section 4.7 specifies a new PermittedSubclasses attribute.
Section 5.3.5 specifies the checks performed at class derivation time in order to enforce the PermittedSubclasses attribute.
A number of revisions to 5.3 are also included in order to more accurately specify how the new class creation checks interact with the existing behavior of the reference implementation.
Changes are described with respect to existing sections of the JVM Specification. New text is indicated like this and deleted text is indicated like this. Explanation and discussion, as needed, is set aside in grey boxes.
Chapter 4: The class File Format
4.7 Attributes
Attributes are used in the ClassFile, field_info, method_info, and Code_attribute structures of the class file format (4.1, 4.5, 4.6, 4.7.3).
All attributes have the following general format:
attribute_info {
u2 attribute_name_index;
u4 attribute_length;
u1 info[attribute_length];
}
For all attributes, the attribute_name_index item must be a valid unsigned 16-bit index into the constant pool of the class. The constant_pool entry at attribute_name_index must be a CONSTANT_Utf8_info structure (4.4.7) representing the name of the attribute. The value of the attribute_length item indicates the length of the subsequent information in bytes. The length does not include the initial six bytes that contain the attribute_name_index and attribute_length items.
28 29 attributes are predefined by this specification. They are listed three times, for ease of navigation:
Table 4.7-A is ordered by the attributes' section numbers in this chapter. Each attribute is shown with the first version of the
classfile format in which it was defined. Also shown is the version of the Java SE Platform which introduced that version of theclassfile format (4.1).Table 4.7-B is ordered by the first version of the
classfile format in which each attribute was defined.Table 4.7-C is ordered by the location in a
classfile where each attribute is defined to appear.
Within the context of their use in this specification, that is, in the attributes tables of the class file structures in which they appear, the names of these predefined attributes are reserved.
Any conditions on the presence of a predefined attribute in an attributes table are specified explicitly in the section which describes the attribute. If no conditions are specified, then the attribute may appear any number of times in an attributes table.
The predefined attributes are categorized into three groups according to their purpose:
SixSeven attributes are critical to correct interpretation of theclassfile by the Java Virtual Machine:ConstantValueCodeStackMapTableBootstrapMethodsNestHostNestMembersPermittedSubclasses
In a
classfile whose version number is v, each of these attributes must be recognized and correctly read by an implementation of the Java Virtual Machine if the implementation supports version v of theclassfile format, and the attribute was first defined in version v or earlier of theclassfile format, and the attribute appears in a location where it is defined to appear.Nine attributes are not critical to correct interpretation of the
classfile by the Java Virtual Machine, but are either critical to correct interpretation of theclassfile by the class libraries of the Java SE Platform, or are useful for tools (in which case the section that specifies an attribute describes it as "optional"):ExceptionsInnerClassesEnclosingMethodSyntheticSignatureSourceFileLineNumberTableLocalVariableTableLocalVariableTypeTable
In a
classfile whose version number is v, each of these attributes must be recognized and correctly read by an implementation of the Java Virtual Machine if the implementation supports version v of theclassfile format, and the attribute was first defined in version v or earlier of theclassfile format, and the attribute appears in a location where it is defined to appear.Thirteen attributes are not critical to correct interpretation of the
classfile by the Java Virtual Machine, but contain metadata about theclassfile that is either exposed by the class libraries of the Java SE Platform, or made available by tools (in which case the section that specifies an attribute describes it as "optional"):SourceDebugExtensionDeprecatedRuntimeVisibleAnnotationsRuntimeInvisibleAnnotationsRuntimeVisibleParameterAnnotationsRuntimeInvisibleParameterAnnotationsRuntimeVisibleTypeAnnotationsRuntimeInvisibleTypeAnnotationsAnnotationDefaultMethodParametersModuleModulePackagesModuleMainClass
An implementation of the Java Virtual Machine may use the information that these attributes contain, or otherwise must silently ignore these attributes.
Table 4.7-A. Predefined class file attributes (by section)
| Attribute | Section | class file |
Java SE |
|---|---|---|---|
ConstantValue |
4.7.2 | 45.3 | 1.0.2 |
Code |
4.7.3 | 45.3 | 1.0.2 |
StackMapTable |
4.7.4 | 50.0 | 6 |
Exceptions |
4.7.5 | 45.3 | 1.0.2 |
InnerClasses |
4.7.6 | 45.3 | 1.1 |
EnclosingMethod |
4.7.7 | 49.0 | 5.0 |
Synthetic |
4.7.8 | 45.3 | 1.1 |
Signature |
4.7.9 | 49.0 | 5.0 |
SourceFile |
4.7.10 | 45.3 | 1.0.2 |
SourceDebugExtension |
4.7.11 | 49.0 | 5.0 |
LineNumberTable |
4.7.12 | 45.3 | 1.0.2 |
LocalVariableTable |
4.7.13 | 45.3 | 1.0.2 |
LocalVariableTypeTable |
4.7.14 | 49.0 | 5.0 |
Deprecated |
4.7.15 | 45.3 | 1.1 |
RuntimeVisibleAnnotations |
4.7.16 | 49.0 | 5.0 |
RuntimeInvisibleAnnotations |
4.7.17 | 49.0 | 5.0 |
RuntimeVisibleParameterAnnotations |
4.7.18 | 49.0 | 5.0 |
RuntimeInvisibleParameterAnnotations |
4.7.19 | 49.0 | 5.0 |
RuntimeVisibleTypeAnnotations |
4.7.20 | 52.0 | 8 |
RuntimeInvisibleTypeAnnotations |
4.7.21 | 52.0 | 8 |
AnnotationDefault |
4.7.22 | 49.0 | 5.0 |
BootstrapMethods |
4.7.23 | 51.0 | 7 |
MethodParameters |
4.7.24 | 52.0 | 8 |
Module |
4.7.25 | 53.0 | 9 |
ModulePackages |
4.7.26 | 53.0 | 9 |
ModuleMainClass |
4.7.27 | 53.0 | 9 |
NestHost |
4.7.28 | 55.0 | 11 |
NestMembers |
4.7.29 | 55.0 | 11 |
PermittedSubclasses |
4.7.30 | 60.65535 | 16 |
Table 4.7-B. Predefined class file attributes (by class file format)
| Attribute | class file |
Java SE | Section |
|---|---|---|---|
ConstantValue |
45.3 | 1.0.2 | 4.7.2 |
Code |
45.3 | 1.0.2 | 4.7.3 |
Exceptions |
45.3 | 1.0.2 | 4.7.5 |
SourceFile |
45.3 | 1.0.2 | 4.7.10 |
LineNumberTable |
45.3 | 1.0.2 | 4.7.12 |
LocalVariableTable |
45.3 | 1.0.2 | 4.7.13 |
InnerClasses |
45.3 | 1.1 | 4.7.6 |
Synthetic |
45.3 | 1.1 | 4.7.8 |
Deprecated |
45.3 | 1.1 | 4.7.15 |
EnclosingMethod |
49.0 | 5.0 | 4.7.7 |
Signature |
49.0 | 5.0 | 4.7.9 |
SourceDebugExtension |
49.0 | 5.0 | 4.7.11 |
LocalVariableTypeTable |
49.0 | 5.0 | 4.7.14 |
RuntimeVisibleAnnotations |
49.0 | 5.0 | 4.7.16 |
RuntimeInvisibleAnnotations |
49.0 | 5.0 | 4.7.17 |
RuntimeVisibleParameterAnnotations |
49.0 | 5.0 | 4.7.18 |
RuntimeInvisibleParameterAnnotations |
49.0 | 5.0 | 4.7.19 |
AnnotationDefault |
49.0 | 5.0 | 4.7.22 |
StackMapTable |
50.0 | 6 | 4.7.4 |
BootstrapMethods |
51.0 | 7 | 4.7.23 |
RuntimeVisibleTypeAnnotations |
52.0 | 8 | 4.7.20 |
RuntimeInvisibleTypeAnnotations |
52.0 | 8 | 4.7.21 |
MethodParameters |
52.0 | 8 | 4.7.24 |
Module |
53.0 | 9 | 4.7.25 |
ModulePackages |
53.0 | 9 | 4.7.26 |
ModuleMainClass |
53.0 | 9 | 4.7.27 |
NestHost |
55.0 | 11 | 4.7.28 |
NestMembers |
55.0 | 11 | 4.7.29 |
PermittedSubclasses |
60.65535 | 16 | 4.7.30 |
Table 4.7-C. Predefined class file attributes (by location)
| Attribute | Location | class file |
|---|---|---|
SourceFile |
ClassFile |
45.3 |
InnerClasses |
ClassFile |
45.3 |
EnclosingMethod |
ClassFile |
49.0 |
SourceDebugExtension |
ClassFile |
49.0 |
BootstrapMethods |
ClassFile |
51.0 |
Module, ModulePackages, ModuleMainClass |
ClassFile |
53.0 |
NestHost, NestMembers |
ClassFile |
55.0 |
PermittedSubclasses |
ClassFile |
60.65535 |
ConstantValue |
field_info |
45.3 |
Code |
method_info |
45.3 |
Exceptions |
method_info |
45.3 |
RuntimeVisibleParameterAnnotations, RuntimeInvisibleParameterAnnotations |
method_info |
49.0 |
AnnotationDefault |
method_info |
49.0 |
MethodParameters |
method_info |
52.0 |
Synthetic |
ClassFile, field_info, method_info |
45.3 |
Deprecated |
ClassFile, field_info, method_info |
45.3 |
Signature |
ClassFile, field_info, method_info |
49.0 |
RuntimeVisibleAnnotations, RuntimeInvisibleAnnotations |
ClassFile, field_info, method_info |
49.0 |
LineNumberTable |
Code |
45.3 |
LocalVariableTable |
Code |
45.3 |
LocalVariableTypeTable |
Code |
49.0 |
StackMapTable |
Code |
50.0 |
RuntimeVisibleTypeAnnotations, RuntimeInvisibleTypeAnnotations |
ClassFile, field_info, method_info, Code |
52.0 |
4.7.30 The PermittedSubclasses Attribute
The PermittedSubclasses attribute is a variable-length attribute in the attributes table of a ClassFile structure. If a class or interface has a PermittedSubclasses attribute, any class or interface that attempts to extend or implement it must be named by the attribute (5.3.5).
In the Java Programming Language, there is a
sealedmodifier to indicate a class or interface that constrains extension in this way. In the class file, there is noACC_SEALEDflag. Instead, asealedclass or interface is indicated by the presence of thePermittedSubclassesattribute.
There may be at most one PermittedSubclasses attribute in the attributes table of a ClassFile structure whose ACC_FINAL flag is not set (4.1). If the ACC_FINAL flag is set, then the ClassFile structure must not have a PermittedSubclasses attribute.
We treat sealed as distinct from final—a sealed class has an enumerated list of designated subclasses, while a final class has no subclasses. Thus, a ClassFile structure may have a PermittedSubclasses attribute, or may have its ACC_FINAL flag set, but not both.
Alternatively, we could have let the PermittedSubclasses attribute refine the ACC_FINAL flag—reinterpreting "final" to mean "can't be extended, except for authorized subclasses". But doing so risks disrupting consumers of class files that have long assumed "final" means "has 0 subclasses" and that interfaces cannot be final.
The PermittedSubclasses attribute has the following format:
PermittedSubclasses_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 number_of_classes;
u2 classes[number_of_classes];
}
The items of the PermittedSubclasses_attribute structure are as follows:
- attribute_name_index
The value of the
attribute_name_indexitem must be a valid index into theconstant_pooltable. Theconstant_poolentry at that index must be aCONSTANT_Utf8_infostructure (4.4.7) representing the string "PermittedSubclasses".- attribute_length
The value of the
attribute_lengthitem indicates the length of the attribute, excluding the initial six bytes.- number_of_classes
The value of the
number_of_classesitem indicates the number of entries in theclassesarray.- classes[]
Each value in the
classesarray must be a valid index into theconstant_pooltable. Theconstant_poolentry at that index must be aCONSTANT_Class_infostructure (4.4.1) representing a class or interface which is authorized to extend or implement the current class or interface.The
classesarray is consulted when a class or interface that attempts to directly extend or implement the current class or interface is created (5.3.5). Array items that do not attempt to directly extend or implement the current class or interface are ignored.
Chapter 5: Loading, Linking, and Initializing
5.3 Creation and Loading
Creation of a class or interface C denoted by the name N consists of locating a binary representation the class or interface (4) and then deriving construction in the method area of the Java Virtual Machine (2.5.4) of an implementation-specific internal representation of C in the method area of the Java Virtual Machine (2.5.4).
Class or interface creation is triggered by another class or interface D, which references C through its run-time constant pool. Class or interface creation may also be triggered by D invoking methods in certain Java SE Platform class libraries (2.12) such as reflection.
When class or interface creation is triggered by a reference to C from a run-time constant pool, the process described in this section is followed. Class or interface creation triggered by a method of the Java SE Platform class libraries follows a similar process, the details of which are specified by the method.
If C is not an array class, it is created by loading a binary representation of C (4) created by loading it using a class loader. Array classes do not have an external binary representation; they are created by the Java Virtual Machine rather than by a class loader.
There are two kinds of class loaders: the bootstrap class loader supplied by the Java Virtual Machine, and user-defined class loaders. Every user-defined class loader is an instance of a subclass of the abstract class ClassLoader. Applications employ user-defined class loaders in order to extend the manner in which the Java Virtual Machine dynamically loads and thereby creates classes. User-defined class loaders can be used to create classes that originate from user-defined sources. For example, a class could be downloaded across a network, generated on the fly, or extracted from an encrypted file.
User-defined class loaders can be used to create classes that originate from user-defined sources. For example, a class could be downloaded across a network, generated on the fly, or extracted from an encrypted file.
A class loader L may create load C by defining defining it directly or by delegating to another class loader. If L creates defines C directly, we say that L defines C or, equivalently, that L is the defining loader of C.
When one class loader delegates to another class loader, the loader that initiates the loading is not necessarily the same loader that completes the loading and defines the class. If L creates loads C, either by defining it directly or by delegation, we say that L initiates loading of C or, equivalently, that L is an initiating loader of C.
At run time, a class or interface is determined not by its name alone, but by a pair: its binary name (4.2.1) and its defining class loader. Each such class or interface belongs to a single run-time package. The run-time package of a class or interface is determined by the package name and defining class loader of the class or interface.
The Java Virtual Machine uses one of three procedures to create class or interface C denoted by N:
If N denotes a nonarray class or an interface, one of the two following methods is used to load and thereby create C:
Otherwise N denotes an array class. An array class is created directly by the Java Virtual Machine (5.3.3), not by a class loader. However, the defining class loader of D is used in the process of creating array class C.
If an error occurs during class loading while the Java Virtual Machine is attempting to create a class, then an instance of a subclass of LinkageError must be thrown at a point in the program that (directly or indirectly) uses the class or interface being loaded.
If the Java Virtual Machine ever attempts to load a class C during verification (5.4.1) or resolution (5.4.3) (but not initialization (5.5)), and the class loader that is used to initiate loading of C throws an instance of ClassNotFoundException, then the Java Virtual Machine must throw an instance of NoClassDefFoundError whose cause is the instance of ClassNotFoundException.
This discussion appears to be a roundabout way to say "when you load a class via reflection, you get direct access to the exceptions that the reflection API throws". But those details can be left to the reflection API specification. For our purposes, this section is only concerned with the behavior of the JVM as it loads classes.
The details about wrapping ClassNotFoundException in NoClassDefFoundError are moved to sections 5.3.1 and 5.3.2, below.
(A subtlety here is that recursive class loading to load superclasses is performed as part of resolution (5.3.5, step 3). Therefore, a ClassNotFoundException that results from a class loader failing to load a superclass must be wrapped in a NoClassDefFoundError.)
Discussions about recursion are best left to 5.3.5.
A well-behaved class loader should maintain three properties:
Given the same name, a good class loader should always return the same
Classobject.If a class loader L1 delegates loading of a class C to another loader L2, then for any type T that occurs as the direct superclass or a direct superinterface of C, or as the type of a field in C, or as the type of a formal parameter of a method or constructor in C, or as a return type of a method in C, L1 and L2 should return the same
Classobject.If a user-defined classloader prefetches binary representations of classes and interfaces, or loads a group of related classes together, then it must reflect loading errors only at points in the program where they could have arisen without prefetching or group loading.
We will sometimes represent a class or interface using the notation <N, Ld>, where N denotes the name of the class or interface and Ld denotes the defining loader of the class or interface.
We will also represent a class or interface using the notation NLi, where N denotes the name of the class or interface and Li denotes an initiating loader of the class or interface.
5.3.1 Loading Creation Using the Bootstrap Class Loader
The following steps are used to load and thereby create the nonarray class or interface C denoted by N using the bootstrap class loader.
First, the Java Virtual Machine determines whether the bootstrap class loader has already been recorded as an initiating loader of a class or interface denoted by N. If so, this class or interface is C, and no class creation is necessary.
Otherwise, the Java Virtual Machine passes the argument N to an invocation of a method on the bootstrap class loader to search load C. This will involve searching for a purported representation of C in a platform-dependent manner and then deriving C from that representation using the algorithm found in 5.3.5. Typically, a class or interface will be represented using a file in a hierarchical file system, and the name of the class or interface will be encoded in the pathname of the file.
Note that there is no guarantee that a purported representation found is valid or is a representation of C. This phase of loading must detect the following error:
- If no purported representation of C is found, loading throws an instance of
ClassNotFoundException.
Then the Java Virtual Machine attempts to derive a class denoted by N using the bootstrap class loader from the purported representation using the algorithm found in 5.3.5. That class is C.
If no purported representation of C is found, the bootstrap class loader throws an instance of ClassNotFoundException. Creation of C then fails with a NoClassDefFoundError whose cause is the ClassNotFoundException produced by the bootstrap class loader.
If deriving C from the purported representation fails, any of the errors specified in 5.3.5 may be thrown by the bootstrap class loader. Creation of C fails for the same reason.
It's confusing to treat class derivation as a followup step. While no particular API is specified to be used, the loadClass model matches what readers will expect. In that case, once the loadClass method returns, the class has already been derived and the creation process is complete (except that the Java Virtual Machine may need to wrap a ClassNotFoundException).
5.3.2 Loading Creation Using a User-defined Class Loader
The following steps are used to load and thereby create the nonarray class or interface C denoted by N using a user-defined class loader L.
First, the Java Virtual Machine determines whether L has already been recorded as an initiating loader of a class or interface denoted by N. If so, this class or interface is C, and no class creation is necessary.
Otherwise, the Java Virtual Machine invokes loadClass(N) on L. The value returned by the invocation is the created class or interface C. The Java Virtual Machine then records that L is an initiating loader of C (5.3.4). The remainder of this section describes this process in more detail.
When the loadClass method of the class loader L is invoked with the name N of a class or interface C to be loaded, L must perform one of the following two operations in order to load C:
The class loader L can create an array of bytes representing C as the bytes of a
ClassFilestructure (4.1); it then must invoke the methoddefineClassof classClassLoader. InvokingdefineClasscauses the Java Virtual Machine to derive a class or interface denoted by N using L from the array of bytes using the algorithm found in 5.3.5.If deriving C from the purported representation fails, any of the errors specified in 5.3.5 may be thrown by
loadClass. Creation of C fails for the same reason.If the class or interface produced by
loadClasshas name N but is different than the class or interface derived bydefineClass, creation fails and the Java Virtual Machine throws an instance ofLinkageErroror a subclass ofLinkageError.The class loader L can delegate the loading of C to some other class loader L'. This is accomplished by passing the argument N directly or indirectly to an invocation of a method on L' (typically the
loadClassmethod). The result of the invocation is C.
In either (1) or (2), if the class loader L is unable to load a class or interface denoted by N for any reason, it must throw an instance of ClassNotFoundException. Creation of C then fails with a NoClassDefFoundError whose cause is the ClassNotFoundException produced by the bootstrap class loader.
If the result of the loadClass invocation is null, or is a loaded class or interface with a name other than N, creation fails with a NoClassDefFoundError.
These additional rules attempt to capture actual error checks as observed in JDK 14 and OpenJ9 0.15.
The check in step 1 produces different errors in the two implementations: Hotspot throws a LinkageError, while J9 throws a NoClassDefFoundError.
Note that if defineClass is called by some loader other than the initiating loader, the check in step (1) is not performed. This is because the delegated loading is a reflective call, and so not subject to these JVM-level loading checks.
To do: what happens if the class loader throws an unexpected exception?
Since JDK 1.1, Oracle’s Java Virtual Machine implementation has invoked the
loadClassmethod of a class loader in order to cause it to load a class or interface. The argument toloadClassis the name of the class or interface to be loaded. There is also a two-argument version of theloadClassmethod, where the second argument is abooleanthat indicates whether the class or interface is to be linked or not. Only the two-argument version was supplied in JDK 1.0.2, and Oracle’s Java Virtual Machine implementation relied on it to link the loaded class or interface. From JDK 1.1 onward, Oracle’s Java Virtual Machine implementation links the class or interface directly, without relying on the class loader.
5.3.5 Deriving a Class from a class File Representation
The derivation process described here is an important, tightly-specified subprocess of loading and creation. These revisions make more prominent use of the term.
Steps 3 and 4 of the process include new checks to enforce the PermittedSubclasses attribute.
The following steps are used to derive a nonarray class or interface C denoted by N using loader L from a purported representation in Class object for theclass file format.
This section says nothing about Class objects, which are only auxiliary to creation and loading. Instead, these rules describe how to derive an abstract "class or interface" from a name, class loader, and byte array.
First, the Java Virtual Machine determines whether
it has already recorded that L is an initiating loader of a class or interface denoted by N. If so, this creation attempt is invalid and loading throws athe attempt to derive a class or interface named N for class loader L is invalid. If so, derivation throws aLinkageError.LinkageError.An attempt to derive a class or interface named N for class loader L is invalid if one of the following are true:
L has already been recorded as as an initiating loader of a class or interface named N.
L is not the bootstrap class loader (5.3) and N is the reserved name
java/lang/Object.
The second case, combined with restrictions in 4.1, ensures that there is only one root of the class hierarchy, and the name
java/lang/Objectcan reliably be used to refer to it.The Java SE API enforces additional security restrictions that prevent unauthorized attempts to load classes in certain packages, including
java.*.In practice, the security restrictions make it impossible to test the
Objectrule—aSecurityExceptionwill happen before the JVM can perform this check. But for internal consistency, it seems important to state.Otherwise, the Java Virtual Machine attempts to parse the purported representation. However, the purported representation may not in fact be a valid representation of C.
This phase of
loadingderivation must detect the following errors:If the purported representation is not aClassFilestructure (4.1, 4.8), loading throws an instance ofClassFormatError.It doesn't make sense to try to parse a
classfile before determining that the version number is supported. The version check has to happen first.Otherwise, ifIf the purported representationis not of a supported major or minor version (4.1)provides a major and minor version number in its 5th through 8th bytes (4.1), but the version number is not supported by this Java Virtual Machine implementation,loadingderivation throws an instance ofUnsupportedClassVersionError.UnsupportedClassVersionError, a subclass ofClassFormatError, was introduced to enable easy identification of aClassFormatErrorcaused by an attempt to load a class whose representation uses an unsupported version of theclassfile format. In JDK 1.1 and earlier, an instance ofNoClassDefFoundErrororClassFormatErrorwas thrown in case of an unsupported version, depending on whether the class was being loaded by the system class loader or a user-defined class loader.Otherwise, if the purported representation is not a
ClassFilestructure (4.1, 4.8), derivation throws an instance ofClassFormatError.Otherwise, if the purported representation does not actually represent a class or interface named N,
loadingderivation throws an instance ofNoClassDefFoundErroror an instance of one of its subclasses.This occurs when the purported representation has either a
this_classitem which specifies a name other than N, or anaccess_flagsitem which has theACC_MODULEflag set.
If C has a direct superclass, the symbolic reference from C to its direct superclass is resolved using the algorithm of 5.4.3.1. Note that if C is an interface it must have
Objectas its direct superclass, which must already have been loaded. OnlyObjecthas no direct superclass.Per 5.2, an arbitrary initial class or interface is the first to be created. If the initial class or interface is an interface, deriving it will recursively prompt the creation of (not-yet-loaded)
Objecthere.Any exceptions that can be thrown due to class or interface resolution can be thrown as a result of this phase of
loadingderivation. In addition, this phase ofloadingderivation must detect the following errors:If the class or interface named as the direct superclass of C is in fact an interface, loading throws anIncompatibleClassChangeError.Otherwise, if any of the superclasses of C is C itselfIf the attempt to resolve the direct superclass leads to the current thread attempting to recursively derive a class named N using loader L,loadingderivation throws aClassCircularityError.How can we decide whether a superclass is "C itself" without actually creating the superclass first? And, of course, we can't create the superclass without recursively applying this check. This dependency loop is broken by detecting when a second attempt is made to derive a class named N.
Otherwise, if the class or interface named as the direct superclass of C is an interface or a
finalclass, derivation throws aVerifyError.This check has long been specified as part of verification (4.10, 4.10.1). However, JDK 14 and OpenJ9 both perform it as part of class derivation.
A
VerifyErrorat this stage is out of place, but that's the exception that actually gets thrown. See JDK-8243582.
Otherwise, if the class named as the direct superclass of C has a
PermittedSubclassesattribute (4.7.30) and any of the following are true, derivation fails with aIncompatibleClassChangeError:The superclass belongs to a different run-time module than C.
C does not have its
ACC_PUBLICflag set (4.1) and the superclass belongs to a different run-time package than C.No entry in the
classestable of the superclass'sPermittedSubclassesattribute references a class or interface with name N.
These restrictions guarantee that an entry in
PermittedSubclassesof the superclass will resolve to C. (We don't actually resolve the reference to C, because doing so would prompt a circularity, since derivation of C hasn't yet completed.)The Java language has an additional requirement that, if the classes belong to an unnamed module, they must both be in the same package. This encourages programmers to use the feature appropriately (they shouldn't try to spread a sealed hierarchy across different maintenance domains), but is not the sort of fundamental restriction the JVM needs to enforce. So another language or bytecode generator is free not to follow the package restriction.
Otherwise, if C is a class and some non-
staticmethod declared in C can override (5.4.5) afinal, non-staticmethod declared in a superclass of C, derivation throws aVerifyError.Same comment about the
VerifyError.
If C has any direct superinterfaces, the symbolic references from C to its direct superinterfaces are resolved using the algorithm of 5.4.3.1.
Any exceptions that can be thrown due to class or interface resolution can be thrown as a result of this phase of
loadingderivation. In addition, this phase ofloadingderivation must detect the following errors:If any of the classes or interfaces named as direct superinterfaces of C is not in fact an interface, loading throws anIncompatibleClassChangeError.Otherwise, if any of the superinterfaces of C is C itselfIf the attempt to resolve a direct superinterface leads to the current thread attempting to recursively derive a class named N using loader L,loadingderivation throws aClassCircularityError.Otherwise, if any of the classes or interfaces named as direct superinterfaces of C is not in fact an interface, derivation throws an
IncompatibleClassChangeError.
Otherwise, for each direct superinterface, if the superinterface has a
PermittedSubclassesattribute (4.7.30) and any of the following are true, derivation fails with aIncompatibleClassChangeError:The superinterface belongs to a different run-time module than C.
C does not have its
ACC_PUBLICflag set (4.1) and the superinterface belongs to a different run-time package than C.No entry in the
classestable of the superinterface'sPermittedSubclassesattribute references a class or interface with name N.
If, since step 1, in another thread, the Java Virtual Machine has marked a class or interface named N as having L as its defining class loader, that class or interface is the result of class derivation, and the class or interface derived in steps 1-4 is discarded.
This is the longstanding behavior of Hotspot, but there was no provision for it in the specification.
TheOtherwise, the class or interface derived in steps 1-4 is the result of class derivation, and the Java Virtual Machine marks C as having L as its defining class loader and records that L is an initiating loader of C (5.3.4).