这有点奇怪,但代码说的多于单词,所以看看测试看看我在做什么。在我当前的设置(Windows 64位上的Java 7更新21)中,此测试因ArrayIndexOutOfBoundsException而失败,但是将测试方法代码替换为注释代码,它可以正常工作。我想知道Java规范中是否有任何部分可以解释原因。
在我看来,正如“michael nesterenko”建议的那样,在调用方法之前,数组字段的值被缓存在堆栈中,并且在从调用返回时不会更新。我不知道它是JVM错误还是记录在案的“优化”。没有涉及多线程或“魔术”。
public class TestAIOOB {
private String[] array = new String[0];
private int grow(final String txt) {
final int index = array.length;
array = Arrays.copyOf(array, index + 1);
array[index] = txt;
return index;
}
@Test
public void testGrow() {
//final int index = grow("test");
//System.out.println(array[index]);
System.out.println(array[grow("test")]);
}
}
答案 0 :(得分:6)
Java Language Specification明确定义:评估x[y]
,首先评估x
,然后评估y
。在您的情况下,x
评估为String[]
,零元素。然后,y
修改成员变量,并计算为0
。尝试访问已返回的数组的第0个元素失败。成员array
更改的事实与数组查找无关,因为我们正在查看在评估它时String[]
引用的array
。
答案 1 :(得分:2)
此行为是由JLS强制执行的。 Per 15.13.1,“使用以下过程计算数组访问表达式:首先,计算数组引用表达式。如果此评估突然完成,则数组访问突然完成,原因相同且索引表达式不是否则,将评估索引表达式。[...]“。
答案 2 :(得分:0)
使用javap -c TestAIOOB
未注释的代码:
public void testGrow();
Code:
0: getstatic #6; //Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: getfield #3; //Field array:[Ljava/lang/String;
7: aload_0
8: ldc #7; //String test
10: invokespecial #8; //Method grow:(Ljava/lang/String;)I
13: aaload
14: invokevirtual #9; //Method java/io/PrintStream.println:(Ljava/lang/St
ing;)V
17: return
评论代码:
public void testGrow();
Code:
0: aload_0
1: ldc #6; //String test
3: invokespecial #7; //Method grow:(Ljava/lang/String;)I
6: istore_1
7: getstatic #8; //Field java/lang/System.out:Ljava/io/PrintStream;
10: aload_0
11: getfield #3; //Field array:[Ljava/lang/String;
14: iload_1
15: aaload
16: invokevirtual #9; //Method java/io/PrintStream.println:(Ljava/lang/Str
ing;)V
19: return
首先getfield
在调用grow
之前发生,第二次发生在{{1}}之后。