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
soA[val]=2
执行B.1时:a={1,2,3,4}
和val=0
soa[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
**/