NullPointerExceptionとは何ですか、どうすれば修正できますか?

Oct 20 2008

ヌルポインタ例外(java.lang.NullPointerException)とは何ですか?また、その原因は何ですか?

例外によってプログラムが途中で終了するのを防ぐために、原因を特定するためにどのような方法/ツールを使用できますか?

回答

3848 VincentRamdhanie Oct 20 2008 at 20:54

参照変数(つまりオブジェクト)を宣言すると、実際にはオブジェクトへのポインターが作成されます。プリミティブ型の変数を宣言する次のコードについて考えてみますint

int x;
x = 10;

この例では、変数xはでありint、Javaがそれを初期化0します。102行目にの値を割り当てると、の値がで10参照されるメモリ位置に書き込まれxます。

しかし、参照を宣言しようとすると、別のことが起こります。次のコードを取ります。

Integer num;
num = new Integer(10);

最初の行は、という名前の変数を宣言していますがnum、実際にはまだプリミティブ値が含まれていません。代わりに、ポインタが含まれています(型がInteger参照型であるため)。何を指すかをまだ言っていないので、Javaはそれをnullに設定します。これは「私は何も指していません」という意味です

2行目では、newキーワードを使用してタイプのオブジェクトをインスタンス化(または作成)Integerし、ポインター変数numをそのIntegerオブジェクトに割り当てています。

NullPointerExceptionあなたは、変数を宣言しますが、オブジェクトを作成し、(と呼ばれる変数の内容を使用しようとする前に、それを変数に割り当てていない場合に発生するデリファレンスを)。つまり、実際には存在しないものを指しているのです。

逆参照は通常、を使用.してメソッドまたはフィールドにアクセスするとき、またはを使用[して配列にインデックスを付けるときに発生します。

numオブジェクトを作成する前に逆参照しようとすると、が得られますNullPointerException。最も些細なケースでは、コンパイラーが問題をキャッチして「num may not have been initialized、」と通知しますが、オブジェクトを直接作成しないコードを作成する場合もあります。

たとえば、次のような方法があります。

public void doSomething(SomeObject obj) {
   //do something to obj, assumes obj is not null
   obj.myMethod();
}

この場合、オブジェクトを作成するのではなくobjdoSomething()メソッドが呼び出される前に作成されたと想定します。次のようなメソッドを呼び出すことができることに注意してください。

doSomething(null);

その場合、objnull、であり、ステートメントobj.myMethod()NullPointerException。をスローします。

上記のメソッドのように、渡されたオブジェクトに対してメソッドが何かを行うことを意図している場合、それはNullPointerExceptionプログラマーエラーであり、プログラマーはデバッグ目的でその情報を必要とするため、をスローするのが適切です。

NullPointerExceptionメソッドのロジックの結果としてスローされることに加えて、メソッドの引数のnull値を確認し、メソッドの先頭近くに次のようなものを追加することでNPEを明示的にスローすることもできます。

//Throws an NPE with a custom error message if obj is null
Objects.requireNonNull(obj, "obj must not be null");

エラーメッセージで、どのオブジェクトを使用できないかを明確に示すと役立つことに注意してくださいnull。このような検証を行うことの利点は、1)独自のより明確なエラーメッセージを返すことができ、2)obj再割り当てされない限り、nullではなく、安全に参照解除できることがわかっている残りのメソッドについてです。

あるいは、メソッドの目的が渡されたオブジェクトを操作することだけではない場合があるため、nullパラメーターが受け入れられる場合があります。この場合、nullパラメータをチェックして、動作を変える必要があります。また、ドキュメントでこれを説明する必要があります。たとえば、次のdoSomething()ように書くことができます。

/**
  * @param obj An optional foo for ____. May be null, in which case 
  *  the result will be ____.
  */
public void doSomething(SomeObject obj) {
    if(obj == null) {
       //do something
    } else {
       //do something else
    }
}

最後に、スタックトレースを使用して例外と原因を特定する方法

例外によってプログラムが途中で終了するのを防ぐために、原因を特定するためにどのような方法/ツールを使用できますか?

検索バグのあるソナーはNPEを検出できます。 ソナーはJVMによって引き起こされたnullポインタ例外を動的にキャッチできますか

現在、Java 14には、NullPointerExceptionの根本原因を示す新しい言語機能が追加されています。この言語機能は、2006年からSAP商用JVMの一部となっています。以下は、この驚くべき言語機能を理解するために2分間読んだものです。

https://jfeatures.com/blog/NullPointerException

Java 14では、以下はNullPointerException例外メッセージのサンプルです。

