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

2008年10月20日に質問されました。  ·  閲覧回数 3M回  ·  ソース

Ziggy picture
2008年10月20日

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

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

回答

Vincent Ramdhanie picture
2008年10月20日
3849

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

int x;
x = 10;

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

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

Integer num;
num = new Integer(10);

最初の行はnumという名前の変数を宣言していますが、実際にはまだプリミティブ値が含まれていません。 代わりに、ポインターが含まれています(タイプはIntegerタイプである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();
}

この場合、オブジェクト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の一部となっています。以下は、この驚くべき言語機能を理解するために2分間読んだものです。

https://jfeatures.com/blog/NullPointerException

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

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

Bill the Lizard picture
2008年10月20日
898

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

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

public class Example {

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

}

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

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

fgb picture
2014年06月08日
709

NullPointerExceptionとは何ですか?

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

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

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

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

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

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

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

つまり、 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()がメソッドから削除されると、プログラムが例外のスローを停止することがわかります。

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

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

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

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

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

その他の修正

変数はデフォルト値を持つことができ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を参照)。

Stephen C picture
2014年06月22日
516

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

ご存知のとおり、Java型はプリミティブ型booleanintなど)と参照型に分けられnullを使用できます。

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

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

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

null値を使用して、 NullPointerExceptionなる方法はたくさんあります。 実際、NPEを発生させずにnullで実行できるのは次のとおりです。

  • それを参照変数に割り当てるか、参照変数から読み取ります。
  • それを配列要素に割り当てるか、配列要素から読み取ります(ただし、配列参照自体が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」は、 Testクラスのmainメソッドにいることを示しています。
  • 「Test.java:4」はクラスのソースファイル名を示し、これが発生したステートメントがファイルの4行目にあることを示しています。

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

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

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

1-完全に真実ではありません。

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

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

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

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

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

実際、1つの方法しかありません。それは、 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つの方法があります:

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

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

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

では、2番目のシナリオはどうですか? さて、 pos1であることがわかります。つまり、 foo[1]nullなければなりません。 これは可能ですか?

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

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

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

Jorge Ferreira picture
2008年10月20日
432

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

TypeA objA;

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

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

String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown
nathan1138 picture
2013年04月17日
365

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

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

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

参照: http

MrZebra picture
2008年10月20日
341

nullポインターは、どこも指さないポインターです。 あなたはポインタ間接参照するとp 、あなたは、p "「に保存されている場所で私にデータを与える"と言う。場合はpあるnullポインタに格納された位置pnowhere 、「場所 'nowhere'のデータを教えてください」と言っています。明らかにこれはできないので、 null pointer exceptionスローします。

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

L. G. picture
2014年06月25日
329

それがどのように発生し、どのように修正するかを説明するために、すでに多くの説明がありますが、 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参照の代わりにオプション値を表すためのソリューションを提供します。
ashish bhatt picture
2012年01月28日
323

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

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

  1. 宣言する
  2. 初期化

例:

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

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

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

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

javid piprani picture
2013年09月24日
322

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

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

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");
        }
    }
}