java数组如何真正起作用

时间:2011-06-16 21:07:40

标签: java arrays jvm runtime scjp

有人可以解释一下阵列在Java中是如何工作的。

我对以下代码感到惊讶:

        Object test = new Object[2][2];
        Object test2 = new Object[] {
                new Object[2],new Object[2]
        };
        Object test3 = new Object[2][];
        ((Object[])test3)[0] = new Object[2];
        ((Object[])test3)[1] = new Object[2];
        System.out.println(test instanceof Object[]);
        System.out.println(test instanceof Object[][]);
        System.out.println(test2 instanceof Object[]);
        System.out.println(test2 instanceof Object[][]);
        System.out.println(test3 instanceof Object[]);
        System.out.println(test3 instanceof Object[][]);

只有test2不是Object [] []

的实例

运行时有什么区别?

编辑:我看到了一些答案。 Jon Skeet,请注意我能做到:

Object[] test4 = (Object [])test;
test4[0] = "blaaa";
test4[1] = "toto";
System.out.println(test4);

test instanceof Object []返回true,并且在转换时没有在运行时引发异常。根据SCJP的Sierra& amp; Bates,测试IS-A Object [] [],但也测试Object []

但是当试图用“test4 [0] =”blaaa“;”重新分配一个新值时,我得到一个例外: 线程“main”中的异常java.lang.ArrayStoreException:java.lang.String     在Main.main(Main.java:24)

所以在运行时看来,test和test2都是IS-A Object [],并且都包含对象数组,但只有一个是IS-A Object [] []

6 个答案:

答案 0 :(得分:12)

Test2仅被声明为对象数组。它包含的对象恰好也是数组,但是没有声明。这就是区别。

答案 1 :(得分:8)

test2指的是两个元素的数组。它的类型只是Object[] - 因此这些元素可以引用任何对象。特别是,你可以写:

// Valid
Object[] foo = (Object[]) test2;
foo[0] = "hello";

不会适用于test

// Invalid - test isn't just an Object[], it's an Object[][]
Object[] foo = (Object[]) test;
test[0] = "hello";

因为test引用的数组的元素类型是Object[]而不是Object。数组“知道”每个元素应为null或对Object[]的引用,因此VM将阻止它存储字符串。

您可以将test转换为Object[]的方式与将String[]转换为Object[]的方式相同 - 即所谓的数组协方差,在我看来允许它是错误的。正如我们所见,VM必须在执行时检查商店。

答案 2 :(得分:2)

我找不到任何问题的完整答案,我会这样做......

读完SCJP书后,对我来说更加清晰。它只是在泛型章节而不是数组中处理过。 (泛型与阵列) Jon Skeet的答案很好但对我来说似乎不完整。

因此,您必须了解与泛型和数组的差异。

泛型只是一种“编译安全性”。在运行时没有检查。这意味着,通过以下技巧,您可以将String objets插入到Set

public static void main(String [] args) {
    Set<Integer> set = new HashSet<Integer>();
    set.add(1);
    set.add(2);
    addString(set,"test");
    for ( Object o : (Set)set ) {
        System.out.println(o);
    }
    for ( Object o : set ) {
        System.out.println(o);
    }
}

public static void addString(Set set,String s) {
    set.add(s);
}

输出结果为:

1
2
test
1
2
ClassCastException

http://www.ideone.com/nOSQz

请注意代码编译(带有警告)并且运行完全正常,直到您使用带有Set<Integer>引用的集合,因为有一个隐式转换尝试将String转换为Integer ...

这是通过这种方式完成的,原因很多(并非所有内容都暴露在本书中),如后向兼容性,以及需要能够调用遗留代码(非泛型),同时提供非泛型集合。

如果您不调用任何遗留代码而只调用泛型代码,那么您不应该遇到任何问题,您的集合只包含您自编译器处理后所需的内容,并且无需在运行时进行检查。 ..

由于泛型在运行时没有检查并试图阻止在集合中插入错误的项目,因此禁止将List<Dog>强制转换为List<Animal>,或者调用method(List<Animal> })使用List<Dog> param,因为这意味着您可以通过操纵List<Dog>来在List<Animal>中插入Cat ...


阵列不起作用。

像Jon Skeet sais一样,数组有类型协方差。

因此,我们可以将Dog []转换为Animal [],因为Dog是一种动物。

与泛型一样,Java希望避免将Cats插入Dog数组。 但由于协方差,它不能在编译时完成。 像Jon Skeet一样,我同意这种协方差可能不是一个好主意,但它是java遗产...... 因此,与泛型相反,数组确实有运行时检查以防止将猫插入狗阵列。

在运行时,JVM知道应该在我的数组中插入什么,而使用泛型则不知道。


所以,回到我最初的问题

Object test = new Object[2][2];
Object[] test2 = (Object [])test;
test2[0] = "blaaa";
test2[1] = "toto";
System.out.println(test2);

测试(2D阵列)可以被铸造到test2(1D阵列),但是在场景后面它仍然是一个2D阵列,最后是一个1D阵列A1,希望用其他包含对象的1D阵列A2填充

这就是为什么在A1(最后是test2)中引发ArrayStoreException时我试图插入一个String,它最终是一个Object而不是一个Object []


总结:

由于使用可以转换为1D数组的1D和2D数组,可能会有一些混乱,但对于此代码它将完全相同:

阵列:

Dog[] dogs = new Dog[1];
dogs[0] = new Dog();
Animal[] animals = (Animal [])dogs;
animals[1] = new Cat();

第4行在运行时失败。 并且你无法将猫插入狗阵列。

如果我们对泛型做同样的事情

Set<Dog> dogs = new HashSet<Dog>();
dogs.add( new Dog() );
Set<Animal> animals = (Set<Animal>) dogs;
animals.add( new Cat() );

由于第3行,这不能编译。 但是通过使用遗留的泛型代码,您可以将猫插入狗集。

答案 3 :(得分:1)

test2引用的对象是Object []。

Instanceof正在测试test2引用的对象的类型,而不是数组内容的类型。

运行时数组的内容是Object [],它可以放入Object [],因为Object []是对象。

答案 4 :(得分:0)

test2是您可以放置​​任何Object的内容,因为它是通过new Object[]创建的。您只能将Object[]放入testtest3,因为它们是通过更严格的构造函数创建的:new Object[][]

答案 5 :(得分:0)

您已将test2定义为

Object test2 = new Object[];     // This is a plain array of Objects.