MEDIA

メディア

  1. TOP
  2. メディア
  3. java.lang.NullPointerExceptionの原因と直し方を最短で解説

java.lang.NullPointerExceptionの原因と直し方を最短で解説

しかし、Java開発で最も遭遇しやすい例外の1つがjava.lang.NullPointerExceptionです。名前の通り「nullが原因」ですが、実際の現場では「どこで」「何が」nullなのか分からず、復旧に時間がかかりがちです。

そのため本記事では、NullPointerExceptionの定義から、原因特定の具体手順、よくある発生パターン、そして再発防止までを一気通貫で整理します。さらに、近年のJDKで使える原因特定の補助機能にも触れます。

なお、NullPointerExceptionの一次情報として、Oracle公式のAPIドキュメントも確認できます(参考:NullPointerException(Java SE 11 日本語))。

NullPointerExceptionが起きる理由

まず、NullPointerExceptionは「オブジェクトが必要な場面でnullを使おうとした」時にスローされます。例えば、nullのインスタンスメソッド呼び出し、フィールド参照、配列のlength参照などが典型です。

一方で、同じ“null”でも原因の入り口は複数あります。未初期化、想定外の戻り値、外部I/Fの欠損、マッピング漏れなど、データの流れのどこかでnullが混入します。したがって「どこでnullが生まれたか」を辿れる構造にしておくことが重要です。

代表的な発生パターン

例えば、次のようなケースでNullPointerExceptionが発生しやすいです。

  • 参照変数がnullのまま、インスタンスメソッドを呼ぶ
  • 参照変数がnullのまま、フィールドへアクセスする
  • 配列がnullのまま、lengthを参照する
  • 配列がnullのまま、添字アクセスする
  • throw nullのようにThrowableとして扱う

そのうえで、Oracleの英語版APIにも同様の例が列挙されています(参考:NullPointerException(Java SE 8))。

原因を最短で特定する手順

しかし、NullPointerExceptionの本当の難しさは「原因がnull」だと分かっても「どの式がnullか」がすぐに分からない点です。そこで、現場で効く特定手順を順番に実行します。

ステップ1:スタックトレースで“発生行”を固定する

まず、例外ログのスタックトレースから「最初に自分のコードが出る行」を探し、該当ファイルと行番号を開きます。さらに、その行にある式を“部品”に分解し、どれがnullになり得るかを洗い出します。

例えば、次の1行で落ちた場合、null候補は複数あります。

String name = user.getProfile().getName();

したがって、次の順で候補を絞ります。

  • userがnullではないか
  • user.getProfile()の戻り値がnullではないか
  • getName()の内部でnull参照が起きていないか

ステップ2:ガード節で“どれがnullか”を確定する

さらに、切り分けが難しい場合は、ガード節(早期return)やObjects.requireNonNullを使い、nullの発生地点を前倒しします。これにより、スタックトレースが「より原因に近い行」を指すようになります。

import java.util.Objects;

public class GuardSample {
  public static String safeName(User user) {
    Objects.requireNonNull(user, "user is null");
    Profile profile = Objects.requireNonNull(user.getProfile(), "profile is null");
    return Objects.requireNonNull(profile.getName(), "name is null");
  }
}

一方で、何でもrequireNonNullで固めると、業務的に“nullを許容すべき”場面でも落ちます。したがって「契約(入力・戻り値の仕様)としてnullを許すか」を先に決め、許さない箇所だけで強制するのがコツです。

ステップ3:JDKのHelpful NullPointerExceptionsを活用する

さらに、JDKではNullPointerExceptionの原因をより詳しく示す仕組みが追加されています。具体的には、JEP 358(Helpful NullPointerExceptions)で「どの変数・式がnullだったか」を説明するメッセージ改善が提案されています(参考:JEP 358)。

そのため、原因特定の速度を上げたい場合は、JDKの機能や実行オプションの利用も検討できます。特にログしか見られない運用環境では、例外メッセージの情報量が生産性に直結します。

よくある原因と直し方(パターン別)

しかし、NullPointerExceptionの修正は「場当たり的なnullチェック」だけだと再発します。そこで、原因の型を分類し、直し方をセットで覚えると安定します。

未初期化:生成・注入・設定が抜けている

