澄清StringBuilder参考和方法执行顺序

Lui*_*ese 6 java stringbuilder reference

这段代码

    StringBuilder b1=new StringBuilder("hello");
    b1.append(b1.append("!"));
    System.out.println("b1 = "+b1);
Run Code Online (Sandbox Code Playgroud)

将打印

    b1 = hello!hello!
Run Code Online (Sandbox Code Playgroud)

因为内部append是先执行并修改对象b1; 然后b1评估外部(它等于hello!现在)并且附加相同的字符串.所以

  1. 内部表达式被执行
  2. 原始对象被修改
  3. 外部表达式在修改后的对象上执行

但现在,为什么这个代码会抛出一个NullPointerException

    StringBuilder s1=null;
    StringBuilder s2=new StringBuilder("world");
    try{s1.append(s1=s2.append("!"));}
    catch(Exception e){System.out.println(e);}
    System.out.println("s1 = "+s1+"\ns2 = "+s2+"\n");
Run Code Online (Sandbox Code Playgroud)

和打印

    java.lang.NullPointerException
    s1 = world!
    s2 = world!
Run Code Online (Sandbox Code Playgroud)

我期待引用s1指向外部执行s2 之前引用的对象append.

在某种程度上,分配b1.append("!");会影响"外部" b1,但s1=s2.append("!")不会.我知道这是因为在第一种情况下我正在修改对象,而在第二种情况下我正在修改引用,但是......评估和执行值/引用/方法的顺序是什么?

编辑

数组也是如此:

    int[] y = { 0, 0, 0 };
    try {y[y[0] = 2] = 4;} 
    catch (Exception e) {System.out.println(e);}
    System.out.println("y = "+Arrays.toString(y)+"\n");
Run Code Online (Sandbox Code Playgroud)

版画

    y = [2, 0, 4]
Run Code Online (Sandbox Code Playgroud)

    int[] x1 = null;
    int[] x2 = { 1, 2, 3 };
    try {x1[(x1=x2)[0]] = 0;} 
    catch (Exception e) {System.out.println(e);}
    System.out.println("x1 = "+Arrays.toString(x1)+"\nx2 = "+Arrays.toString(x2));
Run Code Online (Sandbox Code Playgroud)

版画

    java.lang.NullPointerException
    x1 = [1, 2, 3]
    x2 = [1, 2, 3]
Run Code Online (Sandbox Code Playgroud)

Ale*_* C. 2

JLS 15.12.4 中对此进行了规定:

\n\n
\n

如果 form 是 ExpressionName 。[TypeArguments] 标识符,然后:

\n\n
    \n
  • 如果调用模式是静态的,则没有目标引用。对 ExpressionName 进行求值,但结果随后被丢弃。

  • \n
  • 否则,目标引用是由 ExpressionName 表示的值。

  • \n
\n
\n\n

\n\n
\n

作为实例方法调用 (\xc2\xa715.12) 的一部分,有一个\n 表达式表示要调用的对象。在计算方法调用的任何参数表达式的任何部分之前,该表达式似乎已被完全计算。

\n
\n\n

因此,在行s1.append(s1=s2.append("!")); s1(before .append(s1 = ...)) 中,首先在参数表达式之前求值s1=s2.append("!")。因此null引用被记住为之前的目标引用s1更改为引用 StringBuilders2实例。

\n\n

然后对参数表达式进行求值并s1=s2.append("!")执行。但它之前记住了目标引用,因此在引用append上调用该方法null,调用结果抛出一个NullPointerException.

\n