NullPointerException の原因と直し方
nullに対する操作で発生します。発生箇所の特定方法と、そもそもnullを持ち回さない設計を扱います。
エラーメッセージの読み方
Exception in thread "main" java.lang.NullPointerException:
main- スレッド名 — mainなら起動直後の処理で発生しています
java.lang- パッケージ — java.lang なら標準の基本例外です
NullPointerException- 例外クラス — 何が起きたか。ここを検索するのが最短です
このエラーが出る典型パターン
パターン1
1 public class Main { 2 public static void main(String[] args) { 3 String s = null; ^ 4 System.out.println(s.length()); 5 } 6 }
nullは実体がない状態です。空文字""は長さ0のStringという実体なので、メソッドを呼べます。
直し方: null を "" にします。
パターン2
1 import java.util.HashMap; 2 import java.util.Map; 3 4 public class Main { 5 public static void main(String[] args) { 6 Map<String, Integer> m = new HashMap<>(); 7 int v = m.get("x") ; ^ 8 System.out.println(v); 9 } 10 }
存在しないキーのgetはnullを返します。intへの代入時に自動開封が起きてNullPointerExceptionになります。
直し方: get("x") を getOrDefault("x", 0) にします。
パターン3
1 public class Main { 2 static String find(int n) { 3 if (n > 0) return "found"; 4 return null; ^ 5 } 6 public static void main(String[] args) { 7 System.out.println(find(-1).length()); 8 } 9 }
nullを返すメソッドは呼び出し側にnullチェックを強制します。空の値を返すほうが安全です。
直し方: null を "" にします。
パターン4
1 public class Main { 2 static class Address { String city = "Tokyo"; } 3 static class Person { Address address ; } ^ 4 public static void main(String[] args) { 5 Person p = new Person(); 6 System.out.println(p.address.city); 7 } 8 }
参照型フィールドの既定値はnullです。使う前に必ずnewで実体を作ります。
直し方: (空) を = new Address() にします。
パターン5
1 public class Main { 2 public static void main(String[] args) { 3 Boolean flag = null ; ^ 4 if (flag) System.out.println("yes"); 5 } 6 }
if文の条件はbooleanへ自動開封されます。Booleanがnullだと開封できず例外になります。
直し方: null を false にします。
よくある誤解
nullを代入した行では落ちません。使った行で落ちます。スタックトレースの先頭が示すのは「使った場所」です。
実務での勘所
実務では単一の原因より、複数の経路が重なって起きることが多いです。Map.get()は該当キーが無いとき例外ではなくnullを返すため、存在チェックを忘れると次の行で落ちます。a.getB().getC()のようなメソッドチェーンは、途中の呼び出しがnullを返した瞬間にどこで落ちたか分かりにくくなります。コンストラクタの中で他のインスタンスメソッドを呼ぶと、そのメソッドが参照するフィールドがまだ初期化されていないことがあります。JDK 14でHelpful NullPointerExceptions(JEP 358)が導入され、JDK 15からは既定で有効になりました。「どの変数がnullだったか」がエラー文にそのまま書かれるようになったのはこのためで、このサイトのエラー文もこの形式です。JDK 14より前、またはJDK 14でオプションを明示的に有効化していない環境では、原因の説明がなく「Exception in thread "main" java.lang.NullPointerException」の次にスタックトレースが1行出るだけです。この短い形式しか出ない場合は、スタックトレースの行番号から自力で原因の行を特定してください。
関連するエラー
- オートボクシングで起きる NullPointerExceptionIntegerはnullを持てますが、演算時にintへ開封されます。
- variable might not have been initialized の原因と直し方ローカル変数は自動で初期化されません。
- == と .equals() の違い(文字列比較)Java初学者が最も多く踏む罠。
- ArithmeticException: / by zero の原因と直し方int型同士の割り算・剰余算で分母が0だと例外になります。