ステップ実行の全体像
ステップ実行は、プログラムを一行ずつ進めて「流れ」と「その瞬間の状態」を目で確認するデバッグ手法です。IDE(IntelliJ IDEA、Eclipse、VS Code など)でブレークポイントを置き、そこから「ステップオーバー(次の行へ)」「ステップイン(メソッド内部へ)」「ステップアウト(呼び出し元へ戻る)」を使い分けます。思い込みではなく事実の挙動を観測できるため、分岐の取り違え、境界条件、変数更新の誤りを最短で見つけられます。
基本操作と考え方
ブレークポイントで止めて、最初の一歩を正しく置く
ブレークポイントは「ここで一度止まれ」という合図です。問題が起きる直前の行に置き、止まったらその行が「実行される前」の状態を観測します。次にステップオーバーで行を実行し、前後の差分(変数・戻り値)が期待通りかを確認します。まずはこの「止める→実行→差分を見る」のリズムを体に入れましょう。
ステップオーバー・イン・アウトの使い分け
- ステップオーバーは「この行を実行して、次の行へ」。呼び出し先の詳細は追わないときに使います。
- ステップインは「このメソッドの中へ入る」。挙動が疑わしいメソッドの内部を観測したいときに使います。
- ステップアウトは「今いるメソッドの残りを一気に実行して、呼び出し元へ戻る」。深く入り過ぎたときの退避に便利です。
この三つで「どこを詳しく見るか」を選び、無駄に深く潜らないことが大切です。
重要ポイントの深掘り:分岐・ループ・例外の観測
分岐は「条件が何で、どちらへ進んだか」を確認する
if の直前で止め、条件式の各要素をウォッチして真偽を確かめます。次にステップオーバーで分岐後の行へ進み、選ばれた枝が意図通りかを確認します。
boolean hasText = s != null && !s.isBlank();
if (hasText) {
submit();
} else {
warn();
}
Java止まったら s, s.isBlank(), hasText を観測し、次の行で submit() 側に入るか warn() 側に入るかを確かめます。
ループは「開始条件・終了条件・更新」を三点チェック
for/while の開始前で止め、ループ変数と境界(length/size)をウォッチします。各周回でステップオーバーし、変数の更新(i++ など)と終了条件が一致しているかを確認します。異常があれば「終了条件の比較演算子」「更新式の漏れ」が典型です。
int[] a = {10, 20, 30};
for (int i = 0; i < a.length; i++) {
System.out.println(a[i]); // ここで止めて i, a.length を見る
}
Javaもし <= を使っていたら、最後に範囲外アクセスが起きる事実がステップで分かります。
例外は「発生直前の値」と「どの行で投げられたか」を確認
例外が投げられる可能性がある行の直前で止め、分母、参照、インデックスなどの値を観測します。例外発生後はスタックトレースの先頭行(ファイル名:行番号)に戻って、その行で再度ステップ実行しながら原因となる値の変化を追います。
int a = 10, b = 0;
int c = a / b; // 直前で止めて b が 0 である事実を確認
Java実例で身につける:ステップで原因を特定する
例 1: 税計算の丸め位置の誤りを可視化
public class PriceCalc {
static int taxed(int subtotal, double rate) {
int tax = (int) Math.round(subtotal * rate);
return subtotal + tax;
}
public static void main(String[] args) {
int subtotal = 999;
double rate = 0.1;
int total = taxed(subtotal, rate);
System.out.println(total);
}
}
Javataxed の中へステップインし、subtotal * rate と Math.round(...) の結果、subtotal + tax の差分を行ごとに確認します。「税額丸め」か「合計丸め」かの意図と実装のズレが、数値の変化で即座にわかります。
例 2: 文字列処理の分岐ミスを潰す
static String normalize(String s) {
if (s == null || s.isBlank()) return "";
return s.trim().toUpperCase();
}
Javaif の手前で止め、s == null と s.isBlank() を別々にウォッチ。|| の短絡評価でどちらが判定され、どちらがスキップされたかをステップで確認できます。&& と取り違えた場合は通過条件が変わることが、進む枝で明確になります。
例 3: ネスト探索の早期終了が効いているかを確認
int[][] g = {{1,2,3},{4,42,6}};
search:
for (int r = 0; r < g.length; r++) {
for (int c = 0; c < g[r].length; c++) {
if (g[r][c] == 42) {
System.out.println("found");
break search;
}
}
}
Java外側ループの行で止めて、42 を見つけた瞬間に break search で外側まで抜けているかをステップで確認します。抜けずに内側だけ終わっている場合、ラベルの付け忘れや誤った break 位置が原因だと分かります。
ステップ実行を強くするテクニック
条件付きブレークポイントで「必要な場面だけ」止める
大量データでも、特定の条件(id が “X-999″、i == a.length-1 など)でのみ停止するように設定します。これで不要な停止を減らし、問題の場面に集中できます。ヒットカウント(100回目で止める)も境界の観測に有効です。
式の即時評価で仮説を検証する
停止中に IDE の「Evaluate Expression」で map.containsKey(key), Objects.equals(a, b), i < a.length のような式を評価し、意図と一致するかをその場で確かめます。コードを書き換えずに検証できるため、仮説→確認のサイクルが短くなります。
副作用のある呼び出しを評価しない
停止中に iterator.next() や I/O を伴う dao.find(...) のようなメソッドを評価すると、状態が進んだり外部と通信したりして、再現性が崩れることがあります。ウォッチ・評価は「読み取りだけ」に限定し、重い操作はコード側の修正に回します。
つまずきポイントと対策
深く潜りすぎる迷子問題
無闇にステップインすると、関係ないユーティリティやライブラリの内部に迷い込みます。怪しいメソッドだけに入る、入ったら早めにステップアウトする、ブレークポイントを目的地近くへ移す——この三点で迷子を防ぎます。
並行処理で値が揺れる
複数スレッドが同じデータを触ると、停止中でも他スレッドが先に進み、値が変わります。現在スレッドを固定し、ロック取得中に観測する、スレッド名やIDをウォッチするなどで文脈を掴み、必要なら一時的に同期を強めます。
最小化せずに本体で苦戦する
巨大なアプリを丸ごと止めるより、問題のロジックだけを切り出したミニプログラムや単体テストで再現し、そこでステップ実行するほうが速く正確です。外部依存をスタブ化して、純粋なロジックだけに集中しましょう。
仕上げのアドバイス(重要部分のまとめ)
ステップ実行は「止める→一行進める→差分を見る」を軸に、分岐・ループ・例外の挙動を事実で確認する技法です。ステップオーバー・イン・アウトを意図的に使い分け、条件付きブレークポイントと式評価で観測点を絞る。副作用のある評価は避け、必要なら最小の再現コードで実施する。並行処理ではスレッド文脈を意識して観測する——この型が身につけば、バグは「どこでズレたか」を数ステップで特定できます。
