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
class
file format in which it was defined. Also shown is the version of the Java SE Platform which introduced that version of theclass
file format (4.1).Table 4.7-B is ordered by the first version of the
class
file format in which each attribute was defined.Table 4.7-C is ordered by the location in a
class
file 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 theclass
file by the Java Virtual Machine:ConstantValue
Code
StackMapTable
BootstrapMethods
NestHost
NestMembers
PermittedSubclasses
In a
class
file 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 theclass
file format, and the attribute was first defined in version v or earlier of theclass
file format, and the attribute appears in a location where it is defined to appear.Nine attributes are not critical to correct interpretation of the
class
file by the Java Virtual Machine, but are either critical to correct interpretation of theclass
file 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"):Exceptions
InnerClasses
EnclosingMethod
Synthetic
Signature
SourceFile
LineNumberTable
LocalVariableTable
LocalVariableTypeTable
In a
class
file 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 theclass
file format, and the attribute was first defined in version v or earlier of theclass
file format, and the attribute appears in a location where it is defined to appear.Thirteen attributes are not critical to correct interpretation of the
class
file by the Java Virtual Machine, but contain metadata about theclass
file 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"):SourceDebugExtension
Deprecated
RuntimeVisibleAnnotations
RuntimeInvisibleAnnotations
RuntimeVisibleParameterAnnotations
RuntimeInvisibleParameterAnnotations
RuntimeVisibleTypeAnnotations
RuntimeInvisibleTypeAnnotations
AnnotationDefault
MethodParameters
Module
ModulePackages
ModuleMainClass
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
sealed
modifier to indicate a class or interface that constrains extension in this way. In the class file, there is noACC_SEALED
flag. Instead, asealed
class or interface is indicated by the presence of thePermittedSubclasses
attribute.
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_index
item must be a valid index into theconstant_pool
table. Theconstant_pool
entry at that index must be aCONSTANT_Utf8_info
structure (4.4.7) representing the string "PermittedSubclasses
".- attribute_length
The value of the
attribute_length
item indicates the length of the attribute, excluding the initial six bytes.- number_of_classes
The value of the
number_of_classes
item indicates the number of entries in theclasses
array.- classes[]
Each value in the
classes
array must be a valid index into theconstant_pool
table. Theconstant_pool
entry at that index must be aCONSTANT_Class_info
structure (4.4.1) representing a class or interface which is authorized to extend or implement the current class or interface.The
classes
array 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
Class
object.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
Class
object.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
ClassFile
structure (4.1); it then must invoke the methoddefineClass
of classClassLoader
. InvokingdefineClass
causes 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
loadClass
has name N but is different than the class or interface derived bydefineClass
, creation fails and the Java Virtual Machine throws an instance ofLinkageError
or 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
loadClass
method). 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
loadClass
method of a class loader in order to cause it to load a class or interface. The argument toloadClass
is the name of the class or interface to be loaded. There is also a two-argument version of theloadClass
method, where the second argument is aboolean
that 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/Object
can 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
Object
rule—aSecurityException
will 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 aClassFile
structure (4.1, 4.8), loading throws an instance ofClassFormatError
.It doesn't make sense to try to parse a
class
file 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 aClassFormatError
caused by an attempt to load a class whose representation uses an unsupported version of theclass
file format. In JDK 1.1 and earlier, an instance ofNoClassDefFoundError
orClassFormatError
was 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
ClassFile
structure (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 ofNoClassDefFoundError
or an instance of one of its subclasses.This occurs when the purported representation has either a
this_class
item which specifies a name other than N, or anaccess_flags
item which has theACC_MODULE
flag 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
Object
as its direct superclass, which must already have been loaded. OnlyObject
has 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)
Object
here.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
final
class, 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
VerifyError
at 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
PermittedSubclasses
attribute (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_PUBLIC
flag set (4.1) and the superclass belongs to a different run-time package than C.No entry in the
classes
table of the superclass'sPermittedSubclasses
attribute references a class or interface with name N.
These restrictions guarantee that an entry in
PermittedSubclasses
of 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-
static
method declared in C can override (5.4.5) afinal
, non-static
method 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
PermittedSubclasses
attribute (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_PUBLIC
flag set (4.1) and the superinterface belongs to a different run-time package than C.No entry in the
classes
table of the superinterface'sPermittedSubclasses
attribute 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).