単純なソース・ファイルおよびインスタンスのmainメソッド(第4プレビュー)

Java®言語仕様の変更点・バージョン24+36-3646

このドキュメントでは、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プレビュー仕様からの主な変更:

第6章: 名前

6.1 宣言

宣言は、次のいずれかのエンティティをプログラムに導入します:

コンストラクタ(8.88.10.4)も宣言によって導入されますが、新しい名前が導入されるのではなく、そのコンストラクタが宣言されたクラスの名前が使用されます。

この項の残りの部分に変更はありません。

6.3 宣言のスコープ

宣言のスコープは、単純名を使用して、その宣言によって宣言されたエンティティを参照できるプログラム内の領域です(ただし、シャドウ化されていることが条件です) (6.4.1)。

宣言がプログラム内の特定のポイントでスコープ内にあるとされるのは、宣言のスコープにそのポイントが含まれる場合のみです。

観察可能な最上位パッケージ(7.4.3)の宣言のスコープは、パッケージが一意として表示されるモジュールに関連付けられたすべての観察可能なコンパイル・ユニットです(7.4.3)。

観察可能でないパッケージの宣言はスコープ内ではありません。

サブパッケージの宣言はスコープ内ではありません。

パッケージjavaは常にスコープ内にあります。

単一型インポート宣言(7.5.1)またはオンデマンド型インポート宣言(7.5.2)によってインポートされるクラスまたはインタフェースのスコープは、モジュール宣言(7.7)、およびimport宣言が出現するコンパイル・ユニットのすべてのクラス宣言およびインタフェース宣言(8.19.1)、ならびにコンパイル・ユニットのモジュール宣言またはパッケージ宣言の注釈です。

単一静的インポート宣言(7.5.3)またはオンデマンド静的インポート宣言(7.5.4)によってインポートされるメンバーのスコープは、モジュール宣言、およびimport宣言が出現するコンパイル・ユニットのすべてのクラス宣言およびインタフェース宣言、ならびにコンパイル・ユニットのモジュール宣言またはパッケージ宣言の注釈です。

通常コンパイル・ユニットで宣言される最上位クラスまたはインタフェース(7.6)のスコープは、最上位クラスまたはインタフェースが宣言されているパッケージ内のすべてのクラス宣言およびインタフェース宣言です。

単純コンパイル・ユニット(7.3)での最上位クラスの暗黙宣言のスコープは空であり、クラスはスコープ内にありません。

クラスまたはインタフェースCによって宣言または継承されたメンバーm (8.29.2)の宣言のスコープは、Cの本体全体(ネストされたクラスまたはインタフェースの宣言を含む)です。Cがレコード・クラスである場合、mのスコープには、さらにCのレコード宣言のヘッダーが含まれます。

この項の残りの部分に変更はありません。

6.7 完全修飾名および正規名

プリミティブ型、名前付きパッケージ、最上位クラス、最上位インタフェースにはすべて次のような完全修飾名があります。

各メンバー・クラス、メンバー・インタフェース、および配列型には次のように完全修飾名を付けることができます

ローカル・クラス、ローカル・インタフェース、または匿名クラスに完全修飾名は付けられません。

すべてのプリミティブ型、名前付きパッケージ、最上位クラス、および最上位インタフェースに正規名があります。

メンバー・クラス、メンバー・インタフェース、および配列型にはそれぞれ正規名を持たせることができます

ローカル・クラス、ローカル・インタフェース、または匿名クラスに正規名はありません。

例6.7-1完全修飾名

コードでは、次のようになります:

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.Ip.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つの部分で構成され、それぞれがオプションです。

単純なコンパイル・ユニットは、クラス・メンバー宣言の空でないシーケンスで構成され、そのうちの少なくとも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内のコンパイル・ユニットは除きます(これらはすべて、常に観察可能です)。

それぞれの観察可能なコンパイル・ユニットは、次のようにモジュールに関連付けることができます。

