有人可以解释一下阵列在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 [] []
答案 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
请注意代码编译(带有警告)并且运行完全正常,直到您使用带有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[]
放入test
和test3
,因为它们是通过更严格的构造函数创建的:new Object[][]
。
答案 5 :(得分:0)
您已将test2
定义为
Object test2 = new Object[]; // This is a plain array of Objects.