Javaのエラーは、①いつ出たか(ビルド時か実行時か)②スタックトレース1行目の例外名 ③一番下のCaused by、の3点を見れば、多くの場合、原因を数分で絞り込めます。
この記事では、エラーの切り分けフローと頻出9例外の原因・対処法を早見表でまとめ、コンパイルエラーや「シンボルを見つけられません」など個別に深掘りした記事への道案内もしています。

真っ赤なスタックトレースを見ただけで固まってしまう人は多いはずですが、私もSES時代は半日かけてログとソースコードをにらめっこしていました。
この記事でわかること
- 自分のエラーがどれかを見分ける切り分けフロー
- Javaのエラー3種類と、例外クラスの継承関係
- スタックトレースを最短で読み解く3ステップ
- 実務で頻出する例外9選の原因と対処法
- try-catch-finallyを正しく書くための判断基準
PARTS LIST /目次
Javaのエラーは3種類|まず違いを理解する
Javaのエラーは大きく3種類に分かれます。コンパイルエラー、実行時エラー、論理エラーです。
エラーメッセージを読む前に、自分が今どのエラーと向き合っているのかを区別するだけで、対処のスピードは一気に変わります。
例外(Exception)とは、プログラムの実行中に発生し、正常な処理の流れを妨げる出来事を表すJavaのクラスです。
特に実行時エラーの正体は「例外(Exception)」であり、Javaはこの例外をクラスの継承関係で厳密に管理しています。
継承関係を知らないままcatch文を書くと、思わぬところで例外を取りこぼす原因になるので、まずは全体像から押さえていきましょう。
コンパイルエラーと実行時エラーの違いは何か?
コンパイルエラーはビルド時にJavaコンパイラが検出する文法ミスです。セミコロン忘れや型の不一致が典型で、実行前に必ず気づけます。一方、実行時エラーはプログラムが動いている最中に発生し、コンパイルは通ってしまうのが厄介なところ。
NullPointerExceptionのように、特定の条件でしか顔を出さないバグはここに分類されます。
論理エラーはさらに厄介で、エラーメッセージすら出ないまま「計算結果が違う」形で発覚します。
Exception・RuntimeException・Errorの継承関係
Javaの例外はすべてThrowableクラスを頂点に、ErrorとExceptionの2系統に分かれます。
Errorはメモリ不足やスタックオーバーフローなど、アプリ側では回復できない重大な事象です。
Exceptionはさらにチェック例外と非チェック例外(RuntimeException)に分岐し、日常のバグの大半はRuntimeException側に集中しています。
この継承図を頭に描けるかどうかで、例外処理の設計力は大きく変わります。
チェック例外と非チェック例外の見分け方
見分け方はシンプルで、コンパイラがtry-catchかthrows宣言を強制してくるかどうかです。
IOExceptionやSQLExceptionはチェック例外なので、書かなければコンパイルエラーになります。
対してNullPointerExceptionやArrayIndexOutOfBoundsExceptionは非チェック例外で、書かなくてもビルドは通ってしまう分、実行してから初めて気づきます。
自分のエラーはどれ?症状から原因を切り分ける
エラーが出たら、まず「実行前(ビルド時)に出たのか」「実行中に例外として出たのか」「何も出ないのに結果がおかしいのか」を切り分けます。ここを間違えると、見るべき場所がまったく変わってしまいます。
切り分けの流れを図にすると、次のようになります。
- ビルド時に出た:コンパイルエラー。エラー行の文法・型を直す(詳しくは上の「あわせて読む」の記事へ)
- 実行中に例外が出た:次のH2「エラーメッセージの読み方」で例外名とCaused byを確認し、早見表で対処法を探す
- エラーは出ないが結果がおかしい:論理エラー。後半の「IDEのデバッガを使った原因特定」へ
コンパイルエラーの中でも特に多い「シンボルを見つけられません(cannot find symbol)」は原因が7パターンに絞れるので、Java「シンボルを見つけられません」原因7つと解決コード例で症状別に確認してください。
エラーメッセージ(スタックトレース)の読み方3ステップ
スタックトレースは、エラーが発生するまでにどのメソッドを経由したかを記録した「地図」です。
ただ闇雲に上から読むのではなく、順番とポイントを押さえるだけで原因特定の時間は大幅に短縮できます。
発生した例外クラス名を最初に確認する
一番上の行には、発生した例外クラス名とメッセージが書かれています。
ここを読むだけで「何が」起きたのかは大半わかるので、まず最初に確認する癖をつけましょう。
NullPointerExceptionなら「何かがnullだった」、ClassCastExceptionなら「型変換に失敗した」と、クラス名自体がヒントになっています。
Caused byを下から遡って根本原因を特定する
複数の例外が連鎖している場合、下の方に「Caused by」という行が出てきます。
このCaused byは根本原因を示していて、実は一番下のCaused byこそが本当のバグの発生源です。
上から読んで見当がつかないときは、先に一番下まで目を通す方が早く原因にたどり着けます。
たとえばInvocationTargetExceptionは、リフレクション経由で呼び出したメソッドの中で起きた例外を包んだ「ラッパー」です。この例外名自体に原因はないので、Caused byの行(コードではgetCause())に出ている元の例外を読みましょう。
自分が書いたクラス名を優先的に疑う
スタックトレースにはフレームワークやライブラリのクラス名も大量に混ざります。
すべてを追う必要はなく、自分のパッケージ名(例: com.example)が出てくる行だけを探しましょう。
原因の大半は自分が書いたコードにあり、ライブラリ側のバグであることはまれだというのが、これまでの経験則です。
LOG / スタックトレースの例
↓ 番号の順に読む
よく出るJavaの例外9選と原因・対処法早見表
名前を知っているだけの例外も、発生パターンさえ押さえれば初見のコードでも数分で原因を潰せるようになります。
早見表の中でも、特に遭遇率が高い4つはコード例つきで見ていきます。
NullPointerExceptionが起きる典型パターン
NullPointerExceptionは、初期化されていない変数やメソッドの戻り値がnullのまま、フィールドやメソッドを呼び出したときに発生します。
典型パターンはHashMapのget()結果をそのまま使うケースで、キーが存在しないとnullが返ってくることを忘れがちです。
Java 8以降はOptionalを使えば、nullの可能性をコード上で明示できます。
私も新人時代は、NullPointerExceptionを何度も出していた時期がありました。

