このドキュメントでは、Java SE 21プレビュー機能である名前のないクラスおよびインスタンスのmain
メソッドをサポートするためのJava言語仕様に対する変更について説明します。この機能の概要は、JEP 445を参照してください。
コンパニオン・ドキュメントでは、名前のないクラスおよびインタフェースのmain
メソッドをサポートするためのJava仮想マシン仕様に必要な変更について説明します。
変更は、JLSの既存のセクションについて説明しています。新しいテキストはこのように示され、削除されたテキストはこのように示されます。必要に応じて、説明と考察が端の方にグレーのボックスで囲まれて記載されています。
変更ログ:
2023-05-30: 編集上のマイナー変更。
2023-05-24: 編集上のその他の変更。
2023-05-19: 12.1.4 JEPの変更を反映して、候補のmain
メソッドの定義を変更します。
2023-05-15:
- JVMS付録ドキュメントへの参照を追加しました。
- 12.1.4 候補の
main
メソッドの説明を改善しました。
2023-05-02: 初稿リリース
第6章: 名前
6.7 完全修飾名および正規名
プリミティブ型、名前付きパッケージ、名前付きの最上位クラス、最上位インタフェースにはすべて次のような完全修飾名があります。
プリミティブ型の完全修飾名は、そのプリミティブ型のキーワード、つまり
byte
、short
、char
、int
、long
、float
、double
、またはboolean
です。ある名前付きパッケージのサブパッケージではない名前付きパッケージの完全修飾名は、その単純名です。
別の名前付きパッケージのサブパッケージである名前付きパッケージの完全修飾名は、それを含むパッケージの完全修飾名と、その後に続く「
.
」とサブパッケージの単純(メンバー)名で構成されます。名前のないパッケージで宣言されている名前付き最上位クラスまたは最上位インタフェースの完全修飾名は、クラスまたはインタフェースの単純名です。
名前付きパッケージで宣言されている最上位クラスまたは最上位インタフェースの完全修飾名は、そのパッケージの完全修飾名と、その後に続く「
.
」とそのクラスまたはインタフェースの単純名で構成されます。
各メンバー・クラス、メンバー・インタフェース、および配列型には次のように完全修飾名を付けることができます。
別のクラスまたはインタフェースCのメンバー・クラスまたはメンバー・インタフェースMには、Cに完全修飾名がある場合のみ完全修飾名が付けられます。
その場合、Mの完全修飾名は、Cの完全修飾名と、その後に続く「
.
」とMの単純名で構成されます。配列型には、その要素型に完全修飾名がある場合のみ、完全修飾名が付けられます。
その場合、配列型の完全修飾名は、その配列型の要素型の完全修飾名と、その後に続く「
[]
」で構成されます。
ローカル・クラス、ローカル・インタフェース、または匿名クラス、または名前のない最上位クラスに完全修飾名は付けられません。
すべてのプリミティブ型、名前付きパッケージ、名前付きの最上位クラス、および最上位インタフェースに正規名があります。
- すべてのプリミティブ型、名前付きパッケージ、最上位クラス、および最上位インタフェースの正規名は完全修飾名と同じです。
メンバー・クラス、メンバー・インタフェース、および配列型にはそれぞれ正規名を持たせることができます。
別のクラスまたはインタフェースCで宣言されたメンバー・クラスまたはメンバー・インタフェースMには、Cに正規名がある場合のみ正規名が付けられます。
その場合、Mの正規名はCと、その後に続く「
.
」およびMの単純名で構成されます。配列型には、コンポーネント型に正規名がある場合のみ正規名が付けられます。
その場合、配列型の正規名は、その配列型のコンポーネント型の正規名と、その後に続く「
[]
」で構成されます。
ローカル・クラス、ローカル・インタフェース、または匿名クラス、または名前のない最上位クラスに正規名は付けられません。
例6.7-1.完全修飾名
型
long
の完全修飾名は「long
」です。パッケージ
java.lang
の完全修飾名は、これがパッケージjava
のサブパッケージlang
であるため「java.lang
」です。パッケージ
java.lang
内で定義されているクラスObject
の完全修飾名は「java.lang.Object
」です。パッケージ
java.util
内で定義されているインタフェースEnumeration
の完全修飾名は「java.util.Enumeration
」です。「
double
の配列」の型の完全修飾名は「double[]
」です。「
String
の配列の配列の配列の配列」の型の完全修飾名は「java.lang.String[][][][]
」です。
コードでは、次の点に注意してください。
package points;
class Point { int x, y; }
class PointVec { Point[] vec; }
型Point
の完全修飾名は「points.Point
」、型PointVec
の完全修飾名は「points.PointVec
」、クラスPointVec
のフィールドvec
の完全修飾名は「points.Point[]
」です。
例6.7-2.完全修飾名および正規名
完全修飾名と正規名の違いは、コードでは次のように示されます。
package p;
class O1 { class I {} }
class O2 extends O1 {}
p.O1.I
とp.O2.I
はどちらもメンバー・クラスI
を示す完全修飾名ですが、p.O1.I
のみがその正規名です。
第7章: パッケージおよびモジュール
7.3 コンパイル・ユニット
CompilationUnitは、Javaプログラムの構文文法(2.3)の目標シンボル(2.1)です。これは、次のプロダクションによって定義されます。
- CompilationUnit:
- OrdinaryCompilationUnit
- UnnamedClassCompilationUnit
- ModularCompilationUnit
- OrdinaryCompilationUnit:
- [PackageDeclaration] {ImportDeclaration} {TopLevelClassOrInterfaceDeclaration}
- UnnamedClassCompilationUnit:
- {ImportDeclaration} {ClassMemberDeclarationNoMethod} MethodDeclaration {ClassMemberDeclaration}
- ClassMemberDeclarationNoMethod:
- FieldDeclaration
- ClassDeclaration
- InterfaceDeclaration
-
;
- ModularCompilationUnit:
- {ImportDeclaration} ModuleDeclaration
通常コンパイル・ユニットは3つの部分で構成され、それぞれがオプションです。
コンパイル・ユニットが属するパッケージの完全修飾名(6.7)を指定する、
package
宣言(7.4)。package
宣言がないコンパイル・ユニットは、名前のないパッケージ(7.4.2)に属します。他のパッケージ内のクラスおよびインタフェースや、クラスおよびインタフェースの
static
メンバーを、それらの単純名を使用して参照できるようにするimport
宣言(7.5)。クラスおよびインタフェース(7.6)の最上位宣言。
名前のないクラスのコンパイル・ユニットは、次のもので構成されます。
他のパッケージ内のクラスおよびインタフェースや、クラスおよびインタフェースの
static
メンバーを、それらの単純名を使用して参照できるようにする0以上のimport
宣言。少なくともその1つがメソッド宣言(8.4)である、暗黙的に宣言された最上位クラスのメンバー(8.2)の宣言。
これは、次のコンパイル単位があいまいな通常のコンパイル・ユニットであることを意味します。
import p.*; class Test { ... }
ただし、次のような名前のないクラスのコンパイル・ユニットはあいまいです。
import p.*; static void main(){ ... } class Test { ... }
名前のないクラスのコンパイル・ユニットは、次のプロパティを満たすクラスを暗黙的に宣言します。
常に最上位クラス(7.6)です。
常に名前のないクラスです(正規名も完全修飾名もありません(6.7))。
abstract
(8.1.1.1)ではありません。常に
final
(8.1.1.2)です。常に名前のないパッケージ(7.4.2)のメンバーであり、パッケージにアクセスできます。
直接スーパークラス型は常に
Object
(8.1.4)です。直接スーパーインタフェース型(8.1.5)はありません。
クラスの本体には、名前のないクラスのコンパイル・ユニットからのすべてのClassMemberDeclaration (フィールド(8.3)、メソッド(8.4)、メンバー・クラス(8.5)、およびメンバー・インタフェース(9.1.1.3))が含まれます。名前のないクラスのコンパイル・ユニットは、インスタンス・イニシャライザ(8.6)、静的イニシャライザ(8.7)、またはコンストラクタ(8.8)を宣言できません。
暗黙的に宣言されたデフォルト・コンストラクタ(8.8.9)があります。
このクラスのすべてのメンバー(暗黙的に宣言されたメンバーを含む)は、クラス内のメンバー宣言に関する通常のルールの対象になります。
このクラスが候補のmain
メソッド(12.1.4)を宣言しない場合、コンパイル時エラーが発生します。
名前のないパッケージには、メンバーとして複数の名前のないクラスがある場合があります。
モジュラ・コンパイル・ユニットはmodule
宣言(7.7)で構成され、オプションで、その前にimport
宣言が含められます。import
宣言を使用すると、このモジュールおよび他のモジュールのパッケージ内のクラスおよびインタフェースや、クラスおよびインタフェースのstatic
メンバーを、module
宣言内でそれらの単純名を使用して参照できます。
package
宣言の直後の各コンパイル・ユニットの先頭に宣言import java.lang.*;
が出現する場合と同様に、それぞれのコンパイル・ユニットによって、事前定義済のパッケージjava.lang
で宣言されたそれぞれのpublic
クラスまたはインタフェースが暗黙的にインポートされます。その結果、それらのクラスおよびインタフェースすべての名前をそれぞれのコンパイル・ユニットで単純名として使用できます。
ホスト・システムによって、観察可能なコンパイル・ユニットが特定されます。ただし、事前定義済のパッケージjava
とそのサブパッケージlang
およびio
内のコンパイル・ユニットは除きます(これらはすべて、常に観察可能です)。
それぞれの観察可能なコンパイル・ユニットは、次のようにモジュールに関連付けることができます。
ホスト・システムは、観察可能な通常コンパイル・ユニットが、ホスト・システムによって選択されたモジュールに関連付けられていることを判断できます。ただし、(i)事前定義済のパッケージ
java
とそのサブパッケージlang
およびio
内の通常コンパイル・ユニット(これらはすべて、java.base
モジュールに関連付けられる)、および(ii)名前のないパッケージ内の通常コンパイル・ユニット(これは、7.4.2で指定されたモジュールに関連付けられる)は除きます。ホスト・システムは、観察可能なモジュラ・コンパイル・ユニットがモジュラ・コンパイル・ユニットで宣言されたモジュールに関連付けられていることを判断する必要があります。
コンパイル・ユニットの可観測性はそのパッケージの可観測性(7.4.3)に影響し、可観測のコンパイル・ユニットとモジュールとの関連付けはそのモジュールの可観測性(7.7.6)に影響します。
モジュールMに関連付けられたモジュラ・コンパイル・ユニットおよび通常コンパイル・ユニットをコンパイルする場合、ホスト・システムは、Mの宣言で指定された依存を順守する必要があります。具体的には、ホスト・システムは、本来なら観察可能な通常コンパイル・ユニットを、Mから見えるもののみに制限する必要があります。Mから見える通常コンパイル・ユニットは、Mによって読み取られるモジュールに関連付けられた、観察可能な通常コンパイル・ユニットです。Mによって読み取られるモジュールは、java.lang.module
パッケージ仕様に記載されているように、Mを唯一のルート・モジュールとする解決の結果によって指定されます。ホスト・システムは、解決を実行して、Mによって読み取られるモジュールを特定する必要があります。java.lang.module
パッケージ仕様に記載されているいずれかの理由で解決が失敗すると、コンパイル時にエラーが発生します。
読取り可能性の関係は再帰的であるため、Mはそれ自体を読み取り、Mに関連付けられたモジュラ・コンパイル・ユニットおよび通常コンパイル・ユニットはすべて、Mから見えます。
Mによって読み取られるモジュールによって、Mから一意に見えるパッケージが決まり(7.4.3)、さらに、それによって、スコープ内の最上位パッケージと、Mに関連付けられたモジュラ・コンパイル・ユニットおよび通常コンパイル・ユニット内のコードのパッケージ名の意味の両方が決まります(6.3、6.5.3、6.5.5)。
前述のルールによって、モジュラ・コンパイル・ユニット内の注釈(特に、モジュール宣言に適用される注釈)で使用されるパッケージと型の名前が、モジュールに関連付けられた通常コンパイル・ユニットにそれらが出現する場合と同様に解釈されることが確実になります。
異なる通常コンパイル・ユニットで宣言されたクラスおよびインタフェースは、相互を循環的に参照することがあります。Javaコンパイラは、そのようなすべてのクラスおよびインタフェースを同時にコンパイルするように準備する必要があります。
第8章: クラス
クラス宣言では、新規クラスを定義し、その実装方法について記述します(8.1)。
最上位クラス(7.6)は、コンパイル・ユニットで直接宣言されたクラスです。
ネストしたクラスは、宣言が別のクラスまたはインタフェース宣言の本体内で行われるクラスです。ネストしたクラスは、メンバー・クラス(8.5、9.5)、ローカル・クラス(14.3)または無名クラス(15.9.5)である場合があります。
ネストしたクラスの一部の種類は内部クラス(8.1.3)であり、これは、包含クラス・インスタンス、ローカル変数および型変数を参照できるクラスです。
enumクラス(8.9)は、名前付きクラス・インスタンスの小さいセットを定義する省略構文で宣言されたクラスです。
レコード・クラス(8.10)は、値の単純集計を定義する省略構文で宣言されたクラスです。
非常に小さなプログラムや日常的な開発では、最上位クラスを正規名も完全修飾名(6.7)もないunnamedにできます。名前のないクラスは明示的には宣言されませんが、名前のないクラスのコンパイル・ユニット(7.3)によって暗黙的に宣言されます。名前のない最上位クラスは、プログラムの初期クラス(12.1.4)にできますが、それ自体を含むソース・コードの名前で参照することはできません。
この章では、すべてのクラスに共通するセマンティクスについて説明します。特定の種類のクラスに固有の詳細は、これらのコンストラクトに特化したセクションで説明します。
クラスがpublic
(8.1.1)として宣言される場合、そのモジュールの任意のパッケージ内のコードから、および場合によっては他のモジュール内のコードから参照できます。
クラスはabstract
(8.1.1.1),として宣言でき、これが不完全に実装されている場合はabstract
として宣言する必要があります。このようなクラスはインスタンス化できませんが、サブクラスによって拡張できます。クラスを拡張できる程度は、明示的に制御できます(8.1.1.2)。ここでは、サブクラスを制限するためにsealed
と宣言することも、サブクラスがないことを保証するためにfinal
と宣言することもできます。Object
を除く各クラスは、既存の単一のクラスの拡張(つまり、サブクラス) (8.1.4)であり、インタフェースを実装する場合があります(8.1.5)。
クラスは汎用(8.1.2)である場合があります。つまり、クラスは、クラスの様々なインスタンス間でバインディングが異なる可能性のある型変数を宣言しています。
クラス宣言は、他の種類の宣言と同じように、注釈(9.7)を使用して修飾できます。
クラスの本体は、メンバー(フィールド、メソッド、クラスおよびインタフェース)、インスタンスと静的イニシャライザ、およびコンストラクタ(8.1.7)を宣言します。メンバー(8.2)のスコープ(6.3)は、メンバーが属するクラスの宣言の本体全体です。フィールド、メソッド、メンバー・クラス、メンバー・インタフェースおよびコンストラクタ宣言には、アクセス修飾子public
、protected
またはprivate
が含まれる場合があります(6.6)。クラスのメンバーには、宣言されたメンバーと継承されたメンバーの両方が含まれます(8.2)。新しく宣言されたフィールドは、スーパークラスまたはスーパーインタフェース内で宣言されたフィールドを隠すことができます。新しく宣言されたメンバー・クラスおよびメンバー・インタフェースは、スーパークラスまたはスーパーインタフェース内で宣言されたメンバー・クラスおよびメンバー・インタフェースを隠すことができます。新しく宣言されたメソッドは、スーパークラスまたはスーパーインタフェース内で宣言されたメソッドを隠す、実装する、またはオーバーライドすることができます。
フィールド宣言(8.3)では、1回インカネーションされるクラス変数、およびクラスのインスタンスごとに新しくインカネーションされるインスタンス変数を記述します。フィールドはfinal
(8.3.1.2)として宣言できます。この場合、このフィールドは1回のみ割り当てることができます。任意のフィールド宣言にイニシャライザを含めることができます。
メンバー・クラス宣言(8.5)では、前後のクラスのメンバーであるネストしたクラスを記述します。メンバー・クラスはstatic
である場合があります。この場合、メンバー・クラスは、前後のクラスのインスタンス変数にアクセスできません。または、内部クラスである場合もあります。
メンバー・インタフェース宣言(8.5)では、前後のクラスのメンバーであるネストしたインタフェースを記述します。
メソッド宣言(8.4)では、メソッド呼出し式(15.12)によって呼び出すことのできるコードを記述します。クラス・メソッドはクラスに関連して呼び出され、インスタンス・メソッドは、クラスのインスタンスである特定のオブジェクトに関連して呼び出されます。宣言が実装方法を示していないメソッドは、abstract
として宣言する必要があります。メソッドはfinal
(8.4.3.3)として宣言される場合があります。この場合、このメソッドは隠したりオーバーライドすることができません。メソッドは、プラットフォーム依存のnative
コード(8.4.3.4)によって実装される場合があります。synchronized
メソッド(8.4.3.6)は、synchronized
文(14.19)で使用されるように、本体を実行する前にオブジェクトを自動的にロックし、戻り時にオブジェクトを自動的にロック解除します。これにより、そのアクティビティを他のスレッド(17)のアクティビティと同期できるようになります。
メソッド名はオーバーロードする場合があります(8.4.9)。
インスタンス・イニシャライザ(8.6)は、作成時にインスタンスの初期化をサポートするために使用できる実行可能コードのブロックです(15.9)。
静的イニシャライザ(8.7)は、クラスの初期化をサポートするために使用できる実行可能コードのブロックです。
コンストラクタ(8.8)はメソッドと似ていますが、メソッド呼出しによって直接呼び出すことはできません。コンストラクタは、新しいクラス・インスタンスを初期化するために使用されます。メソッドと同様、これはオーバーロードする場合があります(8.8.8)。
8.1 クラス宣言
クラス宣言では、クラスを指定します。
3種類のクラス宣言があります: 標準クラス宣言、enum宣言(8.9)およびレコード宣言(8.10)。
- ClassDeclaration:
- NormalClassDeclaration
- EnumDeclaration
- RecordDeclaration
- NormalClassDeclaration:
-
{ClassModifier}
class
TypeIdentifier [TypeParameters]
[ClassExtends] [ClassImplements] [ClassPermits] ClassBody
クラスは、名前のないクラスのコンプライアンス・ユニット(7.3)、クラス・インスタンス作成式(15.9.5)、およびクラス本体で終了するenum定数(8.9.1)によっても暗黙的に宣言されます。
クラス宣言内のTypeIdentifierは、クラスの名前を指定します。
クラスの単純名が前後のクラスまたはインタフェースの単純名と同じである場合は、コンパイル時にエラーが発生します。
クラス宣言のスコープおよびシャドウ化については、6.3および6.4.1に規定されています。
第9章 インタフェース
インタフェース宣言は、1つ以上のクラスによって実装できる新しいインタフェースを定義します。プログラムはインタフェースを使用して、そうしない場合は関連しないクラスに共通のサブタイプを提供でき、関連するクラスが共通のabstract
スーパークラスを共有する必要をなくします。
インタフェースにはインスタンス変数がなく、通常は1つ以上のabstract
メソッドを宣言し、そうでない場合、そのabstract
メソッドの実装を指定すると、関連しないクラスによってインタフェースを実装できます。インタフェースを直接インスタンス化することはできません。
最上位インタフェース(7.6)は、コンパイル・ユニットで直接宣言されたインタフェースです。
ネストしたインタフェースは、宣言が別のクラスまたはインタフェース宣言の本体内で行われるインタフェースです。ネストされたインタフェースはメンバー・インタフェース(8.5、9.5)またはローカル・インタフェース(14.3)にできます。
注釈インタフェース(9.6)は、注釈 (9.7)の反射型の表現によって実装することを意図した、特別な構文で宣言されるインタフェースです。
この章では、すべてのインタフェースに共通のセマンティクスについて説明します。特定の種類のインタフェースに固有の詳細は、これらのコンストラクトに特化したセクションで説明します。
インタフェースは他の1つ以上のインタフェースの直接拡張になるように宣言できます。つまり、それがオーバーライドまたは非表示にできるメンバーを除き、拡張するインタフェースのすべてのメンバー・クラスおよびインタフェース、インスタンス・メソッド、およびstatic
フィールドを継承します。
クラスは、1つ以上のインタフェースを直接実装するように宣言できます(8.1.5)。これは、インタフェースによって指定されたすべてのabstract
メソッドをそのクラスのインスタンスが実装することを意味します。クラスは必然的に、その直接スーパークラスおよび直接スーパーインタフェースが実装するすべてのインタフェースを実装します。こうした(複数の)インタフェース継承によって、オブジェクトは、スーパークラスを共有することなく、(複数の)共通動作をサポートできます。
クラスとは異なり、インタフェースをfinal
として宣言することはできません。ただし、インタフェースはsealed
(9.1.1.4)と宣言してサブクラスおよびサブインタフェースを制限できます。
宣言された型がインタフェース型である変数は、指定されたインタフェースを実装するクラスのインスタンスへの参照をその値として持つことができます。インタフェースのすべてのabstract
メソッドがクラスに実装されていても十分ではありません。クラス、またはそのスーパークラスのいずれかが、実際に、インタフェースを実装するように宣言されている必要があります。そうでない場合、クラスはインタフェースを実装するとみなされません。
クラスとは異なり、名前のない最上位インタフェース(7.3)を暗黙的に宣言することはできません。
第12章: 実行
この章では、プログラムの実行時に発生するアクティビティについて説明します。これは、Java仮想マシンのライフ・サイクルと、プログラムを形成するクラス、インタフェースおよびオブジェクトのライフ・サイクルに基づいて構成されています。
Java仮想マシンは、指定されたクラスまたはインタフェースをロードしてから、この指定されたクラスまたはインタフェースでメソッドmain
を呼び出して起動します。セクション12.1では、この章の概念の導入として、main
の実行に関連するロード、リンクおよび初期化のステップの概要を示します。その他のセクションでは、ロード(12.2)、リンク(12.3)、および初期化(12.4)について詳しく説明します。
この章では、新しいクラス・インスタンスを作成する手順(12.5)と、クラス・インスタンスを終了する手順(12.6)について引き続き説明します。最後に、クラスのアンロード(12.7)と、プログラム終了時に実行する手順(12.8)について説明します。
12.1 Java仮想マシンの起動
Java仮想マシンは、指定されたクラスまたはインタフェースのメソッドmain
を呼び出して起動します。このmain
メソッドに仮パラメータがある場合は、文字列の1つの配列である単一の引数が渡されます渡します。この仕様の例では、この最初のクラスは通常、Test
と呼ばれます。
Java仮想マシン起動の正確なセマンティクスは、Java仮想マシン仕様、Java SE 21 Editionの第5章に記載されています。ここでは、Javaプログラミング言語の視点から、このプロセスの概要を示します。
初期クラスまたはインタフェースをJava仮想マシンに指定する方法は、この仕様の範囲を超えていますが、コマンドラインを使用するホスト環境では一般的であり、コマンドライン引数として指定されるinitialクラスまたはインタフェースの完全修飾名、およびメソッドmain
の引数に指定される文字列として使用される次のコマンドライン引数に使用されます。元のコンパイル・ユニットが名前のないクラスのコンパイル・ユニット(7.3)であった場合、コンパイル・ユニットを含むファイルの名前は通常、初期クラスまたはインタフェースの名前の指定に使用されます。
たとえば、UNIX実装では、コマンドラインに次のように入力します。
java Test reboot Bob Dot Enzo
通常は、クラス
Test
(名前のないパッケージ内のクラス)のメソッドmain
を呼び出してJava仮想マシンを起動し、「reboot
」、「Bob
」、「Dot
」および「Enzo
」の4つの文字列を含む引数配列を渡します。
一方、ファイル
HelloWorld.java
に次の名前のないクラスのコンパイル・ユニットが含まれていた場合:void main() { System.out.println("Hello, World!"); }
コンパイル後のコマンドラインは次のようになります。
java HelloWorld
通常は、暗黙的に宣言された名前のないクラス(7.3)の
main
メソッドを呼び出してJava仮想マシンを起動すると、次のような出力が得られます。Hello, World!
ここでは、初期クラスまたはインタフェースを実行するためにJava仮想マシンが実行するステップの概要を説明します。ロード、リンクおよび初期化プロセスの例については、後のセクションで詳しく説明します。Test
12.1.1 初期クラスまたはインタフェースTest
のロード
Test
初期クラスまたはインタフェースのメソッドTest
main
を最初に実行しようとすると、クラスそれがロードされていない、つまりJava仮想マシンには現在、このクラスまたはインタフェースのバイナリ表現が含まれていないことが検出されます。次に、Java仮想マシンがクラス・ローダーを使用して、このようなバイナリ表現を見つけようとします。この処理に失敗した場合は、エラーがスローされます。このロード・プロセスについては、12.2で詳しく説明します。Test
12.1.2 初期クラスまたはインタフェースTest
のリンク: 検証、準備、(オプションの)解決
Test
クラスまたはインタフェースがロードされたら、メソッドTest
main
を呼び出す前に初期化する必要があります。また、すべてのクラスやインタフェースと同様に、これを初期化する前にリンクする必要があります。リンクには、検証、準備および(オプションの)解決が含まれます。リンクの詳細は、12.3を参照してください。Test
は、
検証では、クラスまたはインタフェースのロードされた表現が適切なシンボル・テーブルで整形式になっていることを確認します。検証では、クラスまたはインタフェースTest
を実装するコードが、Javaプログラミング言語およびJava仮想マシンのセマンティック要件に従っていることもチェックされます。検証中に問題が検出されると、エラーがスローされます。検証の詳細は、12.3.1を参照してください。Test
準備には、静的ストレージと、Java仮想マシンの実装によって内部的に使用されるデータ構造(メソッド表など)の割当てが含まれます。準備の詳細は、12.3.2を参照してください。
解決とは、前述の他のクラスおよびインタフェースをロードし、参照が正しいことを確認することによって、クラスまたはインタフェースから他のクラスおよびインタフェースへのシンボリック参照をチェックするプロセスです。Test
解決の手順は、初期リンケージの時点ではオプションです。実装では、再帰的に参照されるクラスおよびインタフェースからすべてのシンボリック参照を解決するポイントに至るまで、非常に早期にリンクされているクラスまたはインタフェースからシンボリック参照を解決できます。(この解決により、これらのロードおよびリンクの手順でエラーが発生する可能性があります。)この実装の選択肢は、C言語の単純な実装で長年使用されてきた「静的」リンクに似ています。(これらの実装では、コンパイルされたプログラムは通常、プログラムで使用されるライブラリ・ルーチンへの完全に解決されたリンクを含む、プログラムの完全なリンク・バージョンのある「a.out
」ファイルとして表されます。これらのライブラリ・ルーチンのコピーは、「a.out
」ファイルに含まれています。)
かわりに、実装では、シンボリック参照がアクティブに使用されている場合にのみ解決するように選択されます。この戦略のすべてのシンボリック参照に対する一貫した使用は、「最も遅い」解決になります。この場合、クラスまたはインタフェースに別のクラスへのシンボリック参照が複数ある場合、プログラムの実行中にそれらの参照が使用されるたびに1つずつ解決され、まったく使用されない場合は何も解決されません。Test
解決が実行される際の唯一の要件は、解決中に検出されたエラーが、プログラムのある時点でスローされることです。そこでは、エラーに関与するクラスまたはインタフェースへの直接的または間接的なリンクが必要なプログラムにより、アクションが実行されます。前述の「静的」な例の実装の選択肢を使用すると、初期クラスまたはインタフェースで示されているクラスまたはインタフェース、あるいはそれ以降の再帰的に参照されるクラスおよびインタフェースに関わるプログラムが実行される前に、ロードおよびリンクのエラーが発生する可能性があります。「最も遅い」解決を実装したシステムでは、不正なシンボリック参照がアクティブに使用されている場合にのみ、これらのエラーがスローされます。Test
解決プロセスの詳細は、12.3.3を参照してください。
12.1.3 初期クラスまたはインタフェーステスト: イニシャライザの実行
続く例では、Java仮想マシンは引き続き初期クラスまたはインタフェースTest
のメソッドmain
を実行しようとしています。これは、クラスが初期化されている場合のみ可能です(12.4.1)。
初期化は、初期クラスまたはインタフェースのクラス変数イニシャライザおよび静的イニシャライザのテキスト順の実行で構成されます。ただし、それTest
を初期化する前に、その直接スーパークラスだけでなく、その直接スーパークラスの直接スーパークラスも再帰的に初期化する必要があります。最も単純なケースでは、初期クラスまたはインタフェースTest
は暗黙的な直接スーパークラスとしてTest
Object
を持ちます。クラスObject
がまだ初期化されていない場合は、初期クラスまたはインタフェースの初期化より前に初期化する必要があります。クラスTest
Object
にはスーパークラスがないため、再帰はここで終了します。
初期クラスまたはインタフェースのスーパークラスとして別のクラスTest
Super
がある場合、初期クラスまたはインタフェースの前にTest
Super
を初期化する必要があります。これには、(まだ実行していない場合は) Super
のロード、検証および準備が必要です。また、実装によっては、Super
などのシンボリック参照の再帰的な解決も含まれる場合があります。
したがって、初期化によって、ロード、リンク、および初期化エラーが発生する可能性があります。これには他のクラスやインタフェースに関連するエラーも含まれます。
初期化プロセスの詳細は、12.4を参照してください。
12.1.4 Test.main
main
メソッドの起動
Test.main
最後に、初期クラスまたはインタフェースの初期化が完了した後(この間に、ロード、リンク、および初期化という他の結果が得られる場合があります)、初期クラスまたはインタフェースTest
のTest
main
メソッドが呼び出されます。main
メソッドmain
は、public
、static
およびvoid
として宣言する必要があります。宣言型がString
の配列である仮パラメータ(8.4.1)を指定します。したがって、次のいずれかの宣言を使用できます。
public static void main(String[] args)
public static void main(String... args)
初期クラスまたはインタフェースのメソッドは、main
という名前で次のいずれかが該当する場合、candidateです。
初期クラスまたはインタフェースで宣言された
static
メソッドであり、void
の結果、public
、protected
またはパッケージ・アクセス、および仮パラメータなし、または宣言された型がString
の配列である単一の仮パラメータのいずれかを持付場合。このような
static
メソッドは、初期クラスまたはインタフェースのスーパークラスから継承できない場合があります。初期クラスまたはインタフェースで宣言または継承されたインスタンス・メソッドであり、
void
の結果、public
、protected
またはパッケージ・アクセスがあり、仮パラメータなし、または宣言された型がString
の配列である単一の仮パラメータのいずれかがあり、さらに、初期クラスまたはインタフェースが内部クラスではない場合。
候補の
main
メソッドには、throws
句(8.4.6)がある場合があります。
main
メソッドで許可されるシグネチャは、Java SE 21で大幅に拡張されました。それ以前は、main
のシグネチャで可能な唯一のバリエーションは、単一の仮パラメータの型におけるString...
に対するString[]
でした。Java SE 21以上では、main
は12のシグネチャ(6つのstatic
と6つの非static
)のいずれかを持つことができます。String[]
が単一の仮パラメータの型でString...
と区別されている場合、この数は18に増加します。初期クラスまたはインタフェースがメンバー間で複数の候補
main
メソッドをカウントする場合、コンパイル時エラーにはなりません。非
static
のmain
メソッドを継承できるため、クラスまたはインタフェースにmain
メソッドが存在することはすぐには明らかになりません。たとえば、インタフェースのデフォルト・メソッドはインスタンス・メソッド(9.4)であるため、インタフェースを実装するクラスによって継承される候補となる場合があります。開発ツールでは、プログラムの開始を示すメンバーmain
メソッドがクラスまたはインタフェースにある場合に強調表示されます。Java SE 21で動作が変更され、継承された
static
main
メソッドは候補メソッドとは見なされなくなりました。main
メソッドのみがstatic
であり、継承されている既存の初期クラスまたはインタフェースは、引き続きプログラムの開始を示すようにリファクタリングする必要があります。
次のルールを適用する場合と同様に、初期クラスまたはインタフェースのmain
メソッドが呼び出されます。
仮パラメータを持つ
static
候補メソッドがある場合、このメソッドが呼び出され、引数配列(12.1)が渡されます。それ以外の場合は、仮パラメータのない
static
候補メソッドがあれば、そのメソッドが呼び出されます。それ以外の場合は、仮パラメータを含むインスタンス候補メソッドがある場合、仮パラメータのないコンストラクタ、
public
、protected
またはパッケージ・アクセスのいずれかを使用して作成された初期クラスのインスタンスで引数配列が渡され、このメソッドが呼び出されます。それ以外の場合は、仮パラメータのないインスタンス候補メソッドがある場合、仮パラメータのないコンストラクタ、
public
、protected
またはパッケージ・アクセスのいずれかを使用して作成された初期クラスのインスタンスでこのメソッドが呼び出されます。
呼び出される候補メソッドがない場合、またはインスタンス候補メソッドの呼出し時に初期クラスに適切なコンストラクタがない場合の実装の動作は、この仕様の範囲を超えています。
第13章: バイナリ互換性
13.1 バイナリの形式
プログラムは、Java仮想マシン仕様、Java SE 20 Editionによって規定されているclass
ファイル形式に、またはJavaプログラミング言語で作成されたクラス・ローダーによってこの形式にマップできる表現にコンパイルする必要があります。
クラスまたはインタフェース宣言に対応するclass
ファイルには、特定の特性が必要です。これらの特性の多くは、バイナリの互換性を確保するソース・コードの変換をサポートするように明確に選択されています。必須特性は次のとおりです。
クラスまたはインタフェースはバイナリ名によって名前を付ける必要があり、これは次の制約を満たす必要があります。
名前付きの最上位クラスまたはインタフェース(7.6)のバイナリ名はその正規名(6.7)です。名前のない最上位クラスのバイナリ名(7.3)は任意の有効な識別子(3.8)です。
コンパイル・ユニットがファイルに格納されるJava SEプラットフォームの単純な実装では、名前のない最上位のバイナリ名は通常、名前のない最上位クラスのコンパイル・ユニット(7.3)から拡張子(
.java
や.jav
など)を除いたファイル名になります。メンバー・クラスまたはインタフェース(8.5、9.5)のバイナリ名は、直前と直後のクラスまたはインタフェースのバイナリ名と、その後に続く
$
とメンバーの単純名で構成されます。ローカル・クラスまたはインタフェース(14.3)のバイナリ名の構成は、それを直接包含するクラスまたはインタフェースのバイナリ名、その後ろに
$
、空でない数字のシーケンス、ローカル・クラスの単純名が続きます。匿名クラス(15.9.5)のバイナリ名は、それを直接囲んでいるクラスまたはインタフェースのバイナリ名と、その後に続く
$
、および空でない数字のシーケンスで構成されます。汎用クラスまたはインタフェースによって宣言される型変数のバイナリ名(8.1.2、9.1.2)は、それを直接囲んでいるクラスまたはインタフェースのバイナリ名、その後ろに順に続く
$
、型変数の単純名で構成されます。汎用メソッド(8.4.4)で宣言された型変数のバイナリ名は、そのメソッドを宣言するクラスまたはインタフェースのバイナリ名と、その後に続く
$
、メソッドの記述子(JVMS §4.3.3)、$
、および型変数の単純名で構成されます。汎用コンストラクタ(8.8.4)で宣言された型変数のバイナリ名は、そのコンストラクタを宣言するクラスのバイナリ名と、その後に続く
$
、コンストラクタの記述子(JVMS §4.3.3)、$
、および型変数の単純名で構成されます。
別のクラスまたはインタフェースへの参照は、クラスまたはインタフェースのバイナリ名を使用するシンボリックである必要があります。
定数変数(4.12.4)であるフィールドへの参照は、コンパイル時に定数変数のイニシャライザが示す値Vに解決される必要があります。
このようなフィールドが
static
である場合、バイナリ・ファイルのコード内にフィールドへの参照が存在しない必要があります。これには、このフィールドを宣言したクラスまたはインタフェースも含まれます。このようなフィールドは常に、初期化(12.4.2)されているものとして示される必要があり、このフィールドのデフォルトの初期値は(Vとは異なる場合)、表示されないようにします。このようなフィールドが非
static
である場合、バイナリ・ファイルのコード内にフィールドへの参照が存在しない必要があります。ただし、このフィールドが含まれるクラス内は除きます。(インタフェースにはstatic
フィールドしかないため、これはインタフェースではなくクラスです。)このクラスには、インスタンス作成時(12.5)にフィールドの値をVに設定するためのコードが必要です。クラスC内でのフィールド・アクセスを示す正当な式が与えられ、定数値ではなく、(場合によっては別個の)クラスまたはインタフェースD内で宣言されているfという名前のフィールドを参照している場合、Oracleではフィールド参照の修飾クラスまたはインタフェースを次のように定義します。
式が単純名によって参照されているときに、fが現在のクラスまたはインタフェースCのメンバーである場合、QをCにします。そうでない場合、Qは、fがメンバーである、最も内側の字句的な包含クラスまたはインタフェース宣言にします。いずれの場合も、Qは参照の修飾クラスまたはインタフェースです。
参照の形式がTypeName
.
fであり、TypeNameがクラスまたはインタフェースを示している場合、TypeNameが示すクラスまたはインタフェースは参照のクラスまたはインタフェースの修飾型です。式の形式がExpressionName
.
fまたはPrimary.
fである場合は次のようになります。式の形式が
super.
fの場合、Cのスーパークラスは、参照の修飾クラスまたはインタフェースです。式の形式がTypeName
.super.
fの場合、TypeNameで示されるクラスのスーパークラスは、参照の修飾クラスまたはインタフェースです。
fへの参照は、参照の修飾クラスまたはインタフェースおよびフィールドの単純名fへのシンボリック参照にコンパイルする必要があります。
また、型が想定どおりであるかどうかを検証者がチェックできるように、この参照には、宣言されたフィールドの型のイレイジャへのシンボリック参照も含まれている必要があります。
クラスまたはインタフェースC内のメソッド呼出し式またはメソッド参照式が与えられ、(場合によっては別個の)クラスまたはインタフェースD内で宣言されている(または暗黙的な宣言されている(9.2)) mという名前のメソッドを参照している場合、Oracleではメソッド呼出しの修飾クラスまたはインタフェースを次のように定義します。
Dが
Object
である場合、メソッド呼出しの修飾クラスまたはインタフェースはObject
です。それ以外の場合:
メソッドが単純名によって参照されているときに、mが現在のクラスまたはインタフェースCのメンバーである場合、QをCにします。そうでない場合、Qは、mがメンバーである、最も内側の字句的な包含クラスまたはインタフェース宣言にします。いずれの場合も、Qはメソッド呼出しの修飾クラスまたはインタフェースです。
式の形式がTypeName
.
mまたはReferenceType::
mの場合、TypeNameによって示されるクラスまたはインタフェース、またはReferenceTypeのイレイジャは、メソッド呼び出しの修飾クラスまたはインタフェースです。式の形式がExpressionName
.
m、Primary.
m、ExpressionName::
mまたはPrimary::
mである場合、次のようになります。ExpressionNameまたはPrimaryのコンパイル時の型が交差型V1
&
...&
Vnである場合、メソッド呼出しの修飾クラスまたはインタフェースはV1のイレイジャです。そうでない場合、ExpressionNameまたはPrimaryのコンパイル時の型のイレイジャがメソッド呼出しの修飾クラスまたはインタフェースです。
式の形式が
super.
mまたはsuper::
mの場合、Cのスーパークラスは、メソッド呼出しの修飾クラスまたはインタフェースです。式の形式がTypeName
.super.
mまたはTypeName.super::
mであり、TypeNameがクラスXを示している場合、Xのスーパークラスがメソッド呼出しの修飾クラスまたはインタフェースです。また、TypeNameがインタフェースXを示している場合、Xがメソッド呼出しの修飾クラスまたはインタフェースです。
メソッドへの参照は、コンパイル時に、メソッド呼出しの修飾クラスまたはインタフェースと、メソッドの宣言されたシグネチャ(8.4.2)のイレイジャへのシンボリック参照に解決される必要があります。メソッドのシグネチャには、15.12.3で確認されたとおりに次のすべてが含まれている必要があります。
メソッドの単純名
メソッドに対するパラメータ数
各パラメータの型へのシンボリック参照
また、メソッドへの参照には、示されるメソッドの戻り型のイレイジャへのシンボリック参照が含まれているか、示されるメソッドが
void
を宣言され、値を戻さないことを示すものが含まれている必要があります。クラス・インスタンス作成式(15.9)、明示的なコンストラクタ呼出し文(8.8.7.1)、またはクラスまたはインタフェースC内のClassType
::
new
(15.13)形式のメソッド参照式が与えられ、(場合によっては別個の)クラスまたはインタフェースD内で宣言されているコンストラクタmを参照している場合、Oracleでは、コンストラクタ呼出しの修飾クラスを次のように定義します。式の形式が
new
D(...)
、ExpressionName.new
D(...)
、Primary.new
D(...)
またはD::
new
の場合、コンストラクタ呼出しの修飾クラスはDです。式の形式が
new
D(...){...}
、ExpressionName.new
D(...){...}
またはPrimary.new
D(...){...}
の場合、コンストラクタ呼出しの修飾クラスは式によって宣言される匿名クラスです。式の形式が
super(...)
、ExpressionName.super(...)
またはPrimary.super(...)
の場合、コンストラクタ呼出しの修飾クラスはCの直接スーパークラスです。式が
this(...)
の形式の場合、コンストラクタ呼出しの修飾クラスはCです。
コンストラクタへの参照は、コンパイル時に、コンストラクタ呼出しの修飾クラスとコンストラクタの宣言されたシグネチャ(8.8.2)へのシンボリック参照に解決される必要があります。コンストラクタのシグネチャには、次の両方が含まれている必要があります。
コンストラクタのパラメータ数
各仮パラメータの型へのシンボリック参照
クラスまたはインタフェースのバイナリ表現には、次もすべて含まれる必要があります。
これが
Object
ではなく、クラスである場合、このクラスの直接スーパークラスへのシンボリック参照。各直接スーパーインタフェースへのシンボリック参照(ある場合)。
フィールドの単純名およびフィールドの型のイレイジャへのシンボリック参照として与えられた、クラスまたはインタフェース内で宣言された各フィールドの仕様。
これがクラスである場合、各コンストラクタの消去されたシグネチャ(前述を参照)。
クラスまたはインタフェース内で宣言された各メソッド(インタフェースについては、暗黙的に宣言されたメソッドを除く(9.2))ごとに、消去されたシグネチャおよび戻り型(前述を参照)。
クラスまたはインタフェースを実装するために必要なコード。
すべてのクラスまたはインタフェースに、その正規名(6.7)をリカバリするために十分な情報が含まれている必要があります。
すべてのメンバー・クラスまたはインタフェースに、ソースレベルのアクセス修飾子をリカバリするために十分な情報が含まれている必要があります(6.6)。
すべてのネストしたクラスまたはインタフェースに、それを直接包含するクラスまたはインタフェース(8.1.3)へのシンボリック参照が含まれている必要があります。
すべてのクラスまたはインタフェースに、そのすべてのメンバー・クラスおよびインタフェース(8.5、9.5)へのシンボリック参照、およびその本体内で宣言されたネストした他のすべてのクラスおよびインタフェースへのシンボリック参照が含まれている必要があります。
Javaコンパイラによって発行されたコンストラクトは、
発行されたコンストラクトがクラス初期化メソッドでないかぎり(JVMS §2.9)、ソース・コードで明示的または暗黙的に宣言されたコンストラクトに対応していない場合、合成としてマークする必要があります。これには、2つの例外があります。Javaコンパイラによって発行されたコンストラクトは、ソース・コードで暗黙的に宣言された仮パラメータに対応している場合、必須としてマークする必要があります(8.8.1、8.8.9、8.9.3、15.9.5.1)。
次の仮パラメータは、ソース・コードで暗黙的に宣言されます。
参考までに、次のコンストラクタは、ソース・コードでは暗黙的に宣言されますが、
class
ファイル内で必須としてマークできるのは仮パラメータおよびモジュールのみであるため、必須としてはマークされません(JVMS §4.7.24、JVMS §4.7.25)。
モジュール宣言に対応するclass
ファイルには、バイナリ名がmodule-info
であり、スーパークラス、スーパーインタフェース、フィールドおよびメソッドを持たないクラスのclass
ファイルのプロパティが必要です。また、このモジュールのバイナリ表現には次のものがすべて含まれている必要があります。
module
の後ろに示す名前へのシンボリック参照として与えられたモジュールの名前の仕様。また、仕様では、モジュールが標準とオープンのどちらであるかも示す必要があります(7.7)。requires
ディレクティブが示すモジュールの名前へのシンボリック参照として与えられ、このディレクティブによって示される各依存の仕様(7.7.1)。また、仕様では、依存がtransitive
であるかどうか、および依存がstatic
であるかどうかも示す必要があります。exports
またはopens
ディレクティブが示すパッケージの名前へのシンボリック参照として与えられ、これらのディレクティブが示す各パッケージの仕様(7.7.2)。また、ディレクティブが修飾されていた場合、仕様では、ディレクティブのto
句が示すモジュールの名前へのシンボリック参照を与える必要があります。uses
ディレクティブが示すクラスまたはインタフェースの名前へのシンボリック参照として指定された、このディレクティブが示す各サービスの仕様(7.7.3)。provides
ディレクティブのwith
句が示すクラスおよびインタフェースの名前へのシンボリック参照として指定された、このディレクティブが示すサービス・プロバイダの仕様(7.7.4)。また、仕様では、ディレクティブがサービスとして示すクラスまたはインタフェースの名前へのシンボリック参照を指定する必要があります。
次の各セクションでは、既存のバイナリとの互換性を損なわずにクラスおよびインタフェース宣言に対して加えられる可能性のある変更点について説明します。前述の翻訳要件に基づき、Java仮想マシンおよびそのclass
ファイル形式はこれらの変更をサポートしています。前述の要件に基づくクラス・ローダーによってclass
ファイルにマップし戻される圧縮または暗号化された表現など、他の有効なバイナリ形式もすべて、これらの変更を必然的にサポートします。