为什么这不会抛出NullPointerException?

com*_*mit 89 java reference nullpointerexception

以下代码的澄清说明:

StringBuilder sample = new StringBuilder();
StringBuilder referToSample = sample;
referToSample.append("B");
System.out.println(sample);
Run Code Online (Sandbox Code Playgroud)

这将打印,B以便证明samplereferToSample对象引用相同的内存引用.

StringBuilder sample = new StringBuilder();
StringBuilder referToSample = sample;
sample.append("A");
referToSample.append("B");
System.out.println(referToSample);
Run Code Online (Sandbox Code Playgroud)

这将打印AB,也证明是相同的.

StringBuilder sample = new StringBuilder();
StringBuilder referToSample = sample;
referToSample = null;
referToSample.append("A");
System.out.println(sample);
Run Code Online (Sandbox Code Playgroud)

显然这将抛出NullPointerException因为我试图调用appendnull引用.

StringBuilder sample = new StringBuilder();
StringBuilder referToSample = sample;
referToSample = null;
sample.append("A");
System.out.println(sample);
Run Code Online (Sandbox Code Playgroud)

所以这是我的问题,为什么最后一个代码示例没有抛出,NullPointerException因为我从前两个示例中看到并理解的是,如果两个对象引用同一个对象,那么如果我们更改任何值,那么它也会反映到其他,因为它们都指向相同的内存参考.那么为什么这条规则不适用于此?如果我分配null给referToSample,那么sample也应该为null,它应该抛出一个NullPointerException,但它不会抛出一个,为什么?

ζ--*_*ζ-- 89

null赋值不会通过全局销毁该对象来更改.这种行为会导致难以追踪的错误和违反直觉的行为.他们只打破那个特定的参考.

为简单起见,我们假设sample指向地址12345.这可能不是地址,仅用于简化此处.地址通常用给定的奇怪十六进制表示Object#hashCode(),但这是依赖于实现的.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);
Run Code Online (Sandbox Code Playgroud)

从标See diagram有当时物体图的线条如下:

图1:

[StringBuilder sample]    -----------------> [java.lang.StringBuilder@00012345]
                                                      ?
[StringBuilder referToSample] ------------------------/
Run Code Online (Sandbox Code Playgroud)

图2:

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

[StringBuilder referToSample] ---->> [null pointer]
Run Code Online (Sandbox Code Playgroud)

图2显示,取消referToSample不会破坏sampleStringBuilder 的引用00012345.

1 GC的考虑因素使这一点难以置信.

  • `hashCode()`不是**与内存地址相同的东西 (11认同)
  • 标识hashCode通常在第一次调用方法时从Object的内存位置派生.从那时起,即使内存管理器决定将对象移动到不同的内存位置,hashCode也会被修复并记住. (6认同)
  • 覆盖`hashCode`的类通常将其定义为返回与内存地址无关的值.我想你可以在不提及`hashCode`的情况下提出关于对象引用与对象的观点.如何获取内存地址的细节可以留一天. (3认同)

Par*_*rth 62

最初,它是如你所说referToSample指的是sample如下图所示:

1.场景1:

referToSample是指样本

2.情景1(续):

referToSample.append(

  • 在这里referToSample指的是sample,所以在你写的时候附加了"B"

    referToSample.append("B")

Scenario2中发生了同样的事情:

但是,在 3.场景3中:正如hexafraction所说,

当你将nullreferToSample当它是指sample它并没有改变价值,而不是它只是打破了参考sample,而现在它指向无处.如下所示:

当referToSample = null时

现在,因为没有referToSample点,所以当你没有任何值或参考它可以追加A.所以,它会抛出.referToSample.append("A");NullPointerException

sample仍然与你初始化它一样

StringBuilder sample = new StringBuilder(); 所以它已被初始化,所以现在它可以附加A,而不会抛出 NullPointerException

  • 不错的图表.有助于说明它. (5认同)

Hov*_*els 17

简而言之:您将null赋给引用变量,而不是对象.

在一个示例中,您可以更改由两个引用变量引用的对象的状态.发生这种情况时,两个参考变量都将反映变化.

在另一个示例中,您更改分配给一个变量的引用,但这对对象本身没有影响,因此仍然引用原始对象的第二个变量将不会注意到对象状态的任何更改.


至于你的具体"规则":

如果两个对象引用同一个对象,那么如果我们更改任何值,那么它也会反映到其他对象,因为它们都指向相同的内存引用.

同样,您指的是更改两个变量引用的一个对象的状态.

那么为什么这条规则不适用于此?如果我为referToSample分配null,那么sample也应该为null并且它应该抛出nullPointerException但是它没有抛出,为什么?

同样,你改变参考具有绝对的一个变量没有影响参考其他变量.

这是两个完全不同的动作,将导致两个完全不同的结果.

  • @commit:这是一个基本但关键的概念,是所有Java的基础,一旦你看到,你永远不会忘记. (3认同)

Doo*_*nob 8

看到这个简单的图表:

图

当你调用一个方法referToSample,然后[your object]进行更新,所以它影响sample了.但是,当你说referToSample = null,那么你只需更改什么referToSample 是指到.

  • FTW油漆图! (3认同)