全体像
Java のエラーは大きく「コンパイルエラー」と「ランタイムエラー」に分かれます。コンパイルエラーは「プログラムに文法・型・宣言の誤りがあり、そもそも実行ファイルを作れない」状態です。ランタイムエラーは「コンパイルは通ったが、実行中の値や環境が原因で崩れる」状態です。違いを掴むコツは「いつ検出されるか」「何が原因か」「どう直すか」をセットで考えることです。
コンパイルエラーの基本
検出のタイミングと特徴
コンパイルエラーは、コンパイラ(javac)や IDE がソースコードを解析した時点で検出されます。プログラムは起動できません。原因は文法ミス、型不一致、未定義の名前、アクセス不可、チェック例外の未処理など「コードとして成立していない」ことです。IDE では赤い波線や Problems/Build ログに詳細が出ます。
代表的な例と直し方
不正な文(セミコロン抜け、括弧不一致、キーワードの誤り)などは最優先で修正します。次に型と宣言の整合性を取ります。
public class Sample {
public static void main(String[] args) {
int x = "123"; // 型不一致(compile error)
System.out.println(x)
// ↑ セミコロン抜け(compile error)
}
}
Java未定義の識別子やスコープ外の変数参照も典型です。
void f() {
System.out.println(value); // value が宣言されていない(compile error)
}
Javaチェック例外の未処理もコンパイルで止まります。これは「呼び出し側が回復戦略を持つべき」という契約違反です。
import java.nio.file.*;
import java.io.*;
static String firstLine(Path p) {
return Files.readString(p); // IOException を処理/宣言していない(compile error)
}
// 修正例:宣言する
static String firstLine(Path p) throws IOException {
return Files.readString(p);
}
Javaランタイムエラーの基本
検出のタイミングと特徴
ランタイムエラーは、プログラムが実行されてから初めて起きます。コンパイル時点では構文・型的に正しくても、実行時の値や外部環境(入力、ファイル、ネットワーク、配列の長さ)が条件を満たさずに崩れます。JVM が例外やエラーを投げ、スタックトレースで「どの行で何が起きたか」を知らせます。
代表的な例と直し方
null を使ったメソッド呼び出しは確実に崩れます。使用前にガードするか、契約として禁止します。
String s = null;
int len = s.length(); // NullPointerException(runtime error)
// 修正例:入口で検証
import java.util.Objects;
int lenSafe(String s) {
Objects.requireNonNull(s, "s must not be null");
return s.length();
}
Java配列・リストの範囲外アクセスは境界の誤りです。原則「0 ≤ index < size」を守ります。
int[] a = {10, 20, 30};
int v = a[3]; // ArrayIndexOutOfBoundsException(runtime error)
// 修正例:未満で回す
for (int i = 0; i < a.length; i++) System.out.println(a[i]);
Java整数の 0 除算は例外になります。分母を事前チェックします。
int a = 10, b = 0;
int c = a / b; // ArithmeticException(runtime error)
// 修正例:ガード
int safeDiv(int x, int y) {
if (y == 0) throw new IllegalArgumentException("divisor must not be 0");
return x / y;
}
Java文字列の不正な数値変換は入力の問題です。バリデーションとフォールバックを設計します。
int port = Integer.parseInt("12a"); // NumberFormatException(runtime error)
Java重要ポイントの深掘り
「チェック例外はコンパイル、実行時例外はランタイム」ではない
チェック例外は「捕捉または throws 宣言」がコンパイル時に必須です。一方、RuntimeException(NPE、IOBE、ArithmeticException など)はコンパイルでは止まりませんが、実行時に起きます。ただし、RuntimeException を「防ぐコード」(null ガード、境界検証)を書かないと、実質的にランタイムエラーの温床になります。結論は「契約をコードで保証するか、契約違反を例外で早期に表面化する」ことです。
コンパイルが通る=安全ではない
型・構文が正しくても、仕様や契約が破られていれば実行時に崩れます。入力検証(null/範囲/フォーマット)、外部要因のハンドリング(I/O の失敗時の設計)、境界と丸めの統一(BigDecimal の divide は丸め指定)を「実行時の現実」を見据えて書く必要があります。
スタックトレースは「先頭行から読む」
ランタイムエラーが出たら、最初の原因行(クラス名:行番号)を開き、どの値が不正だったかを事実で特定します。包み直しがある場合は Caused by の最後まで辿って根本原因を確認します。ここで思い込みではなく観測(変数ウォッチ、式評価)に切り替えると修正が速くなります。
具体例で違いを体得する
例 1: コンパイルは通らないが、直せば実行できる
public class App {
public static void main(String[] args) {
System.out.println("Hello, " + args[0]); // OK
int x = "123"; // コンパイルエラー:型不一致
}
}
Java型を直せば起動できます。
int x = Integer.parseInt("123"); // 実行時に NumberFormatException になり得る点は別問題
Javaこのように「型不一致」はコンパイルで止まり、「入力の不正」は実行時に顕在化します。
例 2: コンパイルは通るが、実行中に崩れる
public class Divide {
public static void main(String[] args) {
int a = 10, b = 0; // コンパイルは通る
System.out.println(a / b); // 実行時に ArithmeticException
}
}
Java事前チェックに置き換えると、崩れ方を「契約違反」として扱えます。
if (b == 0) throw new IllegalArgumentException("divisor must not be 0");
Java例 3: チェック例外はコンパイル時の対応が必須
import java.nio.file.*;
import java.io.*;
public class ReadFile {
public static void main(String[] args) {
String s = Files.readString(Path.of("missing.txt")); // コンパイルエラー:IOException 未処理
}
}
Java対応例は二択です。ここで回復するか、上位へ委ねるか。
try {
String s = Files.readString(Path.of("missing.txt"));
} catch (IOException e) {
System.err.println("読込失敗: " + e.getMessage());
}
// または宣言して上位へ
static String read(Path p) throws IOException {
return Files.readString(p);
}
Java直し方の設計指針
コンパイルエラーは「コードの整合性」を直す
文法、型、宣言、アクセス修飾、例外宣言の整合性を取り、コンパイルが通る最小の状態へ戻します。IDE の修正候補や「Jump to source」で該当箇所へ直行し、波線が消えるまで集中して直します。
ランタイムエラーは「契約と現実」を近づける
使用前ガード、早期リターン、境界検証、入力正規化、try-with-resources による後始末保証、例外の包み直し(原因連鎖)で「失敗しても壊れない」設計へ寄せます。テストで境界と異常系を先に用意すると、実行時の崩れを事前に捕まえられます。
仕上げのまとめ
コンパイルエラーは「コードが成立していない」シグナルで、文法・型・宣言・チェック例外の整合性を直せば起動できます。ランタイムエラーは「実行時の値や環境が契約を破った」シグナルで、入力検証、境界チェック、後始末の保証、例外設計で防ぎます。違いを理解したら、失敗を再現し、スタックトレースの先頭行から事実を観測し、ガードや設計の修正へ落とす——この型が身につけば、エラーは「すぐ見つけて、正しく直せる」相手になります。