スレッド「main」内java.lang.NullPointerException:「list」がnullであるため、「java.util.List.size()」を呼び出すことができません

898 BilltheLizard Oct 20 2008 at 20:20

NullPointerExceptionsは、オブジェクトを参照しているかのように、メモリ内の場所がない(null)参照を使用しようとしたときに発生する例外です。null参照でメソッドを呼び出すか、null参照のフィールドにアクセスしようとすると、がトリガーされますNullPointerException。これらは最も一般的ですが、他の方法はNullPointerExceptionjavadocページにリストされています。

おそらく、私が思いついた最も簡単なサンプルコードは次のようにNullPointerExceptionなります。

public class Example {

    public static void main(String[] args) {
        Object obj = null;
        obj.hashCode();
    }

}

最初の行の内側にはmain、私は明示的に設定していますObject参照objに等しいですnull。これは、参照があるが、オブジェクトを指していないことを意味します。その後、メソッドを呼び出して、オブジェクトを指しているかのように参照を処理しようとします。これNullPointerExceptionは、参照が指している場所で実行するコードがないために発生します。

(これは技術的なことですが、言及する必要があると思います:nullを指す参照は、無効なメモリ位置を指すCポインタと同じではありません。nullポインタは文字通りどこも指していません。これは微妙に異なります。たまたま無効な場所を指しています。)

709 fgb Jun 08 2014 at 02:22

NullPointerExceptionとは何ですか?

開始するのに適した場所はJavaDocsです。彼らはこれをカバーしています:

オブジェクトが必要な場合に、アプリケーションがnullを使用しようとしたときにスローされます。これらには以下が含まれます:

  • nullオブジェクトのインスタンスメソッドを呼び出す。
  • nullオブジェクトのフィールドへのアクセスまたは変更。
  • nullの長さを配列であるかのように取ります。
  • nullのスロットにアクセスするか、配列であるかのように変更します。
  • Throwable値であるかのようにnullをスローします。

アプリケーションは、このクラスのインスタンスをスローして、nullオブジェクトの他の不正な使用を示す必要があります。

JLSに従って、を使用してnull参照を使用しようとするとsynchronized、この例外もスローされる場合もあります。

SynchronizedStatement:
    synchronized ( Expression ) Block
  • それ以外の場合、式の値がnullの場合、aNullPointerExceptionがスローされます。

どうすれば修正できますか?

だからあなたは持っていNullPointerExceptionます。どのように修正しますか?NullPointerException:をスローする簡単な例を見てみましょう。

public class Printer {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer();
        printer.print();
    }
}

null値を特定する

最初のステップは、例外の原因となっている値を正確特定することです。このために、いくつかのデバッグを行う必要があります。スタックトレースの読み方を学ぶことが重要です。これにより、例外がスローされた場所が表示されます。

Exception in thread "main" java.lang.NullPointerException
    at Printer.printString(Printer.java:13)
    at Printer.print(Printer.java:9)
    at Printer.main(Printer.java:19)

ここでは、例外が13行目(printStringメソッド内)でスローされていることがわかります。行を見て、ロギングステートメントを追加するデバッガーを使用して、どの値がnullであるかを確認します。それsがnullであることがわかり、そのlengthメソッドを呼び出すと例外がスローされます。s.length()がメソッドから削除されると、プログラムが例外のスローを停止することがわかります。

これらの値がどこから来ているかをトレースします

次に、この値がどこから来ているかを確認します。メソッドの呼び出し元に従うことにより、我々はその参照sで渡されたprintString(name)print()する方法、およびthis.namenullです。

これらの値を設定する場所をトレースします

どこにthis.name設定されていますか?ではsetName(String)方法。さらにデバッグを行うと、このメソッドがまったく呼び出されていないことがわかります。メソッドが呼び出された場合は、これらのメソッドが呼び出される順序を確認してください。setメソッドはprintメソッドのに呼び出さません。

これは私たちに解決策を与えるのに十分です:を呼び出すprinter.setName()前にに呼び出しを追加しますprinter.print()

その他の修正

変数はデフォルト値を持つことができます(そしてsetNameそれがnullに設定されるのを防ぐことができます):

private String name = "";

printまたはprintStringメソッドのいずれかがnullチェックできます。次に例を示します。

printString((name == null) ? "" : name);

または、name 常にnull以外の値を持つようにクラスを設計することもできます

public class Printer {
    private final String name;

    public Printer(String name) {
        this.name = Objects.requireNonNull(name);
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer("123");
        printer.print();
    }
}