例えば、コンストラクタで初期化すべきフィールドが抜けている、DI設定が漏れている、設定ファイルのキーが誤っている、といったケースです。この場合、対症療法のnullチェックよりも「初期化の責務」を修正するのが本筋です。

public class Service {
  private Repository repo;

  public Service(Repository repo) {
    this.repo = repo; // 初期化の責務をコンストラクタで明確化
  }

  public Result run() {
    return repo.find(); // repoがnullになりにくい構造へ
  }
}

そのため、初期化漏れを防ぐには「必須依存はコンストラクタで受け取り、finalで保持する」設計が有効です。さらに、テストでコンストラクタ生成を通すことで、DI漏れを早期に発見できます。

想定外の戻り値:外部I/Fや検索結果がnull

一方で、DB検索・API呼び出し・設定参照などは、状況によって結果が存在しないことがあります。ここで重要なのは「存在しない」をnullで表すのか、空配列・空Optional・例外で表すのかを統一することです。

例えば、コレクションはnullではなく空で返す方が扱いやすいことが多いです。したがって、戻り値ポリシーをチームで決めると、NPEは大きく減ります。

import java.util.Collections;
import java.util.List;

public class Repo {
  public List findNames(boolean found) {
    if (!found) {
      return Collections.emptyList(); // nullではなく空で返す
    }
    return List.of("Alice", "Bob");
  }
}

データ構造の前提崩れ:ネスト参照で落ちる

さらに、DTOやレスポンスがネストしていると、a.getB().getC().getD()のような連鎖が増えます。この場合、どこがnullかの切り分けが難しくなるため、途中結果を変数に置くのが効果的です。

そのため、可読性と保守性の観点でも「中間変数+ガード節」による分解は、NPE対策として非常に強力です。

再発防止のベストプラクティス

しかし、NullPointerExceptionをゼロにするには、修正だけでなく“設計と運用”まで含めた仕組み化が必要です。そこで、現場で採用されやすい対策をチェックリストとしてまとめます。

設計ルール:nullを許す範囲を明確化する

  • 必須の引数・依存はコンストラクタで受け取り、nullを禁止する
  • コレクションは基本的に空で返し、nullを返さない
  • 戻り値の“未存在”をどう表現するか(null/例外/Optional)を統一する
  • DTOのネスト参照は中間変数で分解し、前提をコメント化する

実装ルール:ガード節とログで“原因の見える化”をする

さらに、運用でログしか追えない場合、例外発生時に入力や重要なキー情報が残らないと復旧が遅れます。したがって、境界(ControllerやUseCase入口)で入力のnullを弾き、識別子をログに残す設計が有効です。

一方で、ログに個人情報や機微情報を出さない配慮も必要です。そこで、ID・処理区分・件数など、追跡に必要な最小限の情報に絞るのが安全です。

まとめ

したがって、java.lang.NullPointerExceptionは「nullを使ってはいけない場面で使った」結果として起きます。まずはスタックトレースで発生行を固定し、式を分解してnull候補を洗い出してください。さらに、ガード節やrequireNonNullで原因箇所を前倒しし、必要に応じてJDKのメッセージ改善機能も活用すると、特定が速くなります。

最後に、戻り値ポリシーや初期化責務を設計として固めれば、NPEは“個人技”ではなく“チームの再現性ある品質”として減らせます。今日からは、対症療法ではなく再発防止までセットで進めてみてください。

つまずいたら、現役エンジニアと一緒に解決しよう

この記事で扱った【Java(NullPointerExceptionの原因特定と対策)】は、独学だと細部で詰まりやすい分野です。さらに理解を深めたい方は、Zerocodeで実務直結の学習を進めてみませんか。

現役エンジニア1on1

コード添削・詰まり解消を丁寧にサポート。

実務設計まで網羅

Java/SQL/Spring Bootを体系的に習得。

ポートフォリオ支援

現場で刺さる成果物づくりを伴走。

完全オンライン

時間と場所を選ばず学べます。

Join us! 未経験からエンジニアに挑戦できる環境で自分の可能性を信じてみよう 採用ページを見る→

記事監修

ドライブライン編集部

[ この記事をシェアする ]

記事一覧へ戻る