public static void main(String[] args) とは、Javaのプログラムが最初に実行する「入口」のメソッドです。public は「どこからでも呼べる」、static は「オブジェクトを作らずに呼べる」、void は「値を返さない」、String[] args は「コマンドラインから渡された文字列の配列を受け取る」という意味です。 java コマンドはこの形のメソッドを探して呼び出すので、1語でも変えると「メイン・メソッドが見つかりません」というエラーになります。

この記事では、1語ずつの意味を表にまとめたうえで、それぞれを外したり変えたりしたときに出るエラーメッセージを実際に再現しました。後半では、コマンドライン引数 args の受け取り方、個数チェック、数値への変換、空白やクォートを含む引数、ワイルドカード展開、終了コードまでを、OpenJDK 21.0.10 の実行結果つきで説明します。Java 25 で正式機能になった簡略な main(void main())は、OpenJDK 25.0.4.1 で実行した結果を載せています。

public static void mainの早見表

語 種類 意味 外す・変えるとどうなるか(Java 21.0.10)
public アクセス修飾子 どのクラスからでも呼べる。JVM(Javaを動かす仕組み)が外から呼ぶために必要 「メイン・メソッドがクラスNoPublicで見つかりません」
static 修飾子 オブジェクトを作らなくても呼べる。起動直後はまだオブジェクトが1つもないため必要 「メイン・メソッドがクラスNoStaticのstaticではありません」
void 戻り値の型 値を返さない。結果は終了コードで伝える 「メイン・メソッドはクラスIntMainのvoid型の値を返す必要があります」
main メソッド名 java コマンドが探す決まった名前。小文字 Main にすると「メイン・メソッドが…見つかりません」
String[] 引数の型 文字列の配列。コマンドライン引数が入る String にすると「メイン・メソッドが…見つかりません」
args 引数の名前 arguments(引数)の略。名前は自由に変えられる argv などに変えても動く

変えてよいところ・そのままにするところを分けると、次のようになります。

書き方 動く?(Java 21.0.10 で確認)
public static void main(String[] args) 動く(基本形)
public static void main(String... args) 動く(可変長引数)
public static void main(String args[]) 動く(配列の [] を名前側に書く形)
static public void main(String[] args) 動く(修飾子の順番は入れ替え可)
public static void main(final String[] argv) 動く(引数名の変更、final の追加)
static void main(String[] args) エラー(Java 25 では動く)
public void main(String[] args) エラー(Java 25 では動く)
public static void main() エラー(Java 25 では動く)
public static int main(String[] args) エラー
public static void Main(String[] args) エラー

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Java public static void mainの意味とargsの使い方」

動作確認の環境

項目 内容
主に使った JDK OpenJDK 21.0.10(Ubuntu 24.04、シェルは bash)
Java 25 の確認 OpenJDK 25.0.4.1
エラー文の言語 日本語は -Duser.language=ja、英語は -Duser.language=en を付けて表示
表記について 実行時はクラスの出力先(-cp out)と文字コードの指定を付けています。記事では読みやすさのため java クラス名 引数 の形に整えて載せています

mainメソッドの基本形と動かし方

まずは基本形です。ファイル名はクラス名と同じ Hello.java にします。

public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello, Java");
    }
}
$ javac Hello.java
$ java Hello
Hello, Java

javac がソースコードをクラスファイル(Hello.class)に変換し、java Hello がそのクラスを読み込んで main メソッドを呼び出します。流れを図にするとこうなります。

flowchart LR
    A["java Hello apple 100"] --> B["JVM が起動"]
    B --> C["Hello クラスを読み込む"]
    C --> D["public static void main を探す"]
    D --> E["args に apple と 100 を入れて呼び出す"]
    E --> F["main が終わると終了コードを返す"]

java コマンドに渡すのは「クラス名」で、ファイル名ではありません。 .class を付けたり、大文字小文字を変えたり、つづりを間違えたりすると、次のエラーになります。

$ java Hello.class
エラー: メイン・クラスHello.classを検出およびロードできませんでした
原因: java.lang.ClassNotFoundException: Hello.class

$ java Helo
エラー: メイン・クラスHeloを検出およびロードできませんでした
原因: java.lang.ClassNotFoundException: Helo

英語表示では Error: Could not find or load main class Helo です。

1語ずつ外すと出るエラーを再現する

