はじめに
最終回は「困ったらここ!」の総合エラー辞典。
“つまずき”をぜんぶここに集約。
各項目は 症状 → 原因 → 直し方(+予防) の順に
サクッと確認できるようにしたから、デバッグの相棒に。
関連回:
- #1 基本構文(whileの流れ・代表パターン)
- #2 do-while(最低1回実行/メニュー)
- #3 無限ループと制御(
while(true)/break/continue/ラベル)- #4 実務活用(入力・DB・状態待ち)
🐛 1. 更新忘れ/終了条件が変わらない(永遠に回る)
症状: 画面が同じログを延々と出し続ける/CPUが高止まり。
int balls = 3;
boolean caught = false;
while (balls > 0 && !caught) {
// balls-- も caught=true も起きない
}
直し方: 終了に関わる値(カウンタ・フラグ)を各周回で必ず更新。
while (balls > 0 && !caught) {
balls--; // 進める
if (tryCatch()) caught = true; // 状態を変える可能性
}
予防: デバッグ時は監視カウンタ(非常口)を併用。
設計時に「何が起きたら終わり?」を日本語で書いてからコード化。
🐛 2. 条件の向き違い(&&/||/否定)
症状: すぐ終わる/ほぼ無限に回る。
boolean caught=false; int balls=3;
while (balls > 0 || !caught) { /* NG: どちらか真なら回る→ほぼ無限 */ }
直し方: 同時に満たす間だけ回したい→&&。
while (balls > 0 && !caught) { /* OK */ }
予防: 口に出して読む:「ボールがあり かつ 未捕獲のあいだ回す」。否定は括弧で明示。
🐛 3. 代入と比較の取り違い(特に boolean)
症状: ずっと真になる/コンパイルエラー。
boolean running = true;
do { /* ... */ } while (running = true); // NG: 代入の結果は常に true
直し方: 比較は ==。boolean はそのまま読むのが定石。
do { /* ... */ } while (running); // OK
予防: IDE の警告をON。条件に副作用のある式は避ける。
🐛 4. 浮動小数をきっちり比較して抜けない
症状: x != 1.0 が永遠に真になる。
double x = 0.0;
while (x != 1.0) { x += 0.1; }
直し方: **許容誤差(イプシロン)**で比べる/カウンタで制御する。
while (Math.abs(x - 1.0) > 1e-9) { x += 0.1; }
// もしくは回数で:for/カウンタ while で 10 回だけ足す
予防: “連続加算”での到達判定は避け、閾値または回数で設計。
🐛 5. 入力の読み残し(nextInt() と nextLine())
症状: 文字列入力を待つはずが即スルーして空文字になる。
java.util.Scanner sc = new java.util.Scanner(System.in);
int lv = sc.nextInt(); // 数字だけ読み、末尾の改行を残す
String name = sc.nextLine(); // ← その改行を拾って空文字に
直し方:
① 全部 nextLine() で受けて parse、または
② nextInt() 後にダミー nextLine() で改行を捨てる。
int lv = sc.nextInt();
sc.nextLine(); // 改行を捨てる
String name = sc.nextLine();// ここで本入力
予防: nextLine()統一が無難。
🐛 6. スピン(CPU100%)で待ってしまう
症状: 「待っているだけ」のはずがCPUが燃える。
while (!ready()) { /* 何もしないで回り続ける */ }
直し方: 座って待つ(ブロッキング)か、Thread.sleep() で間引く。
while (!ready()) { try { Thread.sleep(20); } catch (InterruptedException ignored) {} }
// もしくは
String line = new java.util.Scanner(System.in).nextLine(); // 入力が来るまで停止
予防: 可能ならブロッキングAPI(キューの take() など)を優先。
🐛 7. break / continue の取り違い(+ラベル)
症状: 「2だけ飛ばすつもり」が全終了になる など。
int i=0;
while (i<5) {
if (i==2) break; // NG: ループ自体を終了
System.out.println(i);
i++;
}
直し方: スキップは continue、完全脱出は break。多重ループはラベル付きで外へ。
int k=0;
while (k<5) { if (k==2) { k++; continue; } System.out.println(k); k++; }
outer:
while (condOuter) {
while (condInner) {
if (found()) break outer; // 二重ループごと脱出
}
}
予防: 「この continue はどのループに効く?」を意識。多重時はラベル名を目的語に。
🐛 8. ラベルの置き場所ミス
症状: break label; が効かない/コンパイルエラー。
escape: { while (cond) { /* ... */ } } // NG: ブロックにしか付いてない
直し方: ループ文の直前に置く。
escape:
while (outer) {
while (inner) {
if (found()) break escape;
}
}
🐛 9. do-while の文法・意図ミス
症状: セミコロン忘れ/空ループ化/否定の勘違い。
do {
work();
} while (cond) // NG: セミコロンが必要
// 空ループ
do ; while (cond);
// 否定の勘違い(n で終わりにしたいのに逆)
do { read(); } while (ans.equals("n")); // NG
直し方: } while (cond); とセミコロン必須/本体は {} で囲む/否定は明示。
do { read(); } while (!ans.equals("n")); // n 以外なら続ける
予防: フォーマッタを使う。条件に副作用を入れない。
🐛 10. finally で制御が上書きされる
症状: break のはずが return に負けるなど、抜け方が変わる。
while (true) {
try {
if (ok()) break;
} finally {
// ここで return/throw すると try 内の break より強い
}
}
直し方: finally での return/throw は慎重に。リソース解放専用にする。
予防: 脱出ロジックは1か所に集約。
🧭 付録:保険としての「監視カウンタ」
テスト中の“うっかり無限”を防ぐ非常口。
int guard = 10_000;
while (cond && guard-- > 0) { /* ... */ }
if (guard <= 0) System.out.println("⚠ ガードで停止。条件を見直してね");
注意: 本番ロジックは正しい終了条件で設計し、ガードは常用しない。
あとがき
ここまで読んでくれて、本当にありがとうございました。
「プログラミングって難しい…」って思ってた人も、
「ちょっと楽しいかも…!」って思ってもらえたらうれしいな。
次の投稿も、よろしくおねがいします。
ピッピカチュウ!!
💬 コメント・フィードバック歓迎!
「この章わかりやすかった!」
「これ表現まちがってない?」
「次は○○をやってほしい!」などなど、
お気軽にコメントで教えてくださいね!