このドキュメントでは、Java SE 15のプレビュー機能であるsealedクラスおよびインタフェースをサポートするためのJava仮想マシン仕様に対する変更について説明します。sealedクラスまたはインタフェースは、許可されたサブクラスまたはサブインタフェースの列挙セットに対する拡張と実装を制限します。この機能の概要は、JEP 360を参照してください。
セクション4.7では、新しいPermittedSubclasses
属性を指定します。
セクション5.3.5では、PermittedSubclasses
属性を適用するためにクラスの導出時に実行されるチェックを指定します。
新しいクラスの作成チェックが参照実装の既存の動作とどのように相互作用するかをより正確に指定するために、5.3に対するいくつかの改訂も含まれています。
変更は、JVM仕様の既存のセクションに関して説明します。新しいテキストはこのように示され、削除されたテキストはこのように示されます。必要に応じて、説明と考察が端の方にグレーのボックスで囲まれて記載されています。
第4章: class
ファイル形式
4.7 属性
属性は、class
ファイル形式のClassFile
、field_info
、method_info
およびCode_attribute
構造体で使用されます(4.1、4.5、4.6、4.7.3)。
すべての属性が次の一般的な形式を持ちます。
attribute_info {
u2 attribute_name_index;
u4 attribute_length;
u1 info[attribute_length];
}
すべての属性について、attribute_name_index
項目は、クラスの定数プールに対して有効な符号なし16ビット索引である必要があります。attribute_name_index
でのconstant_pool
エントリは、属性の名前を表すCONSTANT_Utf8_info
構造体(4.4.7)である必要があります。attribute_length
項目の値は、後続の情報の長さをバイト単位で示します。この長さには、attribute_name_index
およびattribute_length
項目を含む先頭の6バイトは含まれません。
2829の属性がこの仕様によって事前に定義されています。これらは、ナビゲートしやすいように3回に分けてリストされています。
表4.7-Aは、この章の属性のセクション番号で順序付けられています。各属性は、それが定義された
class
ファイル形式の最初のバージョンで示されています。もう1つ示されているのは、class
ファイル形式のそのバージョンを導入したJava SEプラットフォームのバージョンです(4.1)。表4.7-Bは、各属性が定義された
class
ファイル形式の最初のバージョンで順序付けられています。表4.7-Cは、各属性の出現が定義されている
class
ファイル内の場所で順序付けられています。
この仕様でのこれらの使用状況、つまり、これらが出現するclass
ファイル構造体のattributes
表において、これらの事前定義済の属性の名前が予約されています。
attributes
表内における事前定義済の属性の存在に関する条件は、属性を説明するセクションで明示的に規定されています。条件が規定されていない場合、属性はattributes
表内に任意の回数出現する場合があります。
事前定義済の属性は、その目的に応じて3つのグループに分類されます。
Java仮想マシンによる
class
ファイルの正しい解釈には、6つ7つの属性が不可欠です。ConstantValue
Code
StackMapTable
BootstrapMethods
NestHost
NestMembers
PermittedSubclasses
バージョン番号がvである
class
ファイル内では、Java仮想マシンの実装がclass
ファイル形式のバージョンvをサポートしており、属性がclass
ファイル形式のバージョンv以前で最初に定義されたもので、その属性が出現先として定義されている場所に出現している場合、これらの各属性はJava仮想マシンの実装によって識別され、適切に読み取られます。9つの属性は、Java仮想マシンによる
class
ファイルの正しい解釈に不可欠ではありませんが、Java SEプラットフォームのクラス・ライブラリによるclass
ファイルの正しい解釈に不可欠であるか、ツールの役に立ちます(この場合、属性を規定するセクションは「オプション」として記載されています)。Exceptions
InnerClasses
EnclosingMethod
Synthetic
Signature
SourceFile
LineNumberTable
LocalVariableTable
LocalVariableTypeTable
バージョン番号がvである
class
ファイル内では、Java仮想マシンの実装がclass
ファイル形式のバージョンvをサポートしており、属性がclass
ファイル形式のバージョンv以前で最初に定義されたもので、その属性が出現先として定義されている場所に出現している場合、これらの各属性はJava仮想マシンの実装によって識別され、適切に読み取られます。13の属性は、Java仮想マシンによる
class
ファイルの正しい解釈に不可欠ではありませんが、Java SEプラットフォームのクラス・ライブラリによって公開されるclass
ファイルに関するメタデータを含むか、ツールによって使用可能になります(この場合、属性を規定するセクションは「オプション」として記載されています)。SourceDebugExtension
Deprecated
RuntimeVisibleAnnotations
RuntimeInvisibleAnnotations
RuntimeVisibleParameterAnnotations
RuntimeInvisibleParameterAnnotations
RuntimeVisibleTypeAnnotations
RuntimeInvisibleTypeAnnotations
AnnotationDefault
MethodParameters
Module
ModulePackages
ModuleMainClass
Java仮想マシンの実装では、これらの属性に含まれる情報が使用される場合がありますが、そうでない場合、これらの属性は内部で無視する必要があります。
表4.7-A.事前定義済のclass
ファイル属性(セクション別)
属性 | セクション | class ファイル |
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 | 59.65535 | 15 |
表4.7-B.事前定義済のclass
ファイル属性(class
ファイル形式別)
属性 | class ファイル |
Java SE | セクション |
---|---|---|---|
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 |
59.65535 | 15 | 4.7.30 |
表4.7-C.事前定義済のclass
ファイル属性(位置別)
属性 | 位置 | class ファイル |
---|---|---|
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 |
59.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 PermittedSubclasses
属性
PermittedSubclasses
属性は、ClassFile
構造体のattributes
表内の可変長属性です。クラスまたはインタフェースがPermittedSubclasses
属性を持つ場合、それを拡張または実装しようとするクラスまたはインタフェースには属性(5.3.5)によって名前を付ける必要があります。
Javaプログラミング言語には、この方法での拡張を制約するクラスまたはインタフェースを示す
sealed
修飾子があります。クラス・ファイル内には、ACC_SEALED
フラグはありません。かわりに、sealed
クラスまたはインタフェースがPermittedSubclasses
属性の存在によって示されます。
ACC_FINAL
フラグが設定されていないClassFile
構造体(4.1)のattributes
表内には最大で1つのPermittedSubclasses
属性を使用できます。ACC_FINAL
フラグが設定されている場合、ClassFile
構造体がPermittedSubclasses
属性を持つことはできません。
Oracleでは、sealedをfinalとは異なるものとして取り扱います。sealedクラスは指定されたサブクラスの列挙リストを持ちますが、finalクラスはサブクラスを持ちません。このため、ClassFile
構造体はPermittedSubclasses
属性を持つこと、またはACC_FINAL
フラグを設定することができますが、両方はできません。
また、PermittedSubclasses
属性によってACC_FINAL
フラグを改良する(「final」を解釈し直して、「許可されたサブクラスを除いて拡張できない」という意味にする)こともできました。ただし、そのようにすると、「final」は「サブクラスを持たない」ことを意味し、インタフェースをfinalにできない、ということを長い間前提としてきたクラス・ファイルの利用者を混乱させるリスクがあります。
PermittedSubclasses
属性の形式は次のとおりです。
PermittedSubclasses_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 number_of_classes;
u2 classes[number_of_classes];
}
PermittedSubclasses_attribute
構造体の項目は次のとおりです。
- attribute_name_index
attribute_name_index
項目の値は、constant_pool
表への有効な索引にする必要があります。その索引でのconstant_pool
エントリは、文字列「PermittedSubclasses
」を表すCONSTANT_Utf8_info
構造体(4.4.7)である必要があります。- attribute_length
attribute_length
項目の値は、属性の長さ(先頭の6バイトを除く)を示します。- number_of_classes
number_of_classes
項目の値は、classes
配列内のエントリの数を示します。- classes[]
classes
配列内の各値は、constant_pool
表への有効な索引にする必要があります。その索引でのconstant_pool
エントリは、現在のクラスまたはインタフェースを拡張または実装することが許可されたクラスまたはインタフェースを表すCONSTANT_Class_info
構造体(4.4.1)である必要があります。現在のクラスまたはインタフェースを直接拡張または実装しようとするクラスまたはインタフェースが作成される場合(5.3.5)、
classes
配列が参照されます。現在のクラスまたはインタフェースを直接拡張または実装しようとしない配列項目は無視されます。
第5章: ロード、リンクおよび初期化
5.3 作成およびロード
名前Nによって示されるクラスまたはインタフェースCの作成は、クラスまたはインタフェース(4)のバイナリ表現の特定、およびJava仮想マシンのメソッド領域(2.5.4)内のCの実装固有の内部表現の仮想マシンのメソッド領域内の構造体(2.5.4)の導出で構成されています。
クラスまたはインタフェースの作成は、実行時定数プールを介してCを参照する別のクラスまたはインタフェースDによってトリガーされます。また、クラスまたはインタフェースの作成は、リフレクションなどの特定のJava SEプラットフォームのクラス・ライブラリ(2.12)内のメソッドを呼び出すDによってもトリガーされる場合があります。
クラスまたはインタフェースの作成が実行時定数プールからのCへの参照によってトリガーされる場合、このセクションで説明するプロセスに従います。Java SEプラットフォームのクラス・ライブラリ内のメソッドによってトリガーされるクラスまたはインタフェースの作成も、同様のプロセスに従います。その詳細は、このメソッドによって規定されます。
Cが配列クラスではない場合、C (4)のバイナリ表現をロードすることによって作成クラス・ローダーを使用してロードすることによって作成されます。配列クラスには外部バイナリ表現はありません。配列クラスは、クラス・ローダーによってではなく、Java仮想マシンによって作成されます。
クラス・ローダーには、Java仮想マシンによって提供されるブートストラップ・クラス・ローダーとユーザー定義のクラス・ローダーの2種類があります。ユーザー定義のクラス・ローダーはすべて、abstractクラスClassLoader
のサブクラスのインスタンスです。アプリケーションは、Java仮想マシンがクラスを動的にロードすることによって作成する方法を拡張するためにユーザー定義のクラス・ローダーを採用します。ユーザー定義のクラス・ローダーは、ユーザー定義のソースが作成元であるクラスを作成するために使用できます。たとえば、クラスは、ネットワーク全体にわたってダウンロードしたり、その場で生成したり、暗号化されたファイルから抽出できます。
ユーザー定義のクラス・ローダーは、ユーザー定義のソースが作成元であるクラスを作成するために使用できます。たとえば、クラスは、ネットワーク全体にわたってダウンロードしたり、その場で生成したり、暗号化されたファイルから抽出できます。
クラス・ローダーLは、Cを直接定義定義するか、別のクラス・ローダーに委譲することにより、作成ロードできます。LがCを直接作成定義する場合、Oracleでは、LがCを定義する、言い換えると、LはCを定義するローダーである、と言います。
1つのクラス・ローダーが別のクラス・ローダーに委譲する場合、ロードを開始するローダーは必ずしも、ロードを完了してクラスを定義するローダーと同じものである必要はありません。LがCを直接定義することによるか委譲によって作成ロードする場合、Oracleでは、LがCのロードを開始する、言い換えると、LはCを開始するローダーである、と言います。
実行時方法には、クラスまたはインタフェースが、名前のみによってではなく、ペア(バイナリ名(4.2.1)と定義する側のクラス・ローダー)によって決定されます。このような各クラスまたはインタフェースは、単一の実行時パッケージに属します。クラスまたはインタフェースの実行時パッケージは、パッケージ名、およびクラスまたはインタフェースを定義するクラス・ローダーによって決定されます。
Java仮想マシンは、Nによって示されるクラスまたはインタフェースCを作成するために3つの手順のうち1つを使用します。
Nが配列以外のクラスまたはインタフェースを示す場合、Cをロードして作成するために次の2つのメソッドの1つを使用します。
それ以外の場合、Nが配列クラスを示します。配列クラスは、クラス・ローダーによってではなく、Java仮想マシンによって直接作成されます(5.3.3)。ただし、Dを定義するクラス・ローダーは、配列クラスCの作成プロセスで使用されます。
クラスのロード中にJava仮想マシンが作成を作成しようとしているときにエラーが発生した場合、ロード中のクラスまたはインタフェースを(直接または間接的に)使用するプログラム内のある時点でLinkageError
のサブクラスのインスタンスがスローされる必要があります。
検証(5.4.1)または解決(5.4.3)中に(ただし、初期化(5.5)中ではなく) Java仮想マシンがクラスCを常にロードしようとしており、Cのロードを開始するために使用されるクラス・ローダーがClassNotFoundException
のインスタンスをスローする場合、Java仮想マシンは、原因がClassNotFoundException
のインスタンスであるNoClassDefFoundError
のインスタンスをスローする必要があります。
ここでの説明は、「リフレクションを介してクラスをロードすると、リフレクションAPIがスローする例外に直接アクセスできるようになる」という説明の遠回しな言い方のように聞こえます。ただし、このような詳細をリフレクションAPIの仕様に残しておくことができます。Oracleの意図としては、このセクションはJVMがクラスをロードする際の動作にのみ関することを目的としています。
NoClassDefFoundError
内のClassNotFoundException
のラップに関する詳細は、次のセクション5.3.1および5.3.2に移動しています。
(ここでの微妙な違いは、スーパークラスをロードするための回帰クラスが解決の一部(5.3.5、ステップ3)で実行される点です。したがって、スーパークラスのロードに失敗したクラス・ローダーの結果として生成されるClassNotFoundException
は、NoClassDefFoundError
でラップする必要があります。)
回帰に関する説明は、5.3.5に残しておくのが最適です。
正常に動作するクラス・ローダーは、3つの特性を有している必要があります。
同じ名前が与えられた場合、正常に動作するクラス・ローダーは常に同じ
Class
オブジェクトを返す必要があります。クラス・ローダーL1がCのロードを別のクラス・ローダーL2に委譲する場合、Cの直接スーパークラスまたは直接スーパーインタフェースとして、また、C内のフィールドの型として、またはC内のメソッドまたはコンストラクタの仮パラメータの型として、またはC内のメソッドの戻り型として生成される任意の型Tについて、L1およびL2は同じ
Class
オブジェクトを返す必要があります。ユーザー定義のクラス・ローダーがクラスおよびインタフェースのバイナリ表現をプリフェッチするか、関連するクラスのグループをまとめてロードする場合、プリフェッチまたはグループでのロードがなかったらロード・エラーが起きていた可能性があるプログラム内のポイントでのみロード・エラーを反映する必要があります。
Oracleではときどき、<
N, Ld>
という表記を使用してクラスまたはインタフェースを表すことがあります。この場合、Nはクラスまたはインタフェースの名前を示し、Ldはクラスまたはインタフェースを定義するローダーを示します。
また、NLiという表記を使用してクラスまたはインタフェースを表すこともあります。この場合、Nはクラスまたはインタフェースの名前を示し、Liはクラスまたはインタフェースを開始するローダーを示します。
5.3.1 ブートストラップ・クラス・ローダーを使用したロード作成
次のステップを使用して、ロードすることによって、ブートストラップ・クラス・ローダーを使用してNによって示される非配列クラスまたはインタフェースCを作成します。
最初に、Java仮想マシンが、Nによって示されるクラスまたはインタフェースを開始するローダーとしてブートストラップ・クラス・ローダーがすでに記録されているかどうかを確認します。記録されている場合、このクラスまたはインタフェースはCであり、クラスの作成は必要ありません。
記録されていない場合、Java仮想マシンは、引数Nをブートストラップ・クラス・ローダーのメソッドの呼出しに渡し、Cを検索ロードします。これには、プラットフォームに依存する方法で意図するCの表現を検索する操作、および5.3.5で見つかったアルゴリズムを使用してこの表現からCを導出する操作が含まれます。通常、クラスまたはインタフェースは階層ファイル・システム内のファイルを使用して表現され、クラスまたはインタフェースの名前はこのファイルのパス名内にエンコードされます。
意図する表現として見つかったものが有効であるかCの表現であることは保証されていません。ロードのこのフェーズでは、次のエラーを検出する必要があります。
- 意図するCの表現が見つかった場合、ロードによって
ClassNotFoundException
のインスタンスがスローされます。
この場合、Java仮想マシンは、5.3.5で見つかったアルゴリズムを使用した意図する表現のブートストラップ・クラス・ローダーを使用して、Nによって示されるクラスを導出しようとします。このクラスはCです。
意図するCの表現が見つかった場合、ブートストラップ・クラス・ローダーはClassNotFoundException
のインスタンスをスローします。この場合、Cの作成は失敗し、ブートストラップ・クラス・ローダーによって生成されたClassNotFoundException
が原因であるNoClassDefFoundError
が発生します。
意図する表現からのCの導出が失敗した場合、5.3.5に記載されているいずれかのエラーがブートストラップ・クラス・ローダーによってスローされる可能性があります。同じ理由でCの作成が失敗します。
クラスの導出をフォローアップ・ステップとして扱うと混乱します。使用する特定のAPIが指定されていない場合、loadClass
モデルはリーダーが想定する内容と一致します。この場合、loadClass
メソッドから戻った後、クラスはすでに導出されており、作成プロセスは完了しています(Java仮想マシンがClassNotFoundException
をラップする必要がある可能性がある点は除きます)。
5.3.2 ユーザー定義のクラス・ローダーを使用したロード作成
次のステップを使用して、ロードすることによって、ユーザー定義のクラス・ローダーLを使用してNによって示される非配列クラスまたはインタフェースCを作成します。
最初に、Java仮想マシンが、Nによって示されるクラスまたはインタフェースを開始するローダーとしてLがすでに記録されているかどうかを確認します。記録されている場合、このクラスまたはインタフェースはCであり、クラスの作成は必要ありません。
それ以外の場合、Java仮想マシンはLに対してloadClass(N)
を呼び出します。この呼出しによって返される値は、作成されたクラスまたはインタフェースCです。この場合、Java仮想マシンは、このLがCを開始するローダーであることを記録します(5.3.4)。このセクションの残りの部分では、このプロセスのプロセスについてさらに詳しく説明します。
ロード対象のクラスまたはインタフェースCの名前Nを使用してクラス・ローダーLのloadClass
メソッドが呼び出される場合、Lは、Cをロードするために次の2つの操作の1つを実行する必要があります。
クラス・ローダーLは、Cを表すバイトの配列を
ClassFile
構造体(4.1)のバイトとして作成できます。この場合、このクラス・ローダーは、クラスClassLoader
のメソッドdefineClass
を呼び出す必要があります。defineClass
を呼び出すと、Java仮想マシンが、5.3.5で見つかったアルゴリズムを使用したバイトの配列のLを使用して、Nによって示されるクラスまたはインタフェースを導出します。意図する表現からのCの導出が失敗した場合、5.3.5に記載されているいずれかのエラーが
loadClass
によってスローされる可能性があります。同じ理由でCの作成が失敗します。loadClass
によって生成されたクラスまたはインタフェースが名前Nを持つが、defineClass
によって導出されたクラスまたはインタフェースとは異なる場合、作成は失敗し、Java仮想マシンは、LinkageError
のインスタンスまたはLinkageError
のサブクラスをスローします。クラス・ローダーLは、Cのロードを他のクラス・ローダーLに委譲できます。これを実現するには、引数NをLに対するメソッド(通常は
loadClass
メソッド)の呼出しに直接または間接的に渡します。この呼出しの結果はCです。
(1)または(2)の場合、クラス・ローダーLがなんらかの理由により、Nによって示されるクラスまたはインタフェースをロードできない場合、ClassNotFoundException
のインスタンスをスローする必要があります。この場合、Cの作成は失敗し、ブートストラップ・クラス・ローダーによって生成されたClassNotFoundException
が原因であるNoClassDefFoundError
が発生します。
loadClass
呼出しの結果がnull
であるか、N以外の名前を持つロードされたクラスまたはインタフェースである場合、作成は失敗し、NoClassDefFoundError
がスローされます。
これらの追加ルールでは、JDK 14およびOpenJ9 0.15で見られた実際のエラー・チェックを捕捉しようとします。
ステップ1のチェックでは、2つの実装で異なるエラーを生成します。HotspotはLinkageError
をスローし、J9はNoClassDefFoundError
をスローします。
開始側のローダーとは異なるローダーによってdefineClass
が呼び出された場合、ステップ(1)のチェックは実行されません。これは、委譲されたロードが反射型呼出しであるため、これらのJVMレベルのロード・チェックの対象ではないからです。
すべきこと: クラス・ローダーが予期しない例外をスローした場合、何が起きますか?
JDK 1.1以降、OracleのJava仮想マシンの実装により、クラス・ローダーの
loadClass
メソッドが呼び出され、これにより、クラスまたはインタフェースがロードされます。loadClass
の引数は、ロード対象のクラスまたはインタフェースの名前です。loadClass
メソッドには2つの引数バージョンもあります。この場合、2番目の引数は、クラスまたはインタフェースをリンクするかどうかを示すboolean
です。JDK 1.0.2ではこの2つの引数バージョンのみが提供されていたため、OracleのJava仮想マシンの実装は、ロードされたクラスまたはインタフェースをリンクするためにこれを使用していました。JDK 1.1以降、OracleのJava仮想マシンの実装では、クラス・ローダーを使用することなく、クラスまたはインタフェースを直接リンクしています。
5.3.5 class
ファイル表現からのクラスの導出
ここで説明する導出プロセスは、厳密に指定された重要なロードおよび作成のサブプロセスです。これらの改訂により、この用語の使用がより顕著になります。
このプロセスのステップ3および4には、PermittedSubclasses
属性を適用するための新しいチェックが含まれます。
次のステップを使用して、class
ファイル形式の意図する表現からのローダーLを使用してNによって示される非配列クラスまたはインタフェースCのを作成します。Class
オブジェクト
このセクションでは、作成およびロードに対して補助的な役割のみを果たすClass
オブジェクトについては言及しません。かわりに、これらルールでは、名前、クラス・ローダーおよびバイト配列から抽象的な「クラスまたはインタフェース」を導出する方法について説明します。
最初に、Java仮想マシンが、
LがNによって示されるクラスまたはインタフェースを開始するローダーであることをすでに記録しているかどうかを確認します。記録している場合、この作成試行は無効であり、ロードにより、クラス・ローダーLのNという名前のクラスまたはインタフェースの導出試行が無効であるかどうかを確認します。無効である場合、導出によってLinkageError
がスローされます。LinkageError
がスローされます。次のいずれかが当てはまる場合、クラス・ローダーLのNという名前のクラスまたはインタフェースを導出しようとする試みは無効です。
Nという名前のクラスまたはインタフェースを開始するローダーとしてLがすでに記録されている。
Lがブートストラップ・クラス・ローダー(5.3)ではなく、Nが予約名
java/lang/Object
である。
2番目の事例を4.1での制約と組み合せることにより、クラス階層のルートが1つのみ存在するよう徹底します。これにより、名前
java/lang/Object
を使用してそれを確実に参照できるようになります。Java SE APIでは、
java.*
を含む特定のパッケージ内のクラスをロードしようとする不正な試みを阻止するために追加のセキュリティ制約が適用されています。実際に、このセキュリティ制約により、
Object
ルールをテストできなくなります。具体的には、JVMがこのチェックを実行する前にSecurityException
が発生します。しかし、内部的な一貫性のために、明言することが重要です。それ以外の場合、Java仮想マシンは、意図する表現を解析しようとします。ただし、意図する表現は実際にはCの有効な表現ではない場合があります。
ロード導出のこのフェーズでは、次のエラーを検出する必要があります。意図する表現がClassFile
構造体(4.1、4.8)ではない場合、ロードによってClassFormatError
のインスタンスがスローされます。バージョン番号がサポートされているかどうかを確認する前に
class
ファイルを解析しようとしても意味がありません。バージョンのチェックを最初に行う必要があります。それ以外の場合、意図する表現がサポートされているメジャー・バージョンまたはマイナー・バージョンではない(4.1)5番目~8番目のバイトでメジャー・バージョンまたはマイナー・バージョン番号を提供するが(4.1)、バージョン番号がこのJava仮想マシンの実装ではサポートされていない場合、ロード導出によってUnsupportedClassVersionError
のインスタンスがスローされます。ClassFormatError
のサブクラスであるUnsupportedClassVersionError
は、サポートされていないバージョンのclass
ファイル形式を表現が使用するクラスをロードする試みが原因であるClassFormatError
を簡単に特定できるようにするために導入されました。JDK 1.1以前では、クラスがシステム・クラス・ローダーまたはユーザー定義のクラス・ローダーのどちらによってロードされたかに応じて、サポートされていないバージョンの場合にNoClassDefFoundError
またはClassFormatError
のインスタンスがスローされていました。それ以外の場合、意図する表現が
ClassFile
構造体(4.1、4.8)ではない場合、導出によってClassFormatError
のインスタンスがスローされます。それ以外の場合、意図する表現がNという名前のクラスまたはインタフェースを実際に表していない場合、
ロード導出により、NoClassDefFoundError
のインスタンスまたはそのサブクラスのインスタンスがスローされます。このようになるのは、意図する表現に、N以外の名前を指定する
this_class
項目、またはACC_MODULE
フラグが設定されたaccess_flags
項目のいずれかが含まれる場合です。
Cに直接スーパークラスがある場合、Cから直接スーパークラスへのシンボリック参照は5.4.3.1のアルゴリズムを使用して解決されます。Cがインタフェースである場合、
Object
を直接スーパークラスとして持つ必要があります。ただし、これはすでにロードされている必要があります。直接スーパークラスを持たないのはObject
のみです。5.2に従って、任意の初期クラスまたはインタフェースが最初に作成されます。初期クラスまたはインタフェースがインタフェースである場合、これを導出すると、(まだロードされていない)
Object
の作成がここで再帰的に要求されます。ロード導出のこのフェーズの結果として、クラスまたはインタフェースの解決が原因でスローできる任意の例外をスローできます。また、ロード導出のこのフェーズでは、次のエラーを検出する必要があります。Cの直接スーパークラスとして名前が付けられたクラスまたはインタフェースが実際にインタフェースである場合、ロードによってIncompatibleClassChangeError
がスローされます。それ以外の場合、Cの任意のスーパークラスがC自体である場合直接スーパークラスを解決しようとした結果、現在のスレッドがローダーLを使用してNという名前のクラスを再帰的に導出しようとする場合、ロード導出によってClassCircularityError
がスローされます。スーパークラスを実際に最初に作成せずに、スーパークラスが「C自体」であることをどうすれば判定できますか。もちろん、このチェックを再帰的に適用せずにスーパークラスを作成することはできません。この依存関係ループは、Nという名前のクラスを導出する2回目の試行が行われたことが検出されたときに壊れます。
それ以外の場合、Cの直接スーパークラスとして名前が付けられたクラスまたはインタフェースがインタフェースまたは
final
クラスである場合、導出によってVerifyError
がスローされます。このチェックは長い間、検証の一部として指定されています(4.10、4.10.1)。ただし、JDK 14とOpenJ9は両方とも、それをクラス導出の一部として実行します。
このステージでの
VerifyError
は場違いですが、これは実際にスローされる例外です。JDK-8243582を参照してください。
これらの制約により、スーパークラスの
PermittedSubclasses
内のエントリがCに解決されるよう保証されます。(Oracleでは、参照を実際にCに解決することはありません。なぜなら、そのようにすると、Cの導出がまだ完了していないため、循環性が要求されるためです。)Java言語には追加要件があるため、名前が付いていないモジュールにクラスが属する場合、これらのクラスはともに同じパッケージに属する必要があります。これにより、プログラマがこの機能を適切に使用することが促進されますが(プログラマは様々なメンテナンス・ドメインにわたってsealed階層を広げようとすべきではありません)、これは、JVMが適用する必要があるような種類の基本的な制約ではありません。このため、別の言語またはバイトコード・ジェネレータには、パッケージ制約に従わない自由があります。
それ以外の場合、Cがクラスであり、Cで宣言された非
static
メソッドが、Cのスーパークラスで宣言されたfinal
の非static
メソッドをオーバーライド(5.4.5)できる場合、導出によってVerifyError
がスローされます。VerifyError
に関するコメントと同じです。
Cに直接スーパーインタフェースがある場合、Cから直接スーパーインタフェースへのシンボリック参照は5.4.3.1のアルゴリズムを使用して解決されます。
ロード導出のこのフェーズの結果として、クラスまたはインタフェースの解決が原因でスローできる任意の例外をスローできます。また、ロード導出のこのフェーズでは、次のエラーを検出する必要があります。Cの直接スーパーインタフェースとして名前が付けられたクラスまたはインタフェースが実際にインタフェースではない場合、ロードによってIncompatibleClassChangeError
がスローされます。それ以外の場合、Cの任意のスーパーインタフェースがC自体である場合直接スーパーインタフェースを解決しようとした結果、現在のスレッドがローダーLを使用してNという名前のクラスを再帰的に導出しようとする場合、ロード導出によってClassCircularityError
がスローされます。それ以外の場合、Cの直接スーパーインタフェースとして名前が付けられたクラスまたはインタフェースが実際にインタフェースではない場合、導出によって
IncompatibleClassChangeError
がスローされます。
それ以外の場合、直接スーパーインタフェースごとに、スーパーインタフェースが
PermittedSubclasses
属性(4.7.30)を持ち、次のいずれかが当てはまる場合、導出は失敗してIncompatibleClassChangeError
がスローされます。このスーパーインタフェースは、Cとは異なる実行時モジュールに属します。
Cには
ACC_PUBLIC
フラグ(4.1)が設定されておらず、このスーパーインタフェースは、Cとは異なる実行時パッケージに属します。スーパーインタフェースの
PermittedSubclasses
属性のclasses
表内のエントリが、名前Nを持つクラスまたはインタフェースを参照することはありません。
ステップ1以降、別のスレッドで、Java仮想マシンにより、Nという名前のクラスまたはインタフェースが定義側のクラス・ローダーとしてLを持つとマークされた場合、そのクラスまたはインタフェースはクラスの導出の結果であり、ステップ1~4で導出されたクラスまたはインタフェースは破棄されます。
これは、Hotspotの長期にわたる動作ですが、その仕様にはこれに対する備えはありません。
それ以外の場合、ステップ1~4で導出されたクラスまたはインタフェースは、クラスの導出の結果であり、Java仮想マシンは、定義側のクラス・ローダーとしてLを持つとしてCをマークし、LをCを開始するローダーとして記録します(5.3.4)。