それぞれのキーワードが必要な理由は、外してみるとよくわかります。以下はすべて、コンパイルは通り、実行したときに java コマンドが出すエラーです(Java 21.0.10、終了コードはいずれも 1)。

publicを外す

public class NoPublic {
    static void main(String[] args) { System.out.println("no public"); }
}
$ java NoPublic
エラー: メイン・メソッドがクラスNoPublicで見つかりません。次のようにメイン・メソッドを定義してください。
   public static void main(String[] args)
またはJavaFXアプリケーション・クラスはjavafx.application.Applicationを拡張する必要があります

英語表示:

Error: Main method not found in class NoPublic, please define the main method as:
   public static void main(String[] args)
or a JavaFX application class must extend javafx.application.Application

private static void main にしても同じメッセージでした。Java 21 までは、JVM がクラスの外から呼び出せるように public が必要です。

staticを外す

public class NoStatic {
    public void main(String[] args) { System.out.println("no static"); }
}
$ java NoStatic
エラー: メイン・メソッドがクラスNoStaticのstaticではありません。次のようにメイン・メソッドを定義してください。
   public static void main(String[] args)

英語表示は Error: Main method is not static in class NoStatic です。static のないメソッドは、new でオブジェクトを作ってからでないと呼べません。プログラムが始まった時点ではオブジェクトがまだ1つもないので、オブジェクトなしで呼べる static が必要になります。

voidを別の型に変える

public class IntMain {
    public static int main(String[] args) { return 0; }
}
$ java IntMain
エラー: メイン・メソッドはクラスIntMainのvoid型の値を返す必要があります。
次のようにメイン・メソッドを定義してください。
   public static void main(String[] args)

英語表示は Error: Main method must return a value of type void in class IntMain です。C言語の int main と違い、Javaの main は値を返しません。呼び出し元(シェルやバッチ)に結果を伝えたいときは System.exit(番号) を使います(後述)。

mainの名前や引数の型を変える

public class BigMain {
    public static void Main(String[] args) { System.out.println("Main"); }
}

Main(大文字)、引数なしの main()、引数が String 1つの main(String args) は、どれも Java 21 では「メイン・メソッドがクラス○○で見つかりません」になりました。Javaは大文字と小文字を区別し、引数の型まで含めてメソッドを探しているためです。

エラーと原因の対応表

エラーメッセージ(日本語/英語の先頭) 主な原因 直し方
メイン・メソッドがクラスXで見つかりません/Main method not found in class X public がない、名前が Main、引数が String[] でない、main を書いていない public static void main(String[] args) の形にそろえる
メイン・メソッドがクラスXのstaticではありません/Main method is not static static がない static を付ける
メイン・メソッドはクラスXのvoid型の値を返す必要があります/Main method must return a value of type void 戻り値が void 以外 void にして、結果は System.exit で返す
メイン・クラスXを検出およびロードできませんでした/Could not find or load main class X クラス名のつづり違い、.class を付けた、クラスパスが違う クラス名だけを正しく指定する。場所は -cp で指定する

コマンドライン引数argsを受け取る

引数を表示して確かめる

java クラス名 の後ろに空白で区切って書いた文字列が、順番に args[0]、args[1]…に入ります。確認用のプログラムを用意します。

public class ShowArgs {
    public static void main(String[] args) {
        System.out.println("個数: " + args.length);
        for (int i = 0; i < args.length; i++) {
            System.out.println("args[" + i + "] = [" + args[i] + "]");
        }
    }
}
$ java ShowArgs
個数: 0

$ java ShowArgs apple 100 true
個数: 3
args[0] = [apple]
args[1] = [100]
args[2] = [true]

ここからわかることを3つ挙げます。

ポイント 説明
引数なしでも args は null にならない 長さ 0 の配列が渡される。args.length は 0
args[0] は最初の引数 C言語などと違い、プログラム名は入らない
100 も true もすべて文字列 数値として使うには変換が必要

空白やクォートを含む引数

引数は空白で区切られます。空白を含む1つの値を渡したいときは、クォート(引用符)で囲みます。

$ java ShowArgs "hello world" 'a b' c\ d
個数: 3
args[0] = [hello world]
args[1] = [a b]
args[2] = [c d]

$ java ShowArgs '' ""
個数: 2
args[0] = []
args[1] = []

