209

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

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

4

12 に答える 12

4074

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

int x;
x = 10;

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

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

Integer num;
num = new Integer(10);

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

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

( NullPointerExceptionNPE) は、変数を宣言したが、変数の内容を使用しようとする前にオブジェクトを作成して変数に代入しなかった場合に発生します (逆参照と呼ばれます)。つまり、実際には存在しないものを指しています。

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

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

たとえば、次のようなメソッドがあるとします。

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

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

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 の一部になっています。

Java 14 の NullPointerException 例外メッセージの例を次に示します。

スレッド「メイン」で java.lang.NullPointerException: 「リスト」が null であるため、「java.util.List.size()」を呼び出せません

于 2008-10-20T13:54:24.253 に答える
932

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 ポインターは文字通りどこも指していませんたまたま無効な場所を指している)。

于 2008-10-20T13:20:44.260 に答える
729

NullPointerException とは何ですか?

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

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

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

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

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

SynchronizedStatement:
    synchronized ( Expression ) Block
  • それ以外の場合、Expression の値が 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 値を特定する

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

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 であるかを確認します。が null であることがわかりs、そのメソッドを呼び出すとlength例外がスローされます。s.length()をメソッドから削除すると、プログラムが例外のスローを停止することがわかります。

これらの値がどこから来たのかを追跡します

次に、この値がどこから来たのかを確認します。メソッドの呼び出し元をたどると、 がメソッドs内で渡され、nullであることがわかります。printString(name)print()this.name

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

どこに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を参照)。

于 2014-06-07T19:22:14.450 に答える
537

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

ご存じのとおり、Java 型はプリミティブ型( booleanintなど) と参照型に分けられます。nullJava の参照型を使用すると、「オブジェクトがない」という Java の方法である特別な値を使用できます。

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

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

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

nullになる値を使用する方法はたくさんありますNullPointerException。実際、NPE を引き起こさずにで実行できる唯一のことは次のとおりです。null

  • 参照変数に代入するか、参照変数から読み取ります。
  • 配列要素に割り当てるか、配列要素から読み取ります (配列参照自体が null でない場合)。
  • パラメータとして渡すか、結果として返すか、または
  • ==or!=演算子またはを使用してテストし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 つのスレッドを持つ単純なプログラムの場合、それは "main" になります。次へ移りましょう ...
  • スローされた例外の完全な名前が表示されます。すなわちjava.lang.NullPointerException
  • 例外にエラー メッセージが関連付けられている場合は、例外名の後に出力されます。NullPointerExceptionエラーメッセージが表示されることはめったにないため、この点で異常です。

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

at Test.main(Test.java:4)

これは多くのことを教えてくれます:

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

上記のファイルの行を数えると、4 行目は "HERE" コメントでラベルを付けた行です。

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

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

関連項目:スタック トレースとは何ですか? また、スタック トレースを使用してアプリケーション エラーをデバッグするにはどうすればよいですか?

1 - あまり当てはまりません。ネストされた例外と呼ばれるものがあります...

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

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

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

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

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

foo実際、方法は 1 つしかありません:値が の場合にのみ発生しますnull。次に、length()メソッドを実行しようとしnullます... BANG!

しかし、(あなたが言うのを聞いたことがあります)length()メソッド呼び出し内で NPE がスローされた場合はどうなるでしょうか?

それが起こった場合、スタック トレースは異なるように見えます。最初の "at" 行は、例外がjava.lang.Stringクラスのある行でスローされたことを示し、4 行Test.java目は 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 つの方法があります。

  • の値が の場合barnullNPEbar[pos]がスローされます。
  • の値が でbar[pos]ある場合、nullそれを呼び出すとlength()NPE がスローされます。

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

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

では、2 番目のシナリオはどうでしょうか。posこれはであることがわかります。1つまり、 であるfoo[1]必要がありますnull。これは可能ですか?

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

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

に初期化されたString[]2 つの要素でa を割り当てます。その後、~の内容は変更していませんので、そのままです。nullfoofoo[1]null

Androidではどうですか?

Android では、NPE の直接の原因を突き止めるのは少し簡単です。例外メッセージは通常、使用している null 参照の (コンパイル時の) タイプと、NPE がスローされたときに呼び出そうとしたメソッドを示しますこれにより、直接の原因を特定するプロセスが簡素化されます。

しかし一方で、Android にはプラットフォーム固有の一般的な NPE の原因がいくつかあります。非常に一般的なのは、getViewById予期せずnull. null私のアドバイスは、予期しない戻り値の原因に関する Q&A を検索することです。

于 2014-06-22T02:16:56.530 に答える
444

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

TypeA objA;

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

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

String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown
于 2008-10-20T13:21:21.590 に答える
374

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

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

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

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

于 2013-04-17T02:57:16.383 に答える
351

ポインターとは、nullどこにも指していないポインターです。pointer を逆参照するときは、p「"p" に格納されている場所のデータをくれ」と言います。明らかに、これはできないので、.pnullpnowherenull pointer exception

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

于 2008-10-20T13:25:08.277 に答える
333

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

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

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");
        }
    }
}
于 2013-09-24T06:01:46.620 に答える
333

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

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

  1. 宣言する
  2. 初期化

例:

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

配列の概念についても同じです。

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

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

于 2012-01-28T06:45:54.253 に答える
324

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 はこのオブジェクトが使用していたメモリを解放し、別のメモリを割り当てます。

于 2008-10-20T20:05:30.790 に答える
297

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));
}
于 2014-05-25T06:11:58.740 に答える