なぜこれがNullPointerExceptionをスローしないのですか?


89

次のコードの説明:

StringBuilder sample = new StringBuilder();
StringBuilder referToSample = sample;
referToSample.append("B");
System.out.println(sample);

これはB、証明samplereferToSampleオブジェクトが同じメモリ参照を参照するように印刷されます。

StringBuilder sample = new StringBuilder();
StringBuilder referToSample = sample;
sample.append("A");
referToSample.append("B");
System.out.println(referToSample);

これはAB同じことを証明するものを印刷します。

StringBuilder sample = new StringBuilder();
StringBuilder referToSample = sample;
referToSample = null;
referToSample.append("A");
System.out.println(sample);

NullPointerException私がappendnull参照を呼び出そうとしているので、これは明らかにスローされます。

StringBuilder sample = new StringBuilder();
StringBuilder referToSample = sample;
referToSample = null;
sample.append("A");
System.out.println(sample);

だからここに私の質問です、NullPointerException最初の2つの例から見て理解できるのは、2つのオブジェクトが同じオブジェクトを参照していて、値を変更すると、両方が指しているため、他にも反映されるため、最後のコードサンプルがスローされないのはなぜですか?同じメモリ参照。では、なぜそのルールがここに適用されないのですか?nullreferToSample に割り当てると、サンプルもnullになり、NullPointerExceptionがスローされますが、スローされないのはなぜですか?


31
sampleまだsampleです。あなただけが変わったreferToSample
デイブニュートン

25
賛成/スター付き!非常に基本的な質問が、これはあなたの問題を説明するの美しい例であるよく質問を。
Ryan Ransford 2013

15
あなたの質問での用語のワンポイント:あなたが参照しておくsamplereferToSampleとしてオブジェクト彼らは変数ですが、彼らはオブジェクトではありません。変数はオブジェクトへの参照を保持できますが、それ自体はオブジェクトではありません。それは微妙な違いですが、基本的にはあなたの混乱の本質です。
Daniel Pryden 2013

1
オブジェクト変数を単なるポインタと考えると役立ちます。(変数に作用する任意のオペレーターvolatilefinal===オブジェクト変数に適用される場合...)は影響ポインタではなく、それが参照するオブジェクトを。
MikeFHay 2013

2
@Arpit私は敬意を払いません。この質問には大きな概念が隠されています。つまり、オブジェクトとそのオブジェクトへの参照の違いです。ほとんどの場合、この違いに気づく必要はありません(必要もありません)。言語デザイナーは、この違いを隠すために一生懸命取り組んでいます。たとえば、C ++の参照渡し引数を考えてみてください。だから、初心者がそのすべての魔法に戸惑うのを見るのは当然のことです!
Gyom 2013

回答:


89

null割り当ては、そのオブジェクトをグローバルに破棄してもを変更しません。そのような動作は、追跡が困難なバグと直感に反する動作につながります。それらはその特定の参照を壊すだけです。

簡単にするために、sampleアドレス12345 を指しているとしましょう。これはおそらくアドレスではなく、ここでは物事を簡単にするためにのみ使用されています。アドレスは通常Object#hashCode()、で指定された奇妙な16進数で表されますが、これは実装に依存します。1

StringBuilder sample = new StringBuilder(); //sample refers to 
//StringBuilder at 12345 

StringBuilder referToSample = sample; //referToSample refers to 
//the same StringBuilder at 12345 
//SEE DIAGRAM 1

referToSample = null; //referToSample NOW refers to 00000, 
//so accessing it will throw a NPE. 
//The other reference is not affected.
//SEE DIAGRAM 2

sample.append("A"); //sample STILL refers to the same StringBuilder at 12345 
System.out.println(sample);

マークされた線から、See diagramそのときのオブジェクトの図は次のとおりです。

図1:

[StringBuilder sample]    -----------------> [java.lang.StringBuilder@00012345]
                                                      
[StringBuilder referToSample] ------------------------/

図2:

[StringBuilder sample]    -----------------> [java.lang.StringBuilder@00012345]

[StringBuilder referToSample] ---->> [null pointer]

図2は、無効にreferToSamplesampleてもStringBuilderへのの参照が壊れないことを示してい00012345ます。

1 GCの考慮事項により、これは妥当ではありません。


@commit、これがあなたの質問に答える場合は、上のテキストの左側にある投票矢印の下のチェックマークをクリックしてください。
レイブリトン2013

@RayBritton私は人々が最も投票された答えが必然的に質問に答えるものであると仮定する方法が好きです。その数は重要ですが、それだけが指標ではありません。-1と-2の回答は、OPを最も支援したという理由だけで受け入れられます。
nanofarad 2013

11
hashCode()はメモリアドレスと同じではありません
Kevin Panko