$ java ShowArgs '"quoted"' \$HOME "$HOME"
個数: 3
args[0] = ["quoted"]
args[1] = [$HOME]
args[2] = [/root]

クォートを解釈しているのは Java ではなくシェル(bash など)です。 Java のプログラムには、クォートが外れた後の文字列が届きます。そのため、OS やシェルによって書き方が少し変わります。

やりたいこと bash・zsh(Linux/macOS) Windows のコマンドプロンプト
空白を含む値を1つの引数にする "hello world" または 'hello world' "hello world"(シングルクォートは区切りにならない)
空文字を渡す '' または "" ""
ダブルクォートそのものを渡す '"quoted"' または \"quoted\" \"quoted\"
変数を展開させずに渡す '$HOME' または \$HOME —(%NAME% が展開の書き方)

Windows の列は公式ドキュメントと一般的な仕様にもとづく記載で、手元では bash の結果のみ確認しています。

ワイルドカード(*)の展開

* を含む引数は、シェルがファイル名に展開してから Java に渡します。memo1.txt〜memo3.txt がある場所で実行した結果です。

$ java ShowArgs memo*.txt
個数: 3
args[0] = [memo1.txt]
args[1] = [memo2.txt]
args[2] = [memo3.txt]

$ java ShowArgs 'memo*.txt'
個数: 1
args[0] = [memo*.txt]

$ java ShowArgs nothing*.xyz
個数: 1
args[0] = [nothing*.xyz]
書き方 bash での結果
memo*.txt(クォートなし) 一致するファイル名に展開され、ファイルの数だけ引数になる
'memo*.txt'(クォートあり) 展開されず、memo*.txt という文字列のまま届く
一致するファイルがない bash の既定では、書いた文字列のまま届く

「引数が1つのはずなのに args.length が増えている」ときは、* がシェルに展開されていないかを確認すると原因が見つかります。計算プログラムで掛け算の記号として * を渡したい場合も、'*' とクォートで囲みます。

日本語の引数

日本語の引数は、ロケール(言語と文字コードの設定)が UTF-8 のときにそのまま受け取れました。

$ LC_ALL=C.UTF-8 java ShowArgs 東京 "新 宿"
個数: 2
args[0] = [東京]
args[1] = [新 宿]

ロケールが未設定(LANG が空)の環境で実行したところ、日本語の引数が文字化けしました。このとき sun.jnu.encoding というプロパティは ANSI_X3.4-1968(ASCII)になっており、UTF-8 ロケールにすると UTF-8 になりました。コンテナやバッチ環境で日本語の引数が化けるときは、LANG や LC_ALL を UTF-8 のロケールに設定すると解決します。

引数の個数チェックと数値変換

チェックなしで書くとどうなるか

2つの整数を足すプログラムを、まずはチェックなしで書いてみます。

public class AddRaw {
    public static void main(String[] args) {
        int a = Integer.parseInt(args[0]);
        int b = Integer.parseInt(args[1]);
        System.out.println(args[0] + args[1]);   // 文字列の連結
        System.out.println(a + b);               // 数値の足し算
    }
}
$ java AddRaw 10 20
1020
30

args[0] + args[1] は文字列どうしの連結なので「1020」になり、Integer.parseInt で int に変換してから足すと「30」になります。args の中身はすべて String なので、計算に使うときは変換が必要です。

引数を付け忘れたり、数字以外を渡したりすると、例外で止まります。

$ java AddRaw
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0
    at AddRaw.main(AddRaw.java:3)

$ java AddRaw 10 abc
Exception in thread "main" java.lang.NumberFormatException: For input string: "abc"
    at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
    at java.base/java.lang.Integer.parseInt(Integer.java:662)
    at java.base/java.lang.Integer.parseInt(Integer.java:778)
    at AddRaw.main(AddRaw.java:4)
例外 意味 起きる場面
ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0 長さ 0 の配列の 0 番目を読もうとした 引数なしで args[0] を読んだ
NumberFormatException: For input string: "abc" 数値に変換できない文字列だった abc、2.5(小数)、空文字などを parseInt に渡した

直した後:個数チェックと使い方の表示

使う人にとって親切なのは、例外のスタックトレースではなく「使い方」を表示する形です。

