Java | 基礎文法:main メソッド

Java Java
スポンサーリンク

Main メソッドの役割

Java の「プログラムの入口」は main メソッドです。JVM は指定されたクラスを読み込み、最初に public static void main(String[] args) を探して、そこから実行を開始します。C や Python と違い、Java は必ずクラスの中に main を置く必要があり、main は「アプリのスタートボタン」と覚えると理解しやすいです。


正しいシグネチャと意味

基本形(覚えるべき一行)

public static void main(String[] args)
Java

この一行には重要な意味が詰まっています。

  • public: JVM からどこからでも呼べるように公開します。公開されていないと入口に到達できません。
  • static: クラスのインスタンスを作らずに呼べます。起動直後はまだオブジェクトがないため、入口は static である必要があります。
  • void: 戻り値は使いません。終了コードは System.exit(コード) で明示します。
  • String[] args: コマンドライン引数を受け取ります。未指定なら長さ 0 の配列が渡されます。

同等の書き方として public static void main(String... args)(可変長引数)も使えます。配列と機能は同じです。


最小の例と実行方法

Hello, World(最小構成)

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

このファイルを Hello.java として保存し、次で実行します。

  • コンパイル: javac Hello.java
  • 実行: java Hello

よくある間違いは「java Hello.class と書く」ことですが、拡張子は付けずにクラス名だけを指定します。また、public class Hello とファイル名 Hello.java を一致させるのも必須です。


引数の扱い(コマンドラインから値を渡す)

単純な計算機(引数の読み取りと型変換)

public class Add {
    public static void main(String[] args) {
        if (args.length < 2) {
            System.out.println("使い方: java Add 3 5");
            return;
        }
        try {
            int a = Integer.parseInt(args[0]);
            int b = Integer.parseInt(args[1]);
            System.out.println(a + b);
        } catch (NumberFormatException e) {
            System.out.println("整数を指定してください");
        }
    }
}
Java

重要点は次の通りです。args は文字列配列なので、数値にしたいときは Integer.parseInt などで変換します。引数不足(args.length)や形式不正(例外)を必ずチェックすると、落ちないプログラムになります。


複数の main とエントリポイントの選び方

どのクラスが入口か(選択の仕組み)

複数のクラスに main を置けますが、起動時に「どのクラス名を指定したか」で入口が決まります。

public class A {
    public static void main(String[] args) {
        System.out.println("A の main");
    }
}
Java
public class B {
    public static void main(String[] args) {
        System.out.println("B の main");
    }
}
Java
  • 実行例: java A なら A が、java B なら B が実行されます。
  • JAR 配布: JAR にする場合はマニフェストに Main-Class: A のように入口を明示します。これで java -jar app.jar だけで起動できます。

よくある落とし穴と対策(深掘り)

static コンテキストの制約

main は static なので、インスタンスメンバー(非 static)を直接触れません。触るには「インスタンスを作る」か「メンバーを static にする」必要があります。

public class App {
    private int counter = 0; // 非 static

    public static void main(String[] args) {
        App app = new App();     // インスタンスを作る
        System.out.println(app.counter);
    }
}
Java

設計のコツは「main は薄く、実処理は別クラス/メソッド」に分離すること。テストしやすく、保守性が上がります。

シグネチャの誤り

public static void main(String args[]) は OK(配列記法の違いだけ)。しかし次は NG です。

  • public void main(String[] args)(static がない)
  • static void main(String[] args)(public がないと起動できない)
  • public static int main(String[] args)(戻り値が void でない)
  • public static void main(String[] a, String[] b)(引数の型・数が違う)

終了コードの使い分け

成功・失敗を OS やスクリプトに伝えたいときは System.exit(コード) を使います。慣例的に 0 が成功、0 以外が失敗です。

public class ExitDemo {
    public static void main(String[] args) {
        if (args.length == 0) {
            System.out.println("引数が必要です");
            System.exit(1); // 失敗を示す
        }
        System.out.println("OK");
        System.exit(0);     // 成功
    }
}
Java

main の最後で自然終了する場合は 0 と同等です。異常系だけ System.exit を使うと読みやすくなります。


実用例題で総復習

例題 1: ファイルを読み行数を数える(例外と終了コード)

import java.nio.file.*;
import java.io.IOException;
import java.util.List;

public class CountLines {
    public static void main(String[] args) {
        if (args.length != 1) {
            System.out.println("使い方: java CountLines <path>");
            System.exit(1);
        }
        try {
            List<String> lines = Files.readAllLines(Path.of(args[0]));
            System.out.println("lines=" + lines.size());
        } catch (IOException e) {
            System.out.println("読み取り失敗: " + e.getMessage());
            System.exit(2);
        }
    }
}
Java

ポイントは、入力検証→処理→例外対応→適切な終了コードの流れを main に集約し、詳細ロジックは標準ライブラリや別メソッドに委ねることです。

例題 2: サブコマンド形式の入口(薄い main、厚いロジック)

public class CliApp {
    public static void main(String[] args) {
        if (args.length == 0) {
            printHelp();
            return;
        }
        String cmd = args[0];
        switch (cmd) {
            case "greet" -> greet(args);
            case "sum" -> sum(args);
            default -> printHelp();
        }
    }

    static void greet(String[] args) {
        String name = (args.length >= 2) ? args[1] : "Java";
        System.out.println("Hello, " + name + "!");
    }

    static void sum(String[] args) {
        if (args.length < 3) {
            System.out.println("使い方: sum a b");
            return;
        }
        int a = Integer.parseInt(args[1]);
        int b = Integer.parseInt(args[2]);
        System.out.println(a + b);
    }

    static void printHelp() {
        System.out.println("使い方: greet <name> | sum <a> <b>");
    }
}
Java

main はコマンドの振り分けだけに集中させ、個別処理は別メソッドへ。規模が大きくなっても整理が効き、テストもしやすくなります。


仕上げのアドバイス(設計視点の深掘り)

main は「起動配線」、ロジックは「別ユニット」

main を「起動・入力検証・エラー処理」に限定することで、アプリの心臓部は疎結合に保てます。単体テストは main 経由ではなくロジックのメソッド/クラスに対して行うのが定石です。

入口は一つでも、責務は分ける

大きなアプリでは、main から DI コンテナや設定読み込み、ログ初期化を呼び、実処理はサービス層へ渡します。責務を分けることで、main は「どこから来てどこへ行くか」を示す最小のガイドになります。


次の一歩

JShell を使えば、main なしで一行から動作確認ができますが、アプリとして配布するなら main の理解が不可欠です。

タイトルとURLをコピーしました