在Java中,数组可以说是经常用到的,但关于数组是什么类型的这个问题,我却没有深入研究过。以下内容参考自《Java语言规范》,记录了数组的类型是什么?更进一步地说,数组对象的getClass()返回的是什么?
1.数组更详细的特点
数组具有以下几点特点:
a.在Java编程语言中,数组是动态创建的对象,可以被赋值给Object类型的变量。Object类的所有方法都可以在数组上调用。
b.数组对象包含大量的变量。
c.数组的所有元素都具有相同的类型,称为数组的元素类型。如果数组的元素类型为T,那么数组自身的类型就写作T[ ]。
这里针对数组的特点a进行详细说明:
下面代码片段中,会创建一个数组对象,并且这个对象可以赋值给Object对象。
int[] ai1 = {1,2};//隐式地创建一个新的数组对象
int[] ai = new int[2];//显式地创建一个数组对象
Object o = ai;//数组对象可以赋值给Object类型的变量
o = ai1;
通过上面的代码片段,再联系“数组是动态创建的对象”这句话,我们可以猜测:数组的类型很可能是运行时通过反射动态创建的,并且其类型是Object的子类。
既然知道数组具有特定类型了,那么这个类型具有什么成员呢?下面将进行说明。
2.数组类型的成员
数组类型的成员包括以下所有内容:
- public final 域 length,它包含了数组的元素数量。length可以是正数或0。
- public 方法 clone,它覆盖了Object类中的同名的方法,并且不会抛出任何受检异常。
多维数组的克隆是浅复制,即它只创建单个新数组,子数组是共享的。 - 所有从Object类继承而来的成员,Object中唯一没有被继承的方法就是clone方法。
因此,数组具有与下面的类相同的public域和方法:
class A<T> implements Cloneable,java.io.Serializable {
public final int length = X;
public T[] clone() {
try{
return (T[])super.clone();
} catch (CloneNotSupportedException e) {
throw new InternalError(e.getMessage());
}
}
}
注意:在上面例子中,如果数组真的以这种方式实现,那么其中向T[ ]的强制类型转换会产生非受检警告。
从上面可以知道,数组是可克隆的
class Test1 {
public static void main(String[] args) {
int ia1[] = {1,2};
int ia2[] = ia1.clone();
System.out.print((ia1 == ia2) + " ");
ia1[1]++;
System.out.println(ia2[1]);
}
}
这个程序会产生下面的输出:
false 2
该输出展示了ia1和ia2所引用的数组元素是不同的变量。
下面的例子说明多维数组克隆后共享子数组:
class Test2 {
public static void main(String[] args) throws Throwable {
int ia[][] = { { 1, 2 }, null };
int ja[][] = ia.clone();
System.out.print((ia == ja) + " ");
System.out.println(ia[0] == ja[0] && ia[1] == ja[1]);
}
}
这个程序会产生下面的输出:
false true
该输出展示了 int[ ] 数组 ia[0] 和 int[ ] 数组 ja[0] 是同一个数组。
3.数组的Class对象
每个数组都与一个Class对象关联,并与其他具有相同元素类型的数组共享该对象。尽管数组类型不是类,但是每一个数组的Class对象起到的作用看起来都像是:
- 每个数组类型的直接超类都是Object。
每个数组类型都实现了Cloneable 接口和 java.io.Serializable 接口
下面的代码用于测试上面的特点:
package com.test;
class A {
}
class B extends A implements Comparable<B>{
@Override
public int compareTo(B o) {
return 0;
}
}
public class Test2 {
public static void main(String[] args) throws Throwable {
B[] ba = new B[2];
B b = new B();
B[] ba1 = new B[2];
//测试数组的Class对象是共享的
System.out.println(ba == ba1);
System.out.println(ba.getClass() == ba1.getClass());
//测试数组ba和b的Class对象是否一样
System.out.println(ba.getClass() + " | " + b.getClass());
//测试数组ba和b的超类是否一样
System.out.println(ba.getClass().getSuperclass() + " | " + b.getClass().getSuperclass());
//测试数组ba和b实现的接口分别是什么
for(Class<?>c : ba.getClass().getInterfaces())
System.out.println("Superinterfaces: " + c);
System.out.println("-----------");
for(Class<?>c : b.getClass().getInterfaces())
System.out.println("Superinterfaces: " + c);
}
}
上面程序输出是:
false
true
class [Lcom.test.B; | class com.test.B
class java.lang.Object | class com.test.A
Superinterfaces: interface java.lang.Cloneable
Superinterfaces: interface java.io.Serializable
- - - - - - - - - - -
Superinterfaces: interface java.lang.Comparable
其中,字符串“[Lcom.test.B”是“元素类型为com.test.B的数组”的Class对象的运行时类型签名。
根据上面的输出结果,可得出以下总结。
数组的Class对象是共享的。
虽然每个数组都与一个 Class 对象关联,但数组的Class对象并不等于数组元素的Class对象。
从上面这个输出可以看出:class [Lcom.test.B; | class com.test.B数组的类型是Object类的子类,并且数组的类型和数组元素的类型不一样。
如上面的输出中, B[ ] 的超类是Object,而B的超类是A。B[ ]类型实现的是Cloneable 和 Serializable 接口,而B类实现的是Comparable 接口。