public class Add {
    public static void main(String[] args) {
        if (args.length != 2) {
            System.err.println("使い方: java Add <整数1> <整数2>");
            System.exit(2);
        }
        try {
            int a = Integer.parseInt(args[0]);
            int b = Integer.parseInt(args[1]);
            System.out.println(a + " + " + b + " = " + (a + b));
        } catch (NumberFormatException e) {
            System.err.println("整数を指定してください: " + e.getMessage());
            System.exit(1);
        }
    }
}
$ java Add 10 20
10 + 20 = 30
(終了コード 0)

$ java Add 10
使い方: java Add <整数1> <整数2>
(終了コード 2)

$ java Add 10 abc
整数を指定してください: For input string: "abc"
(終了コード 1)

ポイントは3つです。

  • 最初に args.length を確かめる:args[0] を読む前に個数をチェックする
  • エラーメッセージは System.err に出す:通常の出力(System.out)と分けておくと、結果をファイルに保存するときにメッセージが混ざらない
  • 0 以外の終了コードで終わる:呼び出し元が「うまくいかなかった」と判断できる

型ごとの変換メソッド一覧

import java.nio.file.Path;
import java.time.LocalDate;

public class Conv {
    enum Mode { FAST, SAFE }
    public static void main(String[] args) {
        int count        = Integer.parseInt(args[0]);
        long big         = Long.parseLong(args[1]);
        double rate      = Double.parseDouble(args[2]);
        boolean verbose  = Boolean.parseBoolean(args[3]);
        Mode mode        = Mode.valueOf(args[4].toUpperCase());
        LocalDate date   = LocalDate.parse(args[5]);
        Path file        = Path.of(args[6]);
        System.out.println(count + 1);
        System.out.println(big * 2);
        System.out.println(rate * 100);
        System.out.println(verbose);
        System.out.println(mode);
        System.out.println(date.plusDays(1));
        System.out.println(file.getFileName());
    }
}
$ java Conv.java 5 3000000000 0.25 TRUE fast 2026-10-02 data/in.csv
6
6000000000
25.0
true
FAST
2026-10-03
in.csv
変換先 メソッド 変換できないとき
int Integer.parseInt(s) NumberFormatException
long Long.parseLong(s) NumberFormatException
double Double.parseDouble(s) NumberFormatException
boolean Boolean.parseBoolean(s) 例外は出ず、true(大文字小文字は問わない)以外はすべて false
enum 列挙型.valueOf(s) IllegalArgumentException(大文字小文字を区別するので toUpperCase() と組み合わせる)
日付 LocalDate.parse(s) DateTimeParseException(2026-10-02 の形式)
ファイルのパス Path.of(s) —

オプション形式(-v、-n 3)を自分で読み取る

-v や -n 3 のようなオプションも、args の中ではただの文字列です。小さなツールなら、次のように for 文と switch で読み取れます。

import java.util.ArrayList;
import java.util.List;

public class Opts {
    public static void main(String[] args) {
        boolean verbose = false;
        int limit = 10;
        List<String> files = new ArrayList<>();

        for (int i = 0; i < args.length; i++) {
            switch (args[i]) {
                case "-v", "--verbose" -> verbose = true;
                case "-n", "--limit" -> {
                    if (i + 1 >= args.length) {
                        System.err.println(args[i] + " には値が必要です");
                        System.exit(2);
                    }
                    limit = Integer.parseInt(args[++i]);
                }
                default -> files.add(args[i]);
            }
        }
        System.out.println("verbose=" + verbose + " limit=" + limit + " files=" + files);
    }
}
$ java Opts.java -v -n 3 a.txt b.txt
verbose=true limit=3 files=[a.txt, b.txt]

$ java Opts.java a.txt
verbose=false limit=10 files=[a.txt]

$ java Opts.java -n
-n には値が必要です
(終了コード 2)

オプションの数が増えてきたら、picocli や Apache Commons CLI といった引数解析用のライブラリを使うと、ヘルプ表示や型変換まで任せられます(この記事の環境では外部ライブラリを取得できないため、ライブラリの動作は未確認です)。

argsとシステムプロパティ・環境変数の違い

プログラムに外から値を渡す方法は、args のほかに2つあります。クラス名より前に書いたものは java コマンド自身へのオプション、後ろに書いたものが args です。