6
IDのhashCodeは、通常、メソッドが初めて呼び出されたときにオブジェクトのメモリ位置から取得されます。それ以降は、メモリマネージャーがオブジェクトを別のメモリ位置に移動することを決定した場合でも、hashCodeは修正されて記憶されます。
Holger

3
オーバーライドhashCodeするクラスは通常、メモリアドレスとは関係のない値を返すように定義します。言及せずに、オブジェクト参照とオブジェクトの比較について説明できると思います hashCode。メモリアドレスを取得する方法の細かい点は、別の日に残すことができます。
Kevin Panko 2013

62

最初は、以下に示すようreferToSampleに言及しsampleていると言ったとおりです。

1.シナリオ1:

referToSampleはサンプルを参照します

2.シナリオ1(続き):

referToSample.append( "B")

  • ここでreferToSampleはを参照していたためsample、「B」を追加して

    referToSample.append("B")

シナリオ2でも同じことが起こりました

しかし、 3。シナリオ3:六分数が言ったように、

参照しているときに割り当てnullreferToSample場合、sampleは変更されませんでしたが、参照が壊れるだけで、現在はどこも指していません。以下に示すように:sample

referToSample = nullの場合

さて、どこにもreferToSampleポイントがないので、Aを追加できる値や参照はありませんが、スローされます。referToSample.append("A");NullPointerException

しかしsample、それを初期化したときと同じです

StringBuilder sample = new StringBuilder(); 初期化されたので、Aを追加でき、スローされません NullPointerException


5
素敵な図。それを説明するのに役立ちます。
nanofarad 2013

いい図ですが、下部のメモは「今、referToSampleは ""を参照していません」のようになっているはずreferToSample = sample;です。サンプルを参照しているときは、参照されているもののアドレスをコピーしているだけだからです
Khaled.K

17

簡単に言えば、オブジェクトではなく参照変数にnullを割り当てます。

1つの例では、2つの参照変数によって参照されるオブジェクトの状態を変更します。これが発生すると、両方の参照変数に変更が反映されます。

別の例では、1つの変数に割り当てられた参照を変更しますが、これはオブジェクト自体には影響しないため、元のオブジェクトを参照している2番目の変数はオブジェクトの状態の変化に気づきません。


特定の「ルール」について:

2つのオブジェクトが同じオブジェクトを参照している場合、値を変更すると、両方が同じメモリ参照を指しているため、他のオブジェクトにも反映されます。

ここでも、両方の変数が参照する1つのオブジェクトの状態を変更することを指します。

では、なぜこのルールがここに適用されないのですか?referToSampleにnullを割り当てると、サンプルもnullになり、nullPointerExceptionがスローされますが、スローされません。なぜですか?

この場合も、1つの変数の参照を変更すると、他の変数の参照にまったく影響がありません

これらは2つのまったく異なるアクションであり、2つのまったく異なる結果になります。


3
@commit:すべてのJavaの基礎となる基本的かつ重要な概念であり、一度見たら忘れることはありません。
ホバークラフトいっぱいのうなぎ

8

次の簡単な図を参照してください。

図

お問い合わせの際の方法をreferToSample、そして[your object]それが影響するので、更新されsampleすぎ。しかし、と言うときはreferToSample = null、単に参照するものreferToSample 変更しているだけです。


3

ここで、「sample」と「referToSample」は同じオブジェクトを参照しています。これは、同じメモリロケーションにアクセスする異なるポインタの概念です。したがって、1つの参照変数をnullに割り当てても、オブジェクトは破棄されません。

   referToSample = null;

nullのみを指す「referToSample」を意味します。オブジェクトは同じままで、他の参照変数は正常に機能しています。したがって、nullを指さず、有効なオブジェクトを持つ「サンプル」の場合

   sample.append("A");

正常に動作します。しかし、「referToSample」にnullを追加しようとすると、NullPointExceptionが表示されます。あれは、

   referToSample .append("A");-------> NullPointerException

そのため、3番目のコードスニペットでNullPointerExceptionが発生しました。


0

新しいキーワードが使用されるたびに、ヒープでオブジェクトが作成されます

1)StringBuilderサンプル= new StringBuilder();

2)StringBuilder referToSample = sample;

2)で、referSampleの参照が同じオブジェクトサンプルに作成されます

したがって、referToSample = null; nullであるreferSampleリファレンスのみがサンプルに影響を与えないため、JavaのガベージコレクションによりNULLポインタ例外が発生しない


0

簡単に言うと、Javaには参照渡しがなく、オブジェクト参照を渡すだけです。


2
説明されているように、パラメータを渡すセマンティクスは実際には状況とは何の関係もありません。
マイケルマイヤーズ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.