コンパイル・ユニットの可観測性はそのパッケージの可観測性(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.36.5.36.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として宣言して、同じモジュールの他のパッケージのコードから(および場合によっては他のモジュールのパッケージのコードから)クラスまたはインタフェースにアクセスすることもできます。

最上位クラスまたはインタフェースの宣言にprotectedprivateまたは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
;

単純コンパイル・ユニットによって暗黙的に宣言されたクラスには、次の特性があります。

このクラスが候補の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!

ここでは、Test初期クラスまたはインタフェースを実行するためにJava仮想マシンが実行するステップの概要を説明します。ロード、リンクおよび初期化プロセスの例については、後のセクションで詳しく説明します。

12.1.1 初期クラスまたはインタフェースTestのロード

初期クラスまたはインタフェースTestメソッドmainを最初に実行しようとすると、クラスTestそれがロードされていない、つまりJava仮想マシンには現在、このクラスまたはインタフェースのバイナリ表現が含まれていないことが検出されます。次に、Java仮想マシンがクラス・ローダーを使用して、このようなバイナリ表現を見つけようとします。この処理に失敗した場合は、エラーがスローされます。このロード・プロセスについては、12.2で詳しく説明します。

12.1.2 初期クラスまたはインタフェースTestのリンク: 検証、準備、(オプションの)解決

クラスまたはインタフェースTestがロードされたら、メソッドmainを呼び出す前に初期化する必要があります。また、Testは、すべてのクラスやインタフェースと同様に、これを初期化する前にリンクする必要があります。リンクには、検証、準備および(オプションの)解決が含まれます。リンクの詳細は、12.3を参照してください。

検証では、クラスまたはインタフェースTestのロードされた表現が適切なシンボル・テーブルで整形式になっていることを確認します。検証では、クラスまたはインタフェースTestを実装するコードが、Javaプログラミング言語およびJava仮想マシンのセマンティック要件に従っていることもチェックされます。検証中に問題が検出されると、エラーがスローされます。検証の詳細は、12.3.1を参照してください。

準備には、静的ストレージと、Java仮想マシンの実装によって内部的に使用されるデータ構造(メソッド表など)の割当てが含まれます。準備の詳細は、12.3.2を参照してください。

解決とは、前述の他のクラスおよびインタフェースをロードし、参照が正しいことを確認することによって、クラスまたはインタフェースTestから他のクラスおよびインタフェースへのシンボリック参照をチェックするプロセスです。

解決の手順は、初期リンケージの時点ではオプションです。実装では、再帰的に参照されるクラスおよびインタフェースからすべてのシンボリック参照を解決するポイントに至るまで、非常に早期にリンクされているクラスまたはインタフェースからシンボリック参照を解決できます。(この解決により、これらのロードおよびリンクの手順でエラーが発生する可能性があります。)この実装の選択肢は、C言語の単純な実装で長年使用されてきた「静的」リンクに似ています。(これらの実装では、コンパイルされたプログラムは通常、プログラムで使用されるライブラリ・ルーチンへの完全に解決されたリンクを含む、プログラムの完全なリンク・バージョンのある「a.out」ファイルとして表されます。これらのライブラリ・ルーチンのコピーは、「a.out」ファイルに含まれています。)

かわりに、実装では、シンボリック参照がアクティブに使用されている場合にのみ解決するように選択されます。この戦略のすべてのシンボリック参照に対する一貫した使用は、「最も遅い」解決になります。この場合、クラスまたはインタフェースTestに別のクラスへのシンボリック参照が複数ある場合、プログラムの実行中にそれらの参照が使用されるたびに1つずつ解決され、まったく使用されない場合は何も解決されません。

解決が実行される際の唯一の要件は、解決中に検出されたエラーが、プログラムのある時点でスローされることです。そこでは、エラーに関与するクラスまたはインタフェースへの直接的または間接的なリンクが必要なプログラムにより、アクションが実行されます。前述の「静的」な例の実装の選択肢を使用すると、初期クラスまたはインタフェース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.mainmainメソッドの起動

最後に、初期クラスまたはインタフェースTestの初期化が完了した後(この間に、ロード、リンク、および初期化という他の結果が発生する場合があります)、初期クラスまたはインタフェースTestで宣言または継承されているmainメソッドのmainが呼び出されます。

メソッドmainは、publicstaticおよびvoidとして宣言する必要があります。宣言型がStringの配列である仮パラメータ(8.4.1)を指定します。したがって、次のいずれかの宣言を使用できます。

public static void main(String[] args)

public static void main(String... args)

初期クラスまたはインタフェースのメソッドは、mainという名前で次のいずれかが該当する場合、candidateです。

なお、候補メソッドは、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メソッドが呼び出されます。

なお、クラスにstaticと、同じシグネチャのインスタンス・メソッド(8.4.2)を両方含めることはできません。

呼び出される候補メソッドがない場合、またはインスタンス候補メソッドの呼出し時に初期クラスに適切なコンストラクタがない場合の実装の動作は、この仕様の範囲を超えています。

第13章: バイナリ互換性

13.1 バイナリの形式

プログラムは、Java仮想マシン仕様、Java SE 23 Editionによって規定されているclassファイル形式に、またはJavaプログラミング言語で作成されたクラス・ローダーによってこの形式にマップできる表現にコンパイルする必要があります。

クラスまたはインタフェース宣言に対応するclassファイルには、特定の特性が必要です。これらの特性の多くは、バイナリの互換性を確保するソース・コードの変換をサポートするように明確に選択されています。必須特性は次のとおりです。

  1. クラスまたはインタフェースはバイナリ名によって名前を付ける必要があり、これは次の制約を満たす必要があります。

    • 最上位クラスまたはインタフェース(7.6)のバイナリ名はその正規名(6.7)です。

      なお、単純コンパイル・ユニットによって暗黙的に宣言された最上位クラスの正規名は、ホスト・システムによって決定されます(8.1.8)。

    • メンバー・クラスまたはインタフェース(8.59.5)のバイナリ名は、直前と直後のクラスまたはインタフェースのバイナリ名と、その後に続く$とメンバーの単純名で構成されます。

    • ローカル・クラスまたはインタフェース(14.3)のバイナリ名の構成は、それを直接包含するクラスまたはインタフェースのバイナリ名、その後ろに$、空でない数字のシーケンス、ローカル・クラスの単純名が続きます。

    • 匿名クラス(15.9.5)のバイナリ名は、それを直接囲んでいるクラスまたはインタフェースのバイナリ名と、その後に続く$、および空でない数字のシーケンスで構成されます。

    • 汎用クラスまたはインタフェースによって宣言される型変数のバイナリ名(8.1.29.1.2)は、それを直接囲んでいるクラスまたはインタフェースのバイナリ名、その後ろに順に続く$、型変数の単純名で構成されます。

    • 汎用メソッド(8.4.4)で宣言された型変数のバイナリ名は、そのメソッドを宣言するクラスまたはインタフェースのバイナリ名と、その後に続く$、メソッドの記述子(JVMS §4.3.3)、$、および型変数の単純名で構成されます。

    • 汎用コンストラクタ(8.8.4)で宣言された型変数のバイナリ名は、そのコンストラクタを宣言するクラスのバイナリ名と、その後に続く$、コンストラクタの記述子(JVMS §4.3.3)、$、および型変数の単純名で構成されます。

  2. 別のクラスまたはインタフェースへの参照は、クラスまたはインタフェースのバイナリ名を使用するシンボリックである必要があります。

  3. 定数変数(4.12.4)であるフィールドへの参照は、コンパイル時に定数変数のイニシャライザが示す値Vに解決される必要があります。

    このようなフィールドがstaticである場合、バイナリ・ファイルのコード内にフィールドへの参照が存在しない必要があります。これには、このフィールドを宣言したクラスまたはインタフェースも含まれます。このようなフィールドは常に、初期化(12.4.2)されているものとして示される必要があり、このフィールドのデフォルトの初期値は(Vとは異なる場合)、表示されないようにします。

    このようなフィールドが非staticである場合、バイナリ・ファイルのコード内にフィールドへの参照が存在しない必要があります。ただし、このフィールドが含まれるクラス内は除きます。(インタフェースにはstaticフィールドしかないため、これはインタフェースではなくクラスです。)このクラスには、インスタンス作成時(12.5)にフィールドの値をVに設定するためのコードが必要です。

  4. クラスC内でのフィールド・アクセスを示す正当な式が与えられ、定数値ではなく、(場合によっては別個の)クラスまたはインタフェースD内で宣言されているfという名前のフィールドを参照している場合、Oracleではフィールド参照の修飾クラスまたはインタフェースを次のように定義します。

    • 式が単純名によって参照されているときに、fが現在のクラスまたはインタフェースCのメンバーである場合、QCにします。そうでない場合、Qは、fがメンバーである、最も内側の字句的な包含クラスまたはインタフェース宣言にします。いずれの場合も、Qは参照の修飾クラスまたはインタフェースです。

    • 参照の形式がTypeName.fであり、TypeNameがクラスまたはインタフェースを示している場合、TypeNameが示すクラスまたはインタフェースは参照のクラスまたはインタフェースの修飾型です。

    • 式の形式がExpressionName.fまたはPrimary.fである場合は次のようになります。

      • ExpressionNameまたはPrimaryのコンパイル時の型が交差型V1 & ... & Vn (4.9)である場合、参照の修飾クラスまたはインタフェースはV1のイレイジャ(4.6)です。

      • そうでない場合、ExpressionNameまたはPrimaryのコンパイル時の型のイレイジャが参照の修飾クラスまたはインタフェースです。

    • 式の形式がsuper.fの場合、Cのスーパークラスは、参照の修飾クラスまたはインタフェースです。

    • 式の形式がTypeName.super.fの場合、TypeNameで示されるクラスのスーパークラスは、参照の修飾クラスまたはインタフェースです。

    fへの参照は、参照の修飾クラスまたはインタフェースおよびフィールドの単純名fへのシンボリック参照にコンパイルする必要があります。

    また、型が想定どおりであるかどうかを検証者がチェックできるように、この参照には、宣言されたフィールドの型のイレイジャへのシンボリック参照も含まれている必要があります。

  5. クラスまたはインタフェースC内のメソッド呼出し式またはメソッド参照式が与えられ、(場合によっては別個の)クラスまたはインタフェースD内で宣言されている(または暗黙的な宣言されている(9.2)) mという名前のメソッドを参照している場合、Oracleではメソッド呼出しの修飾クラスまたはインタフェースを次のように定義します。

    • DObjectである場合、メソッド呼出しの修飾クラスまたはインタフェースはObjectです。

    • それ以外の場合:

      • メソッドが単純名によって参照されているときに、mが現在のクラスまたはインタフェースCのメンバーである場合、QCにします。そうでない場合、Qは、mがメンバーである、最も内側の字句的な包含クラスまたはインタフェース宣言にします。いずれの場合も、Qはメソッド呼出しの修飾クラスまたはインタフェースです。

      • 式の形式がTypeName.mまたはReferenceType::mの場合、TypeNameによって示されるクラスまたはインタフェース、またはReferenceTypeのイレイジャは、メソッド呼び出しの修飾クラスまたはインタフェースです。

      • 式の形式がExpressionName.mPrimary.mExpressionName::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を宣言され、値を戻さないことを示すものが含まれている必要があります。

  6. クラス・インスタンス作成式(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)へのシンボリック参照に解決される必要があります。コンストラクタのシグネチャには、次の両方が含まれている必要があります。

    • コンストラクタのパラメータ数

    • 各仮パラメータの型へのシンボリック参照

クラスまたはインタフェースのバイナリ表現には、次もすべて含まれる必要があります。

  1. これがObjectではなく、クラスである場合、このクラスの直接スーパークラスへのシンボリック参照。

  2. 各直接スーパーインタフェースへのシンボリック参照(ある場合)。

  3. フィールドの単純名およびフィールドの型のイレイジャへのシンボリック参照として与えられた、クラスまたはインタフェース内で宣言された各フィールドの仕様。

  4. これがクラスである場合、各コンストラクタの消去されたシグネチャ(前述を参照)。

  5. クラスまたはインタフェース内で宣言された各メソッド(インタフェースについては、暗黙的に宣言されたメソッドを除く(9.2))ごとに、消去されたシグネチャおよび戻り型(前述を参照)。

  6. クラスまたはインタフェースを実装するために必要なコード。

    • インタフェースについては、フィールド・イニシャライザのコード、およびブロック本体を持つ各メソッドの実装(9.4.3)。

    • クラスについては、フィールド・イニシャライザ、インスタンス・イニシャライザおよび静的イニシャライザのコード、ブロック本体を持つ各メソッドの実装(8.4.7)、および各コンストラクタの実装。

  7. すべてのクラスまたはインタフェースに、その正規名(6.7)をリカバリするために十分な情報が含まれている必要があります。

  8. すべてのメンバー・クラスまたはインタフェースに、ソースレベルのアクセス修飾子をリカバリするために十分な情報が含まれている必要があります(6.6)。

  9. すべてのネストしたクラスまたはインタフェースに、それを直接包含するクラスまたはインタフェース(8.1.3)へのシンボリック参照が含まれている必要があります。

  10. すべてのクラスまたはインタフェースに、そのすべてのメンバー・クラスおよびインタフェース(8.59.5)へのシンボリック参照、およびその本体内で宣言されたネストした他のすべてのクラスおよびインタフェースへのシンボリック参照が含まれている必要があります。

  11. Javaコンパイラによって発行されたコンストラクトは、発行されたコンストラクトがクラス初期化メソッドでないかぎり、ソース・コードで明示的または暗黙的に宣言されたコンストラクトに対応していない場合、合成としてマークする必要があります(JVMS §2.9)。

  12. Javaコンパイラによって発行されたコンストラクトは、ソース・コードで暗黙的に宣言された仮パラメータに対応している場合、必須としてマークする必要があります(8.8.18.8.98.9.315.9.5.1)。

次の仮パラメータは、ソース・コードで暗黙的に宣言されます。

参考までに、次のコンストラクタは、ソース・コードでは暗黙的に宣言されますが、classファイル内で必須としてマークできるのは仮パラメータおよびモジュールのみであるため、必須としてはマークされません(JVMS §4.7.24、JVMS §4.7.25)。

モジュール宣言に対応するclassファイルには、バイナリ名がmodule-infoであり、スーパークラス、スーパーインタフェース、フィールドおよびメソッドを持たないクラスのclassファイルのプロパティが必要です。また、このモジュールのバイナリ表現には次のものがすべて含まれている必要があります。

次の各セクションでは、既存のバイナリとの互換性を損なわずにクラスおよびインタフェース宣言に対して加えられる可能性のある変更点について説明します。前述の翻訳要件に基づき、Java仮想マシンおよびそのclassファイル形式はこれらの変更をサポートしています。前述の要件に基づくクラス・ローダーによってclassファイルにマップし戻される圧縮または暗号化された表現など、他の有効なバイナリ形式もすべて、これらの変更を必然的にサポートします。