public class Prop {
    public static void main(String[] args) {
        System.out.println("プロパティ mode = " + System.getProperty("mode"));
        System.out.println("環境変数 APP_ENV = " + System.getenv("APP_ENV"));
        System.out.println("args = " + java.util.Arrays.toString(args));
    }
}
$ APP_ENV=test java -Dmode=dev Prop -Dmode=prod x
プロパティ mode = dev
環境変数 APP_ENV = test
args = [-Dmode=prod, x]

クラス名の後ろに書いた -Dmode=prod はプロパティにならず、そのまま args に入っています。

渡し方 書く場所 受け取り方 向いている用途
コマンドライン引数 クラス名の後ろ args[i] 実行のたびに変わる値(対象ファイル、件数)
システムプロパティ クラス名の前に -D名前=値 System.getProperty("名前") 動作の切り替え、設定値
環境変数 シェルや OS 側で設定 System.getenv("名前") 実行環境ごとの設定、パスワードなど表示させたくない値

終了コードとSystem.exit

main の戻り値は void ですが、プログラムは終わるときに 終了コード(exit code) という整数を呼び出し元に返します。0 が「正常に終わった」、0 以外が「何か問題があった」という意味で使われます。

public class ExitDemo {
    public static void main(String[] args) {
        String mode = args.length > 0 ? args[0] : "ok";
        try {
            switch (mode) {
                case "exit3" -> System.exit(3);
                case "throw" -> throw new IllegalStateException("想定外の状態");
                default -> System.out.println("正常終了");
            }
        } finally {
            System.out.println("finally を通過");
        }
    }
}
$ java ExitDemo
正常終了
finally を通過
(終了コード 0)

$ java ExitDemo exit3
(終了コード 3)

$ java ExitDemo throw
finally を通過
Exception in thread "main" java.lang.IllegalStateException: 想定外の状態
    at ExitDemo.main(ExitDemo.java:7)
(終了コード 1)
終わり方 終了コード finally
main が最後まで実行された 0 実行される
System.exit(n) を呼んだ n 実行されない(その場で JVM が終了する)
例外が main の外まで出た 1 実行される
java コマンドが main を見つけられなかった 1 —

System.exit を呼ぶと、その時点で JVM が終了に向かうため、finally ブロックは実行されませんでした。後始末が必要な処理があるときは、System.exit を main の最後の1か所にまとめると見通しがよくなります。

終了コードは、シェルで echo $?(Windows のコマンドプロンプトでは echo %ERRORLEVEL%)で確認できます。&& や || と組み合わせると、結果に応じて次のコマンドを実行できます。

$ java Add 10 20 && echo 成功のときだけ表示
10 + 20 = 30
成功のときだけ表示

$ java Add 10 || echo 終了コードが0以外のときだけ表示
使い方: java Add <整数1> <整数2>
終了コードが0以外のときだけ表示

終了コードの番号に言語としての決まりはありませんが、「0 は正常、1 は一般的なエラー、2 は使い方の間違い」という使い分けが広く見られます。値は 0〜255 の範囲に収めておくと、どの OS でもそのまま伝わります。

mainメソッドの応用

可変長引数 String... でも書ける

String[] args は String... args と書いても、java コマンドから同じように呼ばれます。

public class Vararg {
    public static void main(String... args) { System.out.println("個数=" + args.length); }
}
$ java Vararg a b c
個数=3

可変長引数はコンパイルすると配列として扱われるためです。テストコードなどから Vararg.main("a", "b") のように直接呼びたいときに書きやすくなります。

mainは普通のメソッドとしても呼べる・複数のクラスに書ける

main は特別な名前ですが、中身は普通の static メソッドです。ほかのクラスから呼ぶことも、引数違いで同じ名前のメソッドを作る(オーバーロード)こともできます。

public class Two {
    public static void main(String[] args) {
        System.out.println("Two.main");
        Other.main(new String[] {"from", "Two"});   // main は普通のメソッドとしても呼べる
    }
    public static void main(int n) { System.out.println("オーバーロード: " + n); }
}
class Other {
    public static void main(String[] args) {
        System.out.println("Other.main " + String.join(",", args));
    }
}
$ java Two
Two.main
Other.main from,Two

$ java Other p q
Other.main p,q

main を持つクラスが複数あっても問題はなく、java コマンドで指定したクラスの main が入口になります。main(int n) のようなオーバーロードは、入口としては選ばれません。

java Foo.java の単一ファイル実行

Java 11 以降は、javac を使わずにソースファイルを直接実行できます。小さな確認にとても便利です。

