7

这有点奇怪,但代码比文字更能说明问题,所以看看测试看看我在做什么。在我当前的设置(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")]);
    }
}
4

3 回答 3

6

这在Java 语言规范中有很好的定义:要评估x[y],首先x要评估,然后再y评估。在您的情况下,x评估为String[]具有零元素的 a 。然后,y修改一个成员变量,并计算为0。尝试访问已返回数组的第 0 个元素失败。成员更改的事实array与数组查找无关,因为我们正在查看在评估它时引用的那个String[]array

于 2013-06-22T22:30:28.010 回答
2

这种行为是 JLS 规定的。根据15.13.1,“使用以下过程评估数组访问表达式:首先,评估数组引用表达式。如果此评估突然完成,则出于同样的原因,数组访问突然完成并且不评估索引表达式。否则,将评估索引表达式。[...]"。

于 2013-06-22T22:31:48.703 回答
0

通过使用比较编译的 Java 代码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第二次发生在调用之后。

于 2013-06-22T22:26:16.810 回答