为什么不抛出 NullPointerException?

需要对以下代码进行澄清:

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,因为我试图在空引用上调用 append

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

所以我的问题是,为什么最后一个代码样本没有抛出 NullPointerException,因为我从前两个例子中看到和理解的是,如果两个对象引用同一个对象,那么如果我们改变任何值,那么它也会反射到其他对象,因为两个对象都指向同一个内存引用。那么为什么这条规则不适用于这里呢?如果我将 null赋值给 refToSample,那么 sample 也应该为 null,它应该抛出一个 NullPointerException,但是它没有抛出一个,为什么?

4380 次浏览

In a nutshell: You assign null to a reference variable, not to an object.

In one example you change the state of an object that is referred to by two reference variables. When this occurs, both reference variables will reflect the change.

In another example, you change the reference assigned to one variable, but this has no effect on the object itself, and so the second variable, which still refers to the original object, will not notice any change in object state.


So as to your specific "rules":

if two objects referring to same object then if we change any value then it will also reflect to other because both are pointing to same memory reference.

Again, you refer to changing the state of the one object that both variables refer to.

So why is that rule not applying here? If I assign null to referToSample then sample should also be null and it should throw nullPointerException but it is not throwing, why?

Again, you change the reference of one variable which has absolutely no effect on the reference of the other variable.

These are two completely different actions and will result in two completely different results.

null assignments do not change value by globally destroying that object. That kind of behavior would lead to hard-to-track bugs and counterintuitive behavior. They only break that specific reference.

For simplicity, let's say that sample points to address 12345. This is probably not the address, and is only used to make things simple here. The address is typically represented with the weird hexadecimal given in Object#hashCode(), but this is implementation-dependent.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);

From the lines marked See diagram the diagrams of the objects at that time are as follows:

Diagram 1:

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

Diagram 2:

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


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

Diagram 2 shows that annulling referToSample does not break the reference of sample to the StringBuilder at 00012345.

1GC considerations make this implausible.

Here 'sample' and 'referToSample' are referencing the same object.That is the concept of different pointer accessing same memory location. So assigning one reference variable to null does not destroy the object.

   referToSample = null;

means 'referToSample' only pointing to null , object remains same and other reference variable are working fine. So for 'sample' which does not pointing to null and have a valid object

   sample.append("A");

works fine. But if we try to append null to 'referToSample', it will show NullPointException. That is,

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

That is why you got NullPointerException in your third code snippet.

Initially it was as you said referToSample was referring to sample as shown below:

1. Scenario1:

referToSample refers to sample

2. Scenario1 (cont.):

referToSample.append("B")

  • Here as referToSample was referring to sample, so it appended "B" while you write

    referToSample.append("B")

Same thing happend in Scenario2 :

But, in 3. Scenario3 : as hexafraction said,

when you assign null to referToSample when it was referring sample it did not change value instead it just breaks the reference from sample, and now it points nowhere. as shown below:

when referToSample = null

Now, as referToSample points nowhere, so while you referToSample.append("A"); it would not be have any values or reference where it can append A. So, it would throw NullPointerException.

BUT sample is still the same as you had initialized it with

StringBuilder sample = new StringBuilder(); so it has been initalized, so now it can append A, and will not throw NullPointerException

See this simple diagram:

diagram

When you call a method on referToSample, then [your object] is updated, so it affects sample too. But when you say referToSample = null, then you're simply changing what referToSample refers to.

Whenever a new keyword is used it Creates a Object at the Heap

1)StringBuilder sample = new StringBuilder();

2)StringBuilder referToSample = sample;

In 2) the Reference of referSample is created on same object sample

thus referToSample = null; is Nulling Only the referSample Reference giving no effect to sample that's why you are not getting NULL Pointer Exception Thanks to Java's Garbage Collection

Just easy, Java don't have pass by reference, It just pass object reference.