$ java Hello.java
Hello, Java

$ java Script2.java 田中
こんにちは、田中

ファイル名の後ろに書いたものが args になります。クラスファイルは作られず、メモリ上でコンパイルして実行されます。注意点が1つあり、Java 21 では、ファイルの中で最初に書かれたクラスの main が実行されます。

// Script.java(main のないクラスを先に書いた場合)
class Helper {
    static String greet(String n) { return "こんにちは、" + n; }
}
public class Script {
    public static void main(String[] args) {
        System.out.println(Helper.greet(args.length > 0 ? args[0] : "名無し"));
    }
}
$ java Script.java          (Java 21.0.10)
エラー: クラスにmain(String[])メソッドが見つかりません: Helper

$ java Script.java          (Java 25.0.4.1)
こんにちは、名無し

Java 21 では、main を持つクラスをファイルの先頭に置くと動きます。同じファイルを Java 25.0.4.1 で実行すると、そのままで動きました。

単一ファイル実行で static を外した場合は、エラー文が少し変わります(Java 21.0.10)。

$ java NoStatic.java
エラー: 'mainメソッドが'public static'として宣言されていません
実行方法 コマンド 特徴
コンパイルしてから実行 javac Hello.java → java Hello クラスファイルが残る。複数ファイルの構成に向く
単一ファイル実行 java Hello.java 1ファイルで完結する確認や小さなツールに向く
複数ファイルのソース実行 java Main.java(Java 22 以降) 同じフォルダのほかのソースも自動で読み込む(JEP 458。手元の Java 21 では未実行)

Java 25の簡略なmain(void main・インスタンスmain・IO)

Java 25 では、JEP 512「Compact Source Files and Instance Main Methods」が正式機能になり、入門時のコードがぐっと短く書けるようになりました。以下は OpenJDK 25.0.4.1 で実行した結果です。

クラスを書かずに void main() だけで動く

// Simple.java
void main() {
    IO.println("Hello, Java 25");
    var names = List.of("A", "B");      // import なしで List が使える
    IO.println(names);
}
$ java Simple.java          (Java 25.0.4.1)
Hello, Java 25
[A, B]

クラスの宣言も、public も static も String[] args もありません。IO.println は Java 25 で追加された java.lang.IO クラスのメソッドで、System.out.println の短い書き方として使えます。このような「クラス宣言のないソースファイル」はコンパクトソースファイルと呼ばれ、java.base モジュールの公開パッケージ(java.util など)が自動で import されるため、List をそのまま使えています。

コンパイルして中身を見ると、ファイル名から作られた final クラスになっていました。

$ javac -d o Simple.java && javap -cp o Simple
final class Simple {
  Simple();
  void main();
}

引数を受け取る・入力を読む

// WithArgs.java
void main(String[] args) {
    IO.println("個数: " + args.length);
    String name = args.length > 0 ? args[0] : IO.readln("名前を入力: ");
    IO.println(greet(name));
}

String greet(String name) {          // static なしのメソッドをそのまま呼べる
    return "こんにちは、" + name + "さん";
}
$ java WithArgs.java 田中          (Java 25.0.4.1)
個数: 1
こんにちは、田中さん

$ echo 鈴木 | java WithArgs.java
個数: 0
名前を入力: こんにちは、鈴木さん

引数が必要なときは void main(String[] args) と書けば受け取れます。IO.readln はメッセージを表示して1行読み込むメソッドです。main がインスタンスメソッドなので、static を付けていない greet をそのまま呼べます。

通常のクラスでもインスタンスmainが使える

public class Inst {
    private int count = 0;
    void main() {                    // インスタンス main(static なし)
        count++;
        System.out.println("count=" + count);
    }
}
$ java Inst.java          (Java 25.0.4.1)
count=1

java コマンドが引数なしのコンストラクタでオブジェクトを作り、その main を呼んでいます。動かして確かめた選ばれ方は次のとおりです。

条件 Java 25.0.4.1 での結果
public static void main(String[] args) と void main() の両方がある main(String[] args) が選ばれた
public のない static void main(String[] args) 動いた(Java 21 ではエラー)
static のない public void main(String[] args) 動いた(Java 21 ではエラー)
引数のない public static void main() 動いた(Java 21 ではエラー)
インスタンス main で、引数なしのコンストラクタがない 「クラスに引数なしのコンストラクタが見つかりません: NoCtor」
private static void main(String[] args) 「メイン・メソッドが…見つかりません」(private は対象外)
public static int main(String[] args) 「メイン・メソッドが…見つかりません」(void が必要な点は同じ)