参照:

私はまだ問題を見つけることができません

問題をデバッグしようとしても解決策がない場合は、質問を投稿してさらにヘルプを求めることができますが、これまでに試したことを必ず含めてください。少なくとも、質問にスタックトレース含め、コードで重要な行番号マークします。また、最初にコードを単純化してみてください(SSCCEを参照)。

516 StephenC Jun 22 2014 at 09:16

質問:NullPointerException(NPE)の原因は何ですか?

あなたが知っておくべきこととして、Javaタイプが分かれているプリミティブ型booleanintなど)と参照型。Javaの参照型を使用するnullと、「オブジェクトなし」というJavaの言い方である特別な値を使用できます。

NullPointerExceptionプログラムnullが実際の参照であるかのようにを使用しようとすると、実行時にAがスローされます。たとえば、これを書く場合:

public class Test {
    public static void main(String[] args) {
        String foo = null;
        int length = foo.length();   // HERE
    }
}

「HERE」というラベルの付いたステートメントはlength()null参照に対してメソッドを実行しようとしますNullPointerException。これにより、がスローされます。

null結果がNullPointerException。になる値を使用する方法はたくさんあります。実際には、あなたがいることを唯一のものができて行うnullNPEを発生させずには、以下のとおりです。

  • それを参照変数に割り当てるか、参照変数から読み取ります。
  • それを配列要素に割り当てるか、配列要素から読み取ります(配列参照自体がnullでない場合)。
  • パラメータとして渡すか、結果として返すか、または
  • ==または!=演算子、またはを使用してテストしinstanceofます。

質問:NPEスタックトレースを読み取るにはどうすればよいですか?

上記のプログラムをコンパイルして実行するとします。

$ javac Test.java 
$ java Test
Exception in thread "main" java.lang.NullPointerException
    at Test.main(Test.java:4)
$

最初の観察:コンパイルは成功します!プログラムの問題はコンパイルエラーではありません。それは、実行時エラーが発生しました。(一部のIDEは、プログラムが常に例外をスローすることを警告する場合があります...しかし、標準javacコンパイラはスローしません。)

2番目の観察:プログラムを実行すると、2行の「gobbledy-gook」が出力されます。違う!!それはゴツゴツしたグックではありません。これはスタックトレースです...そして、時間をかけて注意深く読むと、コードのエラーを追跡するのに役立つ重要な情報を提供します。

それで、それが何を言っているか見てみましょう:

Exception in thread "main" java.lang.NullPointerException

スタックトレースの最初の行は、いくつかのことを示しています。

  • 例外がスローされたJavaスレッドの名前が表示されます。1つのスレッド(このような)を持つ単純なプログラムの場合、それは「メイン」になります。次へ移りましょう ...
  • スローされた例外のフルネームが表示されます。すなわちjava.lang.NullPointerException
  • 例外に関連するエラーメッセージがある場合、それは例外名の後に出力されます。NullPointerExceptionエラーメッセージが表示されることはめったにないため、この点では異常です。

2行目は、NPEの診断で最も重要な行です。

at Test.main(Test.java:4)

これは私たちに多くのことを教えてくれます:

  • 「atTest.main」は、私たちがクラスのmainメソッドに入っていたことを示していTestます。
  • 「Test.java:4」はクラスのソースファイル名を示し、これが発生したステートメントがファイルの4行目にあることを示しています。

上記のファイルの行数を数えると、4行目が「ここ」のコメントでラベル付けした行です。

より複雑な例では、NPEスタックトレースに多数の行があることに注意してください。ただし、2行目(最初の「at」行)は、NPEがスローされた場所を示していることを確認できます1

つまり、スタックトレースは、プログラムのどのステートメントがNPEをスローしたかを明確に示します。

1-完全に真実ではありません。ネストされた例外と呼ばれるものがあります...

質問:コード内のNPE例外の原因を追跡するにはどうすればよいですか?

これは難しい部分です。簡単な答えは、スタックトレース、ソースコード、および関連するAPIドキュメントによって提供される証拠に論理的推論を適用することです。

最初に簡単な例(上記)で説明しましょう。まず、スタックトレースがNPEが発生した場所を示している行を確認します。

int length = foo.length(); // HERE

どのようにそれはNPEを投げることができますか?

実際、唯一の方法fooがありますnull。値がの場合にのみ発生する可能性があります。次に、length()メソッドを実行してみnullます... BANG!

しかし(あなたが言うのを聞きます)NPEがlength()メソッド呼び出しの中に投げられたらどうなるでしょうか?

