このドキュメントでは、Java SE 24のプレビュー機能である単純なソース・ファイルおよびインスタンスのmain
メソッドをサポートするための、Java言語仕様に対する変更点について説明します。この機能の概要は、JEP 495を参照してください。
この機能は、モジュール・インポート宣言という別の機能とともに開発されています。この機能の概要は、JEP 494を参照してください。このドキュメントは、Java言語仕様にコンパニオン・ドキュメントで説明されている変更がすでに適用されていることを前提としています。
コンパニオン・ドキュメントでは、単純なソース・ファイルおよびインスタンスのmain
メソッドをサポートするためにJava仮想マシン仕様に対して必要となった変更点について説明します。
変更は、JLSの既存のセクションについて説明しています。新しいテキストはこのように示され、削除されたテキストはこのように示されます。必要に応じて、説明と考察が端の方にグレーのボックスで囲まれて記載されています。
変更ログ:
2024-11-01: クラスjava.io.IO
に2つの新しいメソッドが追加されました。(7.3)
2024-10: 第4プレビューの初稿。第3プレビュー仕様からの主な変更:
- 欠落している項7.4.2が追加されました
第6章: 名前
6.1 宣言
宣言は、次のいずれかのエンティティをプログラムに導入します:
module
宣言で宣言されたモジュール(7.7)package
宣言で宣言されたパッケージ(7.4)単一型インポート宣言またはオンデマンド型インポート宣言(7.5.1、7.5.2)で宣言された、インポートされたクラスまたはインタフェース
単一静的インポート宣言またはオンデマンド静的インポート宣言で宣言された、インポートされた
static
メンバー(7.5.3、7.5.4)標準クラス宣言(8.1)、enum宣言(8.9)
またはレコード宣言(8.10)によって宣言されたか、単純コンパイル・ユニットによって暗黙的に宣言されたクラス(7.3)汎用クラス、インタフェース、メソッドまたはコンストラクタの宣言の一部として宣言された型パラメータ(8.1.2、9.1.2、8.4.4、8.8.4)
参照型のメンバー(8.2、9.2、8.9.3、9.6、10.7)で、次のいずれか:
フィールドで、次のいずれか:
メソッドで、次のいずれか:
enum定数(8.9.1)
レコード・コンポーネント(8.10.3)
仮パラメータで、次のいずれか:
try
文のcatch
句で宣言された例外ハンドラの例外パラメータ(14.20)ローカル変数で、次のいずれか:
ローカル・クラスまたはインタフェース(14.3)で、次のいずれか:
標準クラス宣言によって宣言されたローカル・クラス
enum宣言によって宣言されたローカル・クラス
レコード宣言によって宣言されたローカル・クラス
標準インタフェース宣言で宣言されたローカル・インタフェース
コンストラクタ(8.8、8.10.4)も宣言によって導入されますが、新しい名前が導入されるのではなく、そのコンストラクタが宣言されたクラスの名前が使用されます。
この項の残りの部分に変更はありません。
6.3 宣言のスコープ
宣言のスコープは、単純名を使用して、その宣言によって宣言されたエンティティを参照できるプログラム内の領域です(ただし、シャドウ化されていることが条件です) (6.4.1)。
宣言がプログラム内の特定のポイントでスコープ内にあるとされるのは、宣言のスコープにそのポイントが含まれる場合のみです。
観察可能な最上位パッケージ(7.4.3)の宣言のスコープは、パッケージが一意として表示されるモジュールに関連付けられたすべての観察可能なコンパイル・ユニットです(7.4.3)。
観察可能でないパッケージの宣言はスコープ内ではありません。
サブパッケージの宣言はスコープ内ではありません。
パッケージjava
は常にスコープ内にあります。
単一型インポート宣言(7.5.1)またはオンデマンド型インポート宣言(7.5.2)によってインポートされるクラスまたはインタフェースのスコープは、モジュール宣言(7.7)、およびimport
宣言が出現するコンパイル・ユニットのすべてのクラス宣言およびインタフェース宣言(8.1、9.1)、ならびにコンパイル・ユニットのモジュール宣言またはパッケージ宣言の注釈です。
単一静的インポート宣言(7.5.3)またはオンデマンド静的インポート宣言(7.5.4)によってインポートされるメンバーのスコープは、モジュール宣言、およびimport
宣言が出現するコンパイル・ユニットのすべてのクラス宣言およびインタフェース宣言、ならびにコンパイル・ユニットのモジュール宣言またはパッケージ宣言の注釈です。
通常コンパイル・ユニットで宣言される最上位クラスまたはインタフェース(7.6)のスコープは、最上位クラスまたはインタフェースが宣言されているパッケージ内のすべてのクラス宣言およびインタフェース宣言です。
単純コンパイル・ユニット(7.3)での最上位クラスの暗黙宣言のスコープは空であり、クラスはスコープ内にありません。
クラスまたはインタフェースCによって宣言または継承されたメンバーm (8.2、9.2)の宣言のスコープは、Cの本体全体(ネストされたクラスまたはインタフェースの宣言を含む)です。Cがレコード・クラスである場合、mのスコープには、さらにCのレコード宣言のヘッダーが含まれます。
この項の残りの部分に変更はありません。
6.7 完全修飾名および正規名
プリミティブ型、名前付きパッケージ、最上位クラス、最上位インタフェースにはすべて次のような完全修飾名があります。
プリミティブ型の完全修飾名は、そのプリミティブ型のキーワード、つまり
byte
、short
、char
、int
、long
、float
、double
、またはboolean
です。ある名前付きパッケージのサブパッケージではない名前付きパッケージの完全修飾名は、その単純名です。
別の名前付きパッケージのサブパッケージである名前付きパッケージの完全修飾名は、それを含むパッケージの完全修飾名と、その後に続く「
.
」とサブパッケージの単純(メンバー)名で構成されます。名前のないパッケージで宣言されている最上位クラスまたは最上位インタフェースの完全修飾名は、クラスまたはインタフェースの単純名です。
具体的に述べると、これは、単純コンパイル・ユニット(7.3)によって暗黙的に宣言された最上位クラスに完全修飾名(ホスト・システムによって決定される単純名)があるということです(8.1.8)。
名前付きパッケージで宣言されている最上位クラスまたは最上位インタフェースの完全修飾名は、そのパッケージの完全修飾名と、その後に続く「
.
」とそのクラスまたはインタフェースの単純名で構成されます。
各メンバー・クラス、メンバー・インタフェース、および配列型には次のように完全修飾名を付けることができます。
別のクラスまたはインタフェース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
- SimpleCompilationUnit
- ModularCompilationUnit
- OrdinaryCompilationUnit:
- [PackageDeclaration] {ImportDeclaration} {TopLevelClassOrInterfaceDeclaration}
- SimpleCompilationUnit:
- {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)の最上位宣言。
単純なコンパイル・ユニットは、クラス・メンバー宣言の空でないシーケンスで構成され、そのうちの少なくとも1つはメソッド宣言(8.4)である必要があり、オプションでその前にimport
宣言が指定されます。メソッド、フィールド、クラスおよびインタフェースの宣言は、クラス本体(8.1.7)においてと同様に、import
宣言の後(存在する場合)、任意の順序で可能です。
これは、次のコンパイル単位があいまいな通常のコンパイル・ユニットであることを意味します。
import p.*; class Test { ... }
一方で、次のものは明らかに単純コンパイル・ユニットです。
import p.*; static void main(){ ... } class Test { ... }
単純コンパイル・ユニットでは、単純コンパイル・ユニット内で宣言されているメソッド、フィールド、クラスおよびインタフェースがメンバーである最上位クラス(7.6)が、暗黙的に宣言されます。クラスの詳細は、8.1.8を参照してください。
モジュラ・コンパイル・ユニットはmodule
宣言(7.7)で構成され、オプションで、その前にimport
宣言が含められます。import
宣言を使用すると、このモジュールおよび他のモジュールのパッケージ内のクラスおよびインタフェースや、クラスおよびインタフェースのstatic
メンバーを、module
宣言内でそれらの単純名を使用して参照できます。
package
宣言の直後の各コンパイル・ユニットの先頭に宣言import java.lang.*;
が出現する場合と同様に、それぞれのコンパイル・ユニットによって、事前定義済のパッケージjava.lang
で宣言されたそれぞれのpublic
クラスまたはインタフェースが暗黙的にインポートされます。その結果、それらのクラスおよびインタフェースすべての名前をそれぞれのコンパイル・ユニットで単純名として使用できます。
すべての単純コンパイル・ユニットは、それぞれの単純コンパイル・ユニット(7.5.5)の先頭に宣言import module java.base;
があるかのように、事前定義済モジュールjava.base
でエクスポートされたすべてのパッケージで宣言されているすべてのpublic
クラスまたはインタフェースをオンデマンドで暗黙的にインポートします。その結果、それらのクラスとインタフェースのすべての名前をそれぞれの単純コンパイル・ユニットで単純名として使用できます。
モジュール・インポート宣言機能は、JEP 494で提案され、コンパニオン・ドキュメントで規定されています
すべての単純コンパイル・ユニットは、単純コンパイル・ユニットの最初に宣言import static java.io.IO.*;
がモジュールjava.base;
をインポートする宣言の直後にあるかのように、クラスjava.io.IO
からすべてのstatic
メンバーを暗黙的にインポートします。その結果、それらのstatic
メンバーのすべての名前をそれぞれの単純コンパイル・ユニットで単純名として使用できます。
java.io.IO
クラスは、コンソールとの最も基本的なテキストI/Oを提供する次の5つのstatic
メソッドを宣言します:public static void println(Object obj) ... public static void println() ... public static void print(Object obj) ... public static String readln(String prompt) ... public static String readln() ...
これにより、単純コンパイル・ユニットのコードはインポート宣言なしでコンソールとの基本的な対話型操作を簡単に実行できます。次に、有名な"Hello World"プログラムに対応する単純コンパイル・ユニットを示します:
void main() { println("Hello, World!"); }
また、最も単純な対話型プログラムは、次の単純コンパイル・ユニットのように記述できます:
void main() { String name = readln("Please enter your name: "); print("Pleased to meet you, "); println(name); }
ホスト・システムによって、観察可能なコンパイル・ユニットが特定されます。ただし、事前定義済のパッケージ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コンパイラは、そのようなすべてのクラスおよびインタフェースを同時にコンパイルするように準備する必要があります。
7.4 パッケージ宣言
7.4.2 名前のないパッケージ
単純なコンパイル・ユニット、またはpackage
宣言がないが他の種類の1つ以上の宣言がある通常のコンパイル・ユニットは、名前のないパッケージの一部です。
名前のないパッケージは、主に小規模または一時的なアプリケーションの開発時または開発の開始時に利便性のために、Java SEプラットフォームによって提供されます。
名前のないパッケージにはサブパッケージを含めることはできません。これは、package
宣言の構文には常に名前付きの最上位パッケージへの参照が含まれているためです。
Java SEプラットフォームの実装では、名前のないパッケージが少なくとも1つサポートされている必要があります。実装では、複数の名前のないパッケージをサポートできますが、必須ではありません。名前のない各パッケージ内の通常のコンパイル・ユニットは、ホスト・システムによって判断されます。
ホスト・システムは、名前のないパッケージ内の通常のコンパイル・ユニットを名前付きモジュールではなく名前のないモジュール(7.7.5)に関連付ける必要があります。
例7.4.2-1名前のないパッケージ
コンパイル・ユニット:
class FirstCall {
public static void main(String[] args) {
System.out.println("Mr. Watson, come here. "
+ "I want you.");
}
}
名前のないパッケージの一部として、非常に単純な基本コンパイル・ユニットを定義します。
パッケージの格納に階層ファイル・システムを使用するJava SEプラットフォームの実装では、一般的な戦略の1つは、名前のないパッケージを各ディレクトリに関連付けることです。一度に識別できる名前のないパッケージは1つのみで、つまり「現在の作業ディレクトリ」に関連付けられているパッケージです。「現在の作業ディレクトリ」の正確な意味は、ホスト・システムによって異なります。
7.6 最上位クラスおよびインタフェースの宣言
最上位クラスまたはインタフェースの宣言では、最上位クラス(8.1)または最上位インタフェース(9.1)が宣言されます。
- TopLevelClassOrInterfaceDeclaration:
- ClassDeclaration
- InterfaceDeclaration
-
;
コンパイル・ユニット内のクラス宣言およびインタフェース宣言のレベルで余分な"
;
"トークンが出現しても、コンパイル・ユニットの意味に影響はありません。Javaプログラミング言語では、不要なセミコロンは、クラス宣言の後に;
を配置することに慣れているC++プログラマのための便宜的措置としてのみ許容されています。新しいJavaコードでは使用しないでください。
トップ・レベル・クラスは、単純なコンパイル・ユニットによって暗黙的に宣言(8.1.8)することもできます。
アクセス修飾子がない場合、最上位クラスまたはインタフェースはパッケージ・アクセス権を持つため、宣言されたパッケージの通常コンパイル・ユニット内でのみアクセスできます(6.6.1)。クラスまたはインタフェースをpublic
として宣言して、同じモジュールの他のパッケージのコードから(および場合によっては他のモジュールのパッケージのコードから)クラスまたはインタフェースにアクセスすることもできます。
最上位クラスまたはインタフェースの宣言にprotected
、private
またはstatic
のアクセス修飾子のいずれかが含まれている場合は、コンパイル時にエラーが発生します。
最上位クラスまたはインタフェースの名前が、同じパッケージで宣言されている他の最上位クラスまたはインタフェースの名前として出現する場合は、コンパイル時にエラーが発生します。
最上位クラスまたはインタフェースのスコープおよびシャドウ化については、6.3および6.4に規定されています。
最上位クラスまたはインタフェースの完全修飾名については、6.7に規定されています。
この項の残りの部分に変更はありません。
第8章: クラス
8.1 クラス宣言
クラス宣言では、クラスを指定します。
3種類のクラス宣言があります: 標準クラス宣言、enum宣言(8.9)およびレコード宣言(8.10)。
- ClassDeclaration:
- NormalClassDeclaration
- EnumDeclaration
- RecordDeclaration
- NormalClassDeclaration:
-
{ClassModifier}
class
TypeIdentifier [TypeParameters]
[ClassExtends] [ClassImplements] [ClassPermits] ClassBody
クラスは、クラス・インスタンス作成式(15.9.5)、およびクラス本体で終了するenum定数(8.9.1)によっても暗黙的に宣言されます。
一部のクラスは、他の構成要素によって暗黙的に宣言されます(8.1.8)。
クラス宣言内のTypeIdentifierは、クラスの名前を指定します。
クラスの単純名が前後のクラスまたはインタフェースの単純名と同じである場合は、コンパイル時にエラーが発生します。
クラス宣言のスコープおよびシャドウ化については、6.3および6.4.1に規定されています。
8.1.8 暗黙的に宣言されたクラス
すべてのクラスがクラス宣言によって示されるわけではありません。次の構成要素により、クラスが暗黙的に宣言されます。
どの場合も、暗黙的に宣言されたクラスのメンバー(暗黙的に宣言されたメンバーなど)は、クラス内のメンバー宣言に関する通常のルールの対象になります。
便宜上、ここでは7.3の次のプロダクションを示します。
- SimpleCompilationUnit:
- {ImportDeclaration} {ClassMemberDeclarationNoMethod} MethodDeclaration {ClassMemberDeclaration}
- ClassMemberDeclarationNoMethod:
- FieldDeclaration
- ClassDeclaration
- InterfaceDeclaration
;
単純コンパイル・ユニットによって暗黙的に宣言されたクラスには、次の特性があります。
最上位クラス(7.6)です。
その名前は、ホスト・システムによって決定される有効な識別子(3.8)です。
コンパイル・ユニットがファイルに格納される、Java SEプラットフォームの単純な実装では、この暗黙的に宣言されたクラスの名前は、通常は、単純コンパイル・ユニットを含むファイルの名前から拡張子(
.java
や.jav
など)を除いたものになります。abstract
(8.1.1.1)ではありません。final
(8.1.1.2)です。名前のないパッケージ(7.4.2)のメンバーであり、パッケージにアクセスできます。
その直接スーパークラスは
Object
(8.1.4)です。直接スーパーインタフェース(8.1.5)はありません。
クラスの本体には、単純コンパイル・ユニットで宣言されたすべてのクラス・メンバーが含まれます(これらは、フィールド(8.3)、メソッド(8.4)、メンバー・クラス(8.5)およびメンバー・インタフェース(9.1.1.3)の宣言です)。単純コンパイル・ユニットでは、インスタンス・イニシャライザ(8.6)、静的イニシャライザ(8.7)およびコンストラクタ(8.8)を宣言できません。
暗黙的に宣言されたデフォルト・コンストラクタ(8.8.9)があります。
このクラスが候補のmain
メソッド(12.1.4)を宣言しない場合、コンパイル時エラーが発生します。
なお、名前のないパッケージには、複数の暗黙的に宣言されたクラスがメンバーとして含まれている場合があります。
第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 22 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
通常は、暗黙的に宣言されたクラス(8.1.8)の
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です。
それには、宣言された型が
String
の配列である単一の仮パラメータ(8.4.1)があり、void
の結果があり、public
、protected
またはパッケージ・アクセスがあります。それには仮パラメータおよび
void
の結果はなく、public
、protected
またはパッケージ・アクセスがあります。
なお、候補メソッドは、
static
またはインスタンス・メソッドのどちらかです。候補メソッドには、throws
句(8.4.6)を含めることもできます。Java SE 24では、
main
メソッドの形式が大幅に拡張されました。それ以前は、public
static
である必要があり、単一の仮パラメータが必要でした。その単一の仮パラメータの型については、使用可能な唯一のバリエーションはString[]
とString...
でした。Java 24以上では、アクセス修飾子、static
修飾子および仮パラメータに応じて、12種類の形式を使用できます。String[]
が単一の仮パラメータの型でString...
と区別されている場合、この数は18に増加します。なお、初期クラスまたはインタフェースでそのメンバー間の複数の候補メソッドがカウントされる場合、コンパイル時エラーにはなりません。
main
メソッドが継承される場合があるため、クラスまたはインタフェースに候補メソッドが存在することは、すぐには明らかになりません。たとえば、インタフェースのデフォルト・メソッドはインスタンス・メソッド(9.4)であるため、インタフェースを実装するクラスによって継承される候補となる場合があります。開発ツールでは、プログラムの開始を示すメンバーmain
メソッドがクラスまたはインタフェースにある場合に強調表示されます。
次のルールを適用する場合と同様に、初期クラスまたはインタフェースのmain
メソッドが呼び出されます。
仮パラメータが1つある候補メソッドがある場合は、このメソッドが呼び出されます。候補メソッドが
static
の場合は、引数配列(12.1)を渡して直接それが呼び出されます。候補メソッドがインスタンス・メソッドの場合は、仮パラメータのないコンストラクタ、およびpublic
、protected
またはパッケージ・アクセスのいずれかを使用することで作成された初期クラスのインスタンスで、引数配列を渡してそれが呼び出されます。それ以外の場合は、仮パラメータのない候補メソッドがあれば、このメソッドが呼び出されます。候補メソッドが
static
の場合は、それが直接呼び出されます。候補メソッドがインスタンス・メソッドの場合は、仮パラメータのないコンストラクタ、およびpublic
、protected
またはパッケージ・アクセスのいずれかを使用することで作成された初期クラスのインスタンスで、それが呼び出されます。
なお、クラスに
static
と、同じシグネチャのインスタンス・メソッド(8.4.2)を両方含めることはできません。呼び出される候補メソッドがない場合、またはインスタンス候補メソッドの呼出し時に初期クラスに適切なコンストラクタがない場合の実装の動作は、この仕様の範囲を超えています。
第13章: バイナリ互換性
13.1 バイナリの形式
プログラムは、Java仮想マシン仕様、Java SE 23 Editionによって規定されているclass
ファイル形式に、またはJavaプログラミング言語で作成されたクラス・ローダーによってこの形式にマップできる表現にコンパイルする必要があります。
クラスまたはインタフェース宣言に対応するclass
ファイルには、特定の特性が必要です。これらの特性の多くは、バイナリの互換性を確保するソース・コードの変換をサポートするように明確に選択されています。必須特性は次のとおりです。
クラスまたはインタフェースはバイナリ名によって名前を付ける必要があり、これは次の制約を満たす必要があります。
最上位クラスまたはインタフェース(7.6)のバイナリ名はその正規名(6.7)です。
なお、単純コンパイル・ユニットによって暗黙的に宣言された最上位クラスの正規名は、ホスト・システムによって決定されます(8.1.8)。
メンバー・クラスまたはインタフェース(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)。
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
ファイルにマップし戻される圧縮または暗号化された表現など、他の有効なバイナリ形式もすべて、これらの変更を必然的にサポートします。