Java 21で同じコードを動かすと

$ java Simple.java          (Java 21.0.10)
Simple.java:1: エラー: 無名クラスはプレビュー機能であり、デフォルトで無効になっています。
void main() {
^
  (無名クラスを有効にするには--enable-previewを使用します)

Java 21 ではこの機能はプレビュー(試用)段階でした。java --enable-preview --source 21 Inst.java とすると、インスタンス main は Java 21 でも動くことを確認しました。ただし IO クラスは Java 21 にはありません。

項目 Java 21 Java 25
public static void main(String[] args) 動く 動く(これまでのコードはそのまま使える)
void main()/インスタンス main プレビュー(--enable-preview が必要) 正式機能
クラス宣言のないソースファイル プレビュー 正式機能(コンパクトソースファイル)
IO.println/IO.readln なし あり(java.lang.IO)

これまでの public static void main(String[] args) は Java 25 でもそのまま動きます。 既存のコードやフレームワークのサンプルはこの形で書かれているので、基本形の意味を理解したうえで、短い書き方も選べるようになった、と捉えるとよいでしょう。

つまずきやすい点のチェックリスト

症状 原因 直し方
メイン・メソッドが見つかりません public がない、Main と大文字、引数が String[] でない 基本形と1語ずつ見比べる
メイン・クラスを検出およびロードできませんでした クラス名の間違い、.class を付けた、実行する場所が違う クラス名だけを指定し、-cp で場所を指定する
ArrayIndexOutOfBoundsException 引数を付けずに args[0] を読んだ 先に args.length を確かめる
NumberFormatException 数字以外や小数を parseInt に渡した try-catch で受けて使い方を表示する。小数は parseDouble
足し算が「1020」になる 文字列のまま + している 数値に変換してから足す
引数の個数が思ったより多い * がシェルに展開された、空白で分かれた クォートで囲む
-Dname=value が効かない クラス名の後ろに書いている クラス名の前に書く
System.exit の後の finally が動かない exit はその場で JVM を終了させる exit は main の最後にまとめる

よくある質問

Q. args という名前は変えてもよいですか? A. 変えられます。argv でも params でも動きます(argv で動くことを確認済みです)。ただし、ほとんどの Java のコードが args と書いているので、そろえておくと読む人が迷いません。

Q. mainメソッドはどのクラスに書けばよいですか? A. どのクラスに書いても構いません。java コマンドで指定したクラスの main が入口になります。入口専用のクラス(Main や App など)を1つ用意し、そこから他のクラスを呼ぶ構成がよく使われます。

Q. 引数を付けずに実行したとき、args は null ですか? A. null ではなく、長さ 0 の配列です。args.length は 0 になり、args[0] を読むと ArrayIndexOutOfBoundsException になります。args == null のチェックは不要で、args.length を確かめれば十分です。

Q. IDE(Eclipse や IntelliJ IDEA)ではコマンドライン引数をどう渡しますか? A. 実行構成(Run Configuration)の「プログラムの引数(Program arguments)」欄に、コマンドラインと同じように空白区切りで書きます。-D で始まるシステムプロパティは「VM 引数(VM options)」欄に書きます。

まとめ

  • public static void main(String[] args) はプログラムの入口:java コマンドがこの形を探して呼び出す
  • public は外から呼べる、static はオブジェクトなしで呼べる、void は値を返さない:1語でも外すと Java 21 では「メイン・メソッドが見つかりません」などのエラーになる
  • args はコマンドライン引数の配列:引数なしなら長さ 0。中身はすべて文字列なので、数値は Integer.parseInt などで変換する
  • args.length を先に確かめ、使い方を System.err に表示する:例外で止まるより親切なツールになる
  • 空白・クォート・* を処理するのはシェル:1つの値として渡したいときはクォートで囲む
  • 結果は終了コードで返す:正常は 0、問題があれば System.exit(1) など 0 以外
  • Java 25 では void main() と IO.println で短く書ける:これまでの基本形もそのまま使える

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Java public static void mainの意味とargsの使い方」

株式会社RJCでは、Javaのmainメソッドのような基礎を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?