Java 两个代码段的数组分配输出行为不同

Java 两个代码段的数组分配输出行为不同,java,arrays,variable-assignment,Java,Arrays,Variable Assignment,第一个代码段打印2 public static void main(String args[]) throws Exception { int[] a = { 1, 2, 3, 4 }; int[] b = { 2, 3, 1, 0 }; int val = (a = b)[3]; System.out.println( a [val ] ); } 第二个代码段输出1 public static void main(String args[]) throw

第一个代码段打印
2

public static void main(String args[]) throws Exception {  
    int[] a = { 1, 2, 3, 4 };
    int[] b = { 2, 3, 1, 0 };
    int val = (a = b)[3];
    System.out.println( a [val  ] );
}
第二个代码段输出
1

public static void main(String args[]) throws Exception {
    int[] a = { 1, 2, 3, 4 };
    int[] b = { 2, 3, 1, 0 };
    int val;
    System.out.println(a[val = ((a = b)[3])]);
}
发生了什么事


在第一个代码片段中,有趣的部分是

int val = (a = b)[3]
这里有两个作业。第一个(
a=b
)将首先发生,并让变量
a
也引用
b
引用的数组(请注意,旧数组不再被引用,因此有资格被垃圾收集)

之后,您需要索引3上的元素。这就是值“0”。然后,输出请求该数组中索引0上的元素。如你所见,这是值“2”。请记住,
a
现在指的是与
b
相同的数组

在第二个代码段中,您将在同一行中执行所有操作:

System.out.println(a[val = ((a = b)[3])]);
虽然赋值和索引看起来相同,但这里的主要区别在于,在重新赋值变量之前,您正在访问数组(由
a
引用)


因此,在完成所有赋值之后,变量
val
具有相同的值“0”,并且变量
a
引用与
b
相同的数组。但是现在将查找旧数组中的第一个元素(索引0)。这就是“1”。

输出是合理的。让我们把这两行复杂的语句展开成几个单独的语句。第一段:

// This...
int val = (a = b)[3];
// Becomes...
a = b;
int val = a[3]; // 0
所以打印
a[val]
将打印
a[0]
,现在是2

第二个片段更复杂。棘手的一点是,“我们正在索引的数组”在其他副作用之前进行评估。因此:

// This...
System.out.println(a[val = ((a = b)[3])]);
// Becomes...
int[] tmp = a; // So tmp is { 1, 2, 3, 4 }
a = b;
val = a[3]; // 0
System.out.println(tmp[val]); // 1
更详细地介绍了这一点。这里的重要部分是:

在运行时,数组访问表达式的求值行为如下:

  • 首先,对数组引用表达式求值。如果此评估突然完成[…]评估
  • 否则,将计算索引表达式。如果此评估突然完成[…]
  • 否则,如果数组引用表达式的值为null[…]
  • 否则,数组引用表达式的值实际上引用数组。如果索引表达式的值小于零[…]
  • 否则,数组访问的结果是数组中由索引表达式的值选择的T类型变量

有两组经验:

A:

1. int val = (a = b)[3];
2. a [val]
B

1. a[val = ((a = b)[3])]
执行A.1后:
A={2,3,1,0}
val=0
so
A[val]=2

执行B.1时:
a={1,2,3,4}
val=0
so
a[val]=1


B
(a=B)
是对
{2,3,1,0}
的引用,因此
(a=B)[3]=0
val=0
a
是对
{1,2,3,4}
的引用(因为赋值尚未完成)所以
a[0]=1
是Java语言规范的相关部分(15.13.1.阵列访问的运行时评估)状态:

使用以下过程计算数组访问表达式: 首先,对数组引用表达式求值。如果此求值突然完成,则数组访问完成 出于同样的原因突然出现,而索引表达式不是 评价的。 否则,将对索引表达式求值。如果此求值突然完成,则阵列访问也会因为相同的原因突然完成

第一个片段是简单的部分:在语句
intval=(a=b)[3];
之后,数组变量
a
指向数组
{2,3,1,0}
在索引0处取元素,得到答案2


在第二个代码段中,
a
在索引表达式之前求值,因此
a
指向数组
{1,2,3,4}
。这次取索引0处的元素得到答案1。

因此在第一个示例中,b被分配给a,val等于b的第四个值,0,然后我们用索引0,2打印值。您得到了这个结果

在第二个示例中,由于所有操作都是一次性完成的,因此在访问索引0时a仍保留其初始值,因此打印1。第二个代码段相当于:

int val = b[3];
System.out.println(a[val]);
a =b;
第一:

第二:

int[] a = { 1, 2, 3, 4 };
int[] b = { 2, 3, 1, 0 };
int val;
System.out.println(a[val = ((a = b)[3])]);
/**
* a[val = ((a = b)[3])]    
* => a[val=(b[3])]   val is not used here
* => a[b[3]] => a[0] => 1
**/

哇!多么奇怪的语法。你想做什么?@BoratSagdiyev你正在使用哪个java版本?我附上了java截图。@BoratSagdiyev的可能副本不是你答案的第一行,比如“这不是答案…”@亨利:我看到您将此标记为重复。但是,我不太明白Java的求值顺序规则是如何解释此特定示例的。您介意在回答中详细说明吗?谢谢。有人提到JRE 8中的输出是不同的。我自己还没有验证过这一点,但有任何理由认为这会表现为dif吗不同的Java版本(或实现)?@NPE我用Java 8测试了它。没有区别。我怀疑有任何区别。@NPE:Nope-在JRE 8下它给了我1。我怀疑报告值为2的人有一个诊断错误。@NPE可以为一个供应商的不同编译器版本的JLS规范提供不同的输出?@NPE:Borat删除的答案是否有依据,问题是他运行了代码段1,然后运行了代码段2…到那时,
a
已经引用了一个不同的数组。这是明显的误导。有人提到JRE 8中的输出是不同的。我自己还没有验证过这一点,但有没有理由认为这在Java版本(或实现)中表现不同“因为赋值尚未完成”的含义还不清楚。当实际访问数组时,
a=b
的赋值已经完成……但棘手的是,在评估时没有发生
int[] a = { 1, 2, 3, 4 };
int[] b = { 2, 3, 1, 0 };
int val;
System.out.println(a[val = ((a = b)[3])]);
/**
* a[val = ((a = b)[3])]    
* => a[val=(b[3])]   val is not used here
* => a[b[3]] => a[0] => 1
**/