Java | 基礎文法:デバッグの基本

Java Java
スポンサーリンク

デバッグの全体像

デバッグは「バグの原因を特定して、意図どおりに直す」作業です。Javaでは、再現手順の固定、ログやスタックトレースの読み取り、IDEのブレークポイントでの実行停止・変数観察、最小化したテストケースの切り出しが基本になります。重要なのは「思い込みを捨てて、事実を観測する」こと。症状の記録→再現→観測→仮説→検証→修正→再テストの流れを短く回すのがコツです。


まずやること:再現と事実の記録

再現条件を固定する

  • 入力と環境を固定: 同じ引数・ファイル・環境変数・時刻で再現できる状態にします。
  • 最小ケースへ縮小: 問題が発生する最小限のコードやデータに削ると、原因が浮き上がります。
// まずは「この1行」で落ちるかを再現
System.out.println(Integer.parseInt("123"));   // OK
System.out.println(Integer.parseInt("12a"));   // ここで NumberFormatException
Java

症状を正確に書き出す

  • 例外メッセージ: 「何が・どこで」出たかをそのまま記録(コピペ推奨)。
  • スタックトレース: 先頭の原因行(クラス名:行番号)から読み始める。
Exception in thread "main" java.lang.NumberFormatException: For input string: "12a"
    at Sample.main(Sample.java:5)
Java

ログとスタックトレースの読み方(重要ポイントの深掘り)

スタックトレースの要点

  • 先頭行が出発点: 「どの変数が null」「0で除算」など最近のJDKはヒント付き。該当行のコードを開いて、どの参照・値が問題かを特定します。
  • 原因連鎖(cause)を辿る: 「包み直し」がある場合は Caused by の最後まで読むと根本原因に到達します。

ログは「事実」を残す

  • 何が・いつ・どの入力で: ID・入力値・ステータスをキー=値で記録。
  • 一度だけ出す: 下位でログ→上位でもログを重複させない。分析が難しくなります。
log.error("read_failed path={} reason={}", path, e.toString());
Java

IDE デバッグ:ブレークポイントとウォッチ

実行を止めて見る

  • ブレークポイント: 該当行で停止。条件付き(変数が特定値の時だけ止める)を使うと効率的。
  • ステップ操作: ステップイン(メソッド内部へ)、ステップオーバー(次の行へ)、ステップアウト(呼び出し元へ戻る)で流れを追う。

変数の中身を観測する

  • ウォッチ: 監視したい式(例:list.size(), map.get(key))を登録して、行ごとに値を確認。
  • 評価: IDEの「Evaluate」機能で、その場で式を評価すると仮説検証が速い。
// ブレークポイントで停止中に list.size() をウォッチして「期待通りか」を確認
Java

小さく切り出す:最小テストケースと再現コード

問題だけを抽出する

  • 独立した main や JUnit: 問題のある処理だけを呼ぶミニプログラムを作る。
  • 外部依存を切る: ダミー入力・スタブ化で環境差を排除。ロジック単体を検証。
public class Repro {
    public static void main(String[] args) {
        System.out.println(slice("abcdef", 2, 5)); // 期待:"cde"
        System.out.println(slice("abc", 2, 5));    // 例外で再現
    }
    static String slice(String s, int begin, int end) {
        if (begin < 0 || end > s.length() || begin > end)
            throw new IllegalArgumentException("0<=begin<=end<=length");
        return s.substring(begin, end);
    }
}
Java

典型バグの特定法(重要ポイントの深掘り)

NullPointerException を潰す型

  • どの変数が null: メッセージと原因行で特定。使用前に null ガード、短絡評価、Objects.requireNonNull、Optional を活用。
  • equals の呼び順: "OK".equals(s)Objects.equals(a, b) にすると null セーフ。
Objects.requireNonNull(s, "s must not be null");
if (s != null && !s.isBlank()) { /* ... */ }
Java

IndexOutOfBoundsException の境界

  • 原則: 0 ≤ index < size、substring は [begin, end)(end 排他)。
  • for の条件: < length を使う。<= は範囲外。
for (int i = 0; i < arr.length; i++) { /* ... */ }
Java

ArithmeticException と数値の落とし穴

  • 整数の 0 除算: 事前チェックで弾く。
  • 浮動小数点: 例外にならず Infinity/NaN。Double.isInfinite/Double.isNaN を確認。
  • オーバーフロー: Math.addExact で検知。
if (b == 0) throw new IllegalArgumentException("divisor must not be 0");
Java

ログ設計と後始末:原因を見失わないために

原因を必ず連鎖させる

  • 包み直すとき: new Xxx("msg", cause) の形で元の例外を保持。スタックトレースが繋がる。
try {
    dao.save(order);
} catch (java.sql.SQLException e) {
    throw new IllegalStateException("保存失敗: id=" + order.id(), e);
}
Java

try-with-resources で後始末を自動化

  • クローズ漏れを防ぐ: 例外時でも自動でリソース解放。サプレスト例外も記録される。
import java.nio.file.*;
import java.io.*;

try (var br = Files.newBufferedReader(Path.of("input.txt"))) {
    System.out.println(br.readLine());
}
Java

例題で身につける

例 1: スタックトレースから原因行へ直行する

public class Sample {
    public static void main(String[] args) {
        String s = null;
        System.out.println(s.length()); // ← スタックトレースの行番号に一致
    }
}
Java

実行して例外を出し、行番号の場所を開く。参照が null である事実を確認し、使用前のガードを追加する。

例 2: 条件付きブレークポイントでピンポイント停止

for (String id : ids) {
    // id が "X-999" のときだけ止める条件付きブレークポイント
    process(id);
}
Java

大量データでも、問題のIDにだけ止めて原因を観測できる。

例 3: ログで「何が・どこで」を固定

private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(App.class);

void run(String path) {
    try {
        String line = readFirst(java.nio.file.Path.of(path));
        log.info("read_ok path={} len={}", path, line.length());
    } catch (Exception e) {
        log.error("read_failed path={}", path, e); // 例外連鎖ごと記録
    }
}
Java

仕上げのアドバイス(重要部分のまとめ)

デバッグの基本は「再現条件の固定」「スタックトレースの先頭行から読む」「IDEで止めて観測する」「最小ケースに切り出す」の4点です。NPE・IOBE・算術の落とし穴は型どおりに潰し、ログは原因連鎖と入力文脈を必ず残す。後始末は try-with-resources で自動化し、例外の包み直しでは cause を渡す。思い込みではなく事実を観測し、仮説→検証→修正→再テストを短いサイクルで回す——この型が身につけば、バグは「すぐ見つけて、すぐ直せる」相手になります。

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