そうだとすれば、スタックトレースは異なって見えるでしょう。最初の「at」行は、java.lang.Stringクラスのある行で例外がスローされたTest.javaことを示し、の4行目は2番目の「at」行になります。

それで、それはnullどこから来たのですか?この場合、それは明らかであり、それを修正するために何をする必要があるかは明らかです。(null以外の値をに割り当てますfoo。)

では、もう少しトリッキーな例を試してみましょう。これには、論理的な推論が必要になります。

public class Test {

    private static String[] foo = new String[2];

    private static int test(String[] bar, int pos) {
        return bar[pos].length();
    }

    public static void main(String[] args) {
        int length = test(foo, 1);
    }
}

$ javac Test.java 
$ java Test
Exception in thread "main" java.lang.NullPointerException
    at Test.test(Test.java:6)
    at Test.main(Test.java:10)
$ 

これで、2つの「at」行ができました。最初のものはこの行のためのものです:

return args[pos].length();

2番目はこの行用です:

int length = test(foo, 1);

最初の行を見ると、どのようにしてNPEをスローできますか?2つの方法があります:

  • 値が場合barnull、その後bar[pos]NPEをスローします。
  • の値bar[pos]nullそれを呼び出しlength()ている場合、NPEがスローされます。

次に、これらのシナリオのどれが実際に何が起こっているのかを説明しているのかを理解する必要があります。最初のものを探索することから始めます:

どこbarから来たの?これはtestメソッド呼び出しのパラメーターであり、呼び出された方法を見るtestと、foo静的変数からのものであることがわかります。さらに、foonull以外の値に初期化したことがはっきりとわかります。この説明を暫定的に却下するには、これで十分です。(理論的には、他の何かが...に変わる 可能性fooがありnullますが、それはここでは発生していません。)

では、2番目のシナリオはどうですか?まあ、それはであることがわかります、それpos1それがでfoo[1]なければならないことを意味しますnull。これは可能ですか?

確かにそうです!そしてそれが問題です。このように初期化すると:

private static String[] foo = new String[2];

初期化されるString[]2つの要素nullを割り当てます。その後、foo...の内容は変更していませんので、foo[1]引き続きnull

432 RakeshBurbure Oct 20 2008 at 20:21

であるオブジェクトにアクセスしようとしているようなものですnull。以下の例を検討してください。

TypeA objA;

この時点で、このオブジェクトを宣言したばかりですが、初期化またはインスタンス化されていません。そして、その中のプロパティやメソッドにアクセスしようとすると、それはNullPointerException意味のあるものをスロー します。

以下の例も参照してください。

String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown
365 nathan1138 Apr 17 2013 at 09:57

オブジェクトが必要な場合にアプリケーションがnullを使用しようとすると、nullポインタ例外がスローされます。これらには以下が含まれます:

  1. nullオブジェクトのインスタンスメソッドを呼び出す。
  2. nullオブジェクトのフィールドへのアクセスまたは変更。
  3. の長さをnull配列のように取ります。
  4. null配列であるかのようにのスロットにアクセスまたは変更します。
  5. 投げnullそれはThrowableの値であるかのように。

アプリケーションは、このクラスのインスタンスをスローして、nullオブジェクトの他の不正な使用を示す必要があります。

参照:http//docs.oracle.com/javase/8/docs/api/java/lang/NullPointerException.html

341 MrZebra Oct 20 2008 at 20:25

nullポインタは、1ポイントはどこにあるという。ポインタを逆参照するpときpは、「「p」に格納されている場所のデータを教えてください。がnullポインタの場合、に格納されている場所pnowhereです。「どこにもない場所のデータを教えてください」と言います。明らかに、これを行うことはできないので、をスローしnull pointer exceptionます。

一般に、何かが正しく初期化されていないことが原因です。

329 L.G. Jun 25 2014 at 18:17

それがどのように発生し、どのように修正するかを説明するために、すでに多くの説明がありますが、を回避するためのベストプラクティスにも従う必要がありますNullPointerException

参照: ベストプラクティスの優れたリスト

非常に重要なことですが、final修飾子をうまく利用します。 Javaで適用可能な場合は常に「final」修飾子を使用する