Optionalの存在を知ってからはその頻度が激減したので、NPEが多い人はOptionalを知らない、というのは実感としてよくわかるあるあるです。
// nullチェックの代わりにOptionalを使う例
Optional<String> name = Optional.ofNullable(map.get("key"));
String result = name.orElse("デフォルト値");
Optionalに加えて@Nullableアノテーションやnull判定の書き分けまで押さえたい方は、Java Nullableとは?@Nullableとの使い分けとnull判定・NPE対策で詳しく解説しています。
ClassCastException・NumberFormatExceptionの対処法
ClassCastExceptionは、Object型からダウンキャスト(親クラス型の変数を、より具体的な子クラス型に変換すること)する際に実際の型と一致しないと発生します。
instanceofで型を確認してからキャストすれば防げます。アップキャスト・ダウンキャストの基本から整理したい場合はJavaのキャスト完全ガイド|ClassCastException対策が参考になります。
NumberFormatExceptionは文字列を数値に変換する際、数字以外の文字が混ざっていると起きるので、Integer.parseInt()の前に正規表現でバリデーションするか、try-catchで包むのが定石です。
ArrayIndexOutOfBoundsExceptionと回避策
配列の範囲外アクセスは、ArrayIndexOutOfBoundsExceptionの典型例です。ArrayListなどのListで範囲外の添字を指定した場合は、親クラスのIndexOutOfBoundsExceptionが投げられます。
for文のループ条件を「<= 配列長」と書き間違えるだけで簡単に発生します。
ConcurrentModificationExceptionは、拡張for文でループしながらリストの要素を削除したときに起きます。
削除が必要な場面では、Iterator.remove()を使うのが安全です。
IOException・FileNotFoundExceptionの扱い方
ファイル操作系の例外はチェック例外なので、必ず処理を書いてください。
FileNotFoundExceptionはパスの指定ミスやファイル未生成が原因になることが大半で、パスの存在確認とパーミッションのチェックで大部分は防げます。
リソースの後始末を忘れずに済むよう、try-with-resources構文を使う習慣をつけておくと安心です。
StackOverflowError・OutOfMemoryErrorの原因と対処法
名前が「Error」で終わるこの2つは、ExceptionではなくError系です。catchして処理を続けるものではなく、コードや設定そのものを直す必要があります。
StackOverflowErrorは、再帰呼び出しの終了条件が抜けているときの典型です。スタックトレースに同じメソッドの行が延々と並んでいたら、まず再帰の終了条件を疑いましょう。
OutOfMemoryErrorは、大量のデータを一度にメモリへ読み込んだり、コレクションに要素を溜め込み続けたりすると発生します。-Xmxでヒープの上限を増やすのは、どこでメモリを使っているかを特定してからにしてください。
スタック領域とヒープ領域の違いがあいまいな場合は、図で動かすJVMメモリ|スタックとヒープ・参照・GCの関係を先に読むと、2つのErrorの違いがイメージしやすくなります。
ClassNotFoundException・ArithmeticExceptionも要注意
早見表の9つ以外で初心者がよく出会うのが、この2つです。
ClassNotFoundExceptionは、実行時に必要なクラスがクラスパス上に見つからないときに発生します。コンパイルは通ったのに動かない場合は、依存するjarが含まれているか、Maven/Gradleの依存関係とクラスパスの設定を確認しましょう。
ArithmeticExceptionは、整数を0で割ったとき(メッセージは/ by zero)に発生します。割り算の前に除数が0でないかをチェックすれば防げます。
try-catch-finallyの正しい書き方と独自例外
例外の種類がわかっても、書き方が雑だとバグが潜在化するだけです。実務で一番多いのが、catchブロックを空のままにしてエラーを握りつぶすパターンです。
catchで例外を握りつぶしてはいけない理由
やってはいけないこと
catchブロックの中身を空にする、いわゆる「例外の握りつぶし」は絶対にやってはいけません。
エラーが起きているのに処理が正常終了したように見えてしまい、原因調査が数倍面倒になります。
最低限、ログにスタックトレースを出力するだけでも、後から追跡できる情報は大きく変わります。
以前、本番環境で@Transactionalが同一クラス内の呼び出しでは効かないことに気づかず、データ不整合が発生してから初めて問題に気づいたことがありました。
エラーを握りつぶすと、気づかないまま進行するバグを量産することになります。
finallyとtry-with-resourcesの使い分け
finallyは例外の有無にかかわらず必ず実行したい処理(接続のクローズなど)に使います。
ただし、ファイルやDB接続のクローズ処理に限っては、Java 7以降のtry-with-resources構文を使う方がシンプルです。
AutoCloseableを実装したリソースなら、finallyでclose()を書かなくても自動的に解放されます。
// try-with-resourcesでクローズ処理を自動化
try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) {
String line = reader.readLine();
} catch (IOException e) {
logger.error("ファイル読み込みに失敗しました", e);
}
throws宣言で呼び出し元に例外を伝播させる書き方
throws宣言は、メソッドの中で例外をcatchせず、呼び出し元に処理を委ねるための仕組みです。
IOExceptionのようなチェック例外は、catchせずに済ませたい場合、メソッド定義にthrows IOExceptionと書かないとコンパイルエラーになります。
呼び出し元で例外処理をした方が意味のある場面ではthrows宣言、その場で対処できるならtry-catchと使い分けましょう。
マルチキャッチ構文で複数例外をまとめる
複数の例外を同じ処理でまとめたい場合、catch (IOException | SQLException e)のようにパイプでつなぐマルチキャッチ構文が使えます。
同じcatchブロックを何度も書く必要がなくなり、コードの見通しが良くなります。ただし継承関係にある例外同士は組み合わせられない点には注意しましょう。
独自例外クラスは作るべき?標準例外との判断基準
標準の例外だけで足りるのか、独自の例外クラスを作るべきなのか。この判断で迷う人は多いはずです。
私自身、駆け出しの頃はとりあえずRuntimeExceptionを継承した独自クラスを量産していましたが、後から見返すと情報量が乏しく、結局あまり役に立っていませんでした。
標準例外で足りるケースと自作すべきケース
引数チェックのようなJavaの標準ライブラリでカバーできる範囲は、IllegalArgumentExceptionなど既存クラスで十分です。
独自例外を作るべきなのは、業務ロジック固有のエラー(在庫不足、権限不足など)を呼び出し元に明確に伝えたいときに限ります。
むやみに増やすと、キャッチする側の分岐がかえって複雑になります。
独自例外を作りすぎるのは、以前、将来の拡張に備えてStrategyパターンを導入したものの、結局そのケースが来なかった「過剰設計」の失敗と同じ構造です。
使うかどうかわからない独自例外を先回りして作るのは、YAGNI(You Ain't Gonna Need It)に反する行動でした。
例外メッセージに入れるべき情報
独自例外を作るなら、メッセージには「何が」「どんな値で」失敗したのかを具体的に書きましょう。
「在庫不足です」だけでは調査に時間がかかりますが、「商品ID:1234の在庫が0のため注文できません」まで書けば、ログを見た瞬間に原因がわかります。
原因不明のJavaエラーで詰まったときのチェックリスト
例外の知識だけでは解決しない、環境まわりのつまずきもあります。原因が例外そのものではなく、ログ設定やIDEの使い方に隠れています。
最後に周辺の見直しポイントを確認しておきましょう。
ログ出力設定とスタックトレースの見え方
本番環境ではログレベルをERRORやWARNに絞ることが多く、必要なスタックトレースが出力されていないケースがあります。
logback.xmlやlog4j2.xmlの設定を見直し、少なくとも例外発生時はスタックトレース全体が記録されるようにしておくと、後から原因を追いやすくなります。ログレベルの設定方法はlogback.xmlの書き方|ログレベル設定とサンプルコードで解説にまとめています。
IDEのデバッガを使った原因特定
エラーメッセージだけで原因が見えないときは、ブレークポイントを張って変数の中身を1行ずつ確認するのが結局は近道です。
IntelliJ IDEAのEvaluate Expression機能を使えば、実行を止めたまま任意の式を評価できるので、仮説検証のスピードが上がります。
ちなみに今はスタックトレースをそのままClaude Codeに貼るだけで、原因の見当がつくことも増えました。
SES時代に半日かけていた調査が、秒で終わる時代になったのは正直驚きです。
よくある質問(FAQ)
Q1. NullPointerExceptionはなぜこんなに多発しますか?
A1. 参照型の変数はデフォルトでnullが入る可能性があり、コンパイラがnullチェックを強制しないためです。Java 8以降はOptional型を使うことで、nullの許容範囲を型レベルで明示できます。
Q2. 例外を握りつぶしても動くなら問題ないですか?
A2. 一時的には動きますが、データ不整合や別の場所での二次障害につながる危険があります。最低限、ログには出力しておきましょう。
Q3. Errorクラスの例外もcatchしていいですか?
A3. 技術的には可能ですが、OutOfMemoryErrorのようなErrorは回復不能な事象を示すため、catchして処理を続けるのは推奨されません。
Q4. チェック例外は全部try-catchで囲むべきですか?
A4. いいえ。呼び出し元で処理できないなら、throws宣言で上位に伝播させる選択肢もあります。むやみに握りつぶすより素直なやり方です。
Q5. スタックトレースが長すぎて追えないときはどうすればいいですか?
A5. 一番上の例外クラス名と、一番下のCaused byだけを先に確認しましょう。途中のフレームワーク部分は読み飛ばして問題ありません。
Q6. ConcurrentModificationExceptionはマルチスレッドの問題ですか?
A6. 名前に反してシングルスレッドでも発生します。拡張for文でループ中にリストを直接変更すると起きる、実装上の制約が原因です。
まとめ|Exception全体マップを手元に置く
Javaのエラーは、種類と継承関係さえ頭に入っていれば、対処のスピードが劇的に変わります。最後に今回のポイントを整理しておきます。
- Javaのエラーはコンパイルエラー・実行時エラー・論理エラーの3種類に分かれる
- 例外はThrowableからError・Exceptionへ続く継承関係を押さえておく
- スタックトレースは一番上の例外名と一番下のCaused byを先に読む
- catchで握りつぶさず、最低限ログには出力する
- 独自例外は業務ロジック固有のエラーだけに絞って作る
半日かけてログを追っていたSES時代と比べると、今はスタックトレースをそのまま貼るだけで原因の見当がつくことも多く、時代の変化を感じます。
とはいえ、根っこにあるException全体マップの知識がないと、AIの回答を正しく判断することもできません。
この記事が、目の前の赤いエラーと向き合う手助けになれば嬉しいです。
| REV. | DATE | 内容 |
|---|---|---|
| 3 | 2026-10-16 | 図解を描き直し(図面のデザインにそろえた) |
| 2 | 2026-10-06 | 記載内容の事実確認と修正 |
| 1 | 2026-08-22 | 初版 |


