このドキュメントでは、Java SE 16のプレビュー機能であるsealedクラスおよびインタフェースをサポートするためのJava仮想マシン仕様に対する変更について説明します。sealedクラスまたはインタフェースは、許可されたサブクラスまたはサブインタフェースの列挙セットに対する拡張と実装を制限します。この機能の概要は、JEPを参照してください。
セクション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つの属性が不可欠です。ConstantValueCodeStackMapTableBootstrapMethodsNestHostNestMembersPermittedSubclasses
バージョン番号がvである
classファイル内では、Java仮想マシンの実装がclassファイル形式のバージョンvをサポートしており、属性がclassファイル形式のバージョンv以前で最初に定義されたもので、その属性が出現先として定義されている場所に出現している場合、これらの各属性はJava仮想マシンの実装によって識別され、適切に読み取られます。9つの属性は、Java仮想マシンによる
classファイルの正しい解釈に不可欠ではありませんが、Java SEプラットフォームのクラス・ライブラリによるclassファイルの正しい解釈に不可欠であるか、ツールの役に立ちます(この場合、属性を規定するセクションは「オプション」として記載されています)。ExceptionsInnerClassesEnclosingMethodSyntheticSignatureSourceFileLineNumberTableLocalVariableTableLocalVariableTypeTable
バージョン番号がvである
classファイル内では、Java仮想マシンの実装がclassファイル形式のバージョンvをサポートしており、属性がclassファイル形式のバージョンv以前で最初に定義されたもので、その属性が出現先として定義されている場所に出現している場合、これらの各属性はJava仮想マシンの実装によって識別され、適切に読み取られます。13の属性は、Java仮想マシンによる
classファイルの正しい解釈に不可欠ではありませんが、Java SEプラットフォームのクラス・ライブラリによって公開されるclassファイルに関するメタデータを含むか、ツールによって使用可能になります(この場合、属性を規定するセクションは「オプション」として記載されています)。SourceDebugExtensionDeprecatedRuntimeVisibleAnnotationsRuntimeInvisibleAnnotationsRuntimeVisibleParameterAnnotationsRuntimeInvisibleParameterAnnotationsRuntimeVisibleTypeAnnotationsRuntimeInvisibleTypeAnnotationsAnnotationDefaultMethodParametersModuleModulePackagesModuleMainClass
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 | 60.65535 | 16 |
表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 |
60.65535 | 16 | 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 |
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 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)。