概要:

  1. final修飾子を使用して、適切な初期化を強制します。
  2. 該当する場合は空のコレクションを返すなど、メソッドでnullを返さないようにします。
  3. 注釈@NotNullを使用して@Nullable
  4. 速く失敗し、アサートを使用して、nullであってはならないときにアプリケーション全体にnullオブジェクトが伝播するのを防ぎます。
  5. 最初に既知のオブジェクトとequalsを使用します。 if("knownObject".equals(unknownObject)
  6. を優先valueOf()toString()ます。
  7. nullセーフStringUtilsメソッドを使用しますStringUtils.isEmpty(null)
  8. メソッドの戻り値としてJava8オプションを使用します。オプションクラスは、null参照の代わりにオプション値を表すためのソリューションを提供します。
323 ashishbhatt Jan 28 2012 at 13:45

Javaでは、すべて(プリミティブ型を除く)がクラスの形式になっています。

オブジェクトを使用する場合は、次の2つのフェーズがあります。

  1. 宣言する
  2. 初期化

例:

  • 宣言: Object object;
  • 初期化: object = new Object();

アレイの概念についても同じです。

  • 宣言: Item item[] = new Item[5];
  • 初期化: item[0] = new Item();

初期化セクションを指定していない場合は、NullPointerException発生します。

322 javidpiprani Sep 24 2013 at 13:01

ヌルポインタ例外は、オブジェクトを初期化せずに使用していることを示します。

たとえば、以下はコードで使用する学生クラスです。

public class Student {

    private int id;

    public int getId() {
        return this.id;
    }

    public setId(int newId) {
        this.id = newId;
    }
}

以下のコードは、nullポインタ例外を示します。

public class School {

    Student student;

    public School() {
        try {
            student.getId();
        }
        catch(Exception e) {
            System.out.println("Null pointer exception");
        }
    }
}

を使用しstudentているが、以下に示す正しいコードのように初期化するのを忘れたためです。

public class School {

    Student student;

    public School() {
        try {
            student = new Student();
            student.setId(12);
            student.getId();
        }
        catch(Exception e) {
            System.out.println("Null pointer exception");
        }
    }
}
315 OscarRyz Oct 21 2008 at 03:05

Javaのあなたが宣言するすべての変数は、実際のオブジェクト(またはプリミティブ)といないオブジェクト自体への「参照」です。

1つのオブジェクトメソッドを実行しようとすると、参照は生きているオブジェクトにそのメソッドを実行するように要求します。ただし、参照がNULL(nothing、zero、void、nada)を参照している場合、メソッドが実行される方法はありません。次に、ランタイムはNullPointerExceptionをスローしてこれを通知します。

あなたの参照はnullを「指す」、つまり「Null-> Pointer」です。

オブジェクトはVMメモリスペースに存在し、オブジェクトにアクセスする唯一の方法はthis参照を使用することです。この例を見てください:

public class Some {
    private int id;
    public int getId(){
        return this.id;
    }
    public setId( int newId ) {
        this.id = newId;
    }
}

そして、あなたのコードの別の場所で:

Some reference = new Some();    // Point to a new object of type Some()
Some otherReference = null;     // Initiallly this points to NULL

reference.setId( 1 );           // Execute setId method, now private var id is 1

System.out.println( reference.getId() ); // Prints 1 to the console

otherReference = reference      // Now they both point to the only object.

reference = null;               // "reference" now point to null.

// But "otherReference" still point to the "real" object so this print 1 too...
System.out.println( otherReference.getId() );

// Guess what will happen
System.out.println( reference.getId() ); // :S Throws NullPointerException because "reference" is pointing to NULL remember...

これは知っておくべき重要なことです。オブジェクトへの参照がなくなると(上記の例ではreferenceotherReference両方がnullを指している場合)、オブジェクトは「到達不能」になります。それを操作する方法がないため、このオブジェクトはガベージコレクションの準備ができており、ある時点で、VMはこのオブジェクトによって使用されているメモリを解放し、別のメモリを割り当てます。

287 Makoto May 25 2014 at 13:11

NullPointerExceptionオブジェクト配列を宣言し、すぐにその中の要素を逆参照しようとすると、別のaが発生します。

String[] phrases = new String[10];
String keyPhrase = "Bird";
for(String phrase : phrases) {
    System.out.println(phrase.equals(keyPhrase));
}

この特定のNPEは、比較順序を逆にすると回避できます。つまり、.equals保証されたnull以外のオブジェクトで使用します。

配列内のすべての要素は、共通の初期値に初期化されます。どのタイプのオブジェクト配列でも、すべての要素がnull。であることを意味します。

要素アクセスまたは逆参照するに、配列内の要素を初期化する必要あります。

String[] phrases = new String[] {"The bird", "A bird", "My bird", "Bird"};
String keyPhrase = "Bird";
for(String phrase : phrases) {
    System.out.println(phrase.equals(keyPhrase));
}