一、定义数组
数组的定义有三种方式:
方式1:推荐
type[] a = new type[元素个数];
方式2:c语言方式
type a[] = new type[元素个数];
方式3:直接初始化
type[] a = new type[]{逗号分隔的初始化值};
例如:int[] a = new int[]{1,2,3,4,5};
当数组存放的元素不是基本数据类型时,数组内存放的是对象的引用
二、数组是什么?
数组肯定不是基本数据类型,按照我们自己的说法:
数组是一种复合数据类型,它含有一系列相同类型的元素,并且元素的数量是定长的,元素的排列是有序的。
我们可以通过代码来了解一下数组:
int[] a = new int[10];
System.out.println(a.getClass().getSuperclass());
System.out.println(a.getClass().getName());
int[][] b = new int[10][10];
System.out.println(b.getClass().getSuperclass());
System.out.println(b.getClass().getName());
Integer[] c = new Integer[10];
System.out.println(c.getClass().getSuperclass());
System.out.println(c.getClass().getName());
Integer a = new Integer(1);
System.out.println(a.getClass().getSuperclass());
System.out.println(a.getClass().getName());
//output:
class java.lang.Object
[I
class java.lang.Object
[[I
class java.lang.Object
[Ljava.lang.Integer
class java.lang.Object
[C
class java.lang.Number
java.lang.Integer
可见,数组也是一个对象,但是数组的类名却有点奇怪,但是从规律上来看,可以知道:
1. [ 代表数组的维度
2. 最后的大写字母是一种特殊的标识,标识数组的类型,例如int数组的是I,char数组的C,同理double的就是D,如果是对象则是L + 全限定名
在jvm中,这被叫做描述符,描述符的作用是用来描述字段的数据结构、方法的参数列表和返回值,它的规则是:基本数据类型和void用它们的第一个大写字母表示,而对象类型则用字符L加对象的全限定名来表示,例如:
int[][]的描述符是:[[I
方法void inc(int i)的的描述符是:(I)V
从上面可以看出,数组的元素时用描述符来表示的,而普通的对象则是用类的全限定名表示,这也侧面的暗示了jvm处理数组和普通对象的方式是不同的!
对于数组,jvm不通过类加载器加载(类加载器会从源码包中读取类的class文件),而是由jvm直接创建的,而对于普通对象,则是由类加载器加载,因此会到类路径下加载。但是,数组类的元素类型最终还是靠类加载器去创建
有一个问题值得思考,为什么数组通过.length来获取长度,而String通过.length()来获取长度?
从面向对象的角度来说,直接暴露成员变量是不合理的,我们可以通过反射看看数组的内部结构:
Object[] obj = new Object[10];
Class clazz = obj.getClass();
System.out.println(clazz.getDeclaredConstructors().length);
System.out.println(clazz.getDeclaredFields().length);
System.out.println(clazz.getDeclaredMethods().length);
try {
System.out.println(clazz.getDeclaredField("length"));
} catch (NoSuchFieldException e) {
System.out.println("没有该域");
} catch (SecurityException e) {
}
//output:
0
0
0
没有该域
可以看到,数组内部,没有任何的 构造方法、方法、变量,并且连length这个域都没有,而我们就是通过length这个域来获取长度的,这不是很矛盾吗?
这就是JVM做的处理:
使用SUN JDK 1.6编译上述代码,并使用jclasslib打开Main.class文件,得到main方法的字节码:
0 iconst_2 //将int型常量2压入操作数栈
1 newarray 10 (int) //将2弹出操作数栈,作为长度,创建一个元素类型为int, 维度为1的数组,并将数组的引用压入操作数栈
3 astore_1 //将数组的引用从操作数栈中弹出,保存在索引为1的局部变量(即a)中
4 aload_1 //将索引为1的局部变量(即a)压入操作数栈
5 arraylength //从操作数栈弹出数组引用(即a),并获取其长度(JVM负责实现如何获取),并将长度压入操作数栈
6 istore_2 //将数组长度从操作数栈弹出,保存在索引为2的局部变量(即i)中
7 return //main方法返回
可见,在这段字节码中,根本就没有看见length这个成员变量,获取数组长度是由一条特定的指令arraylength实现(怎么实现就不管了,JVM总有办法)。编译器对Array.length这样的语法做了特殊处理,直接编译成了arraylength指令
三、二维数组的定义
基本的定义方式有两种形式,如:
方式一:(推荐)
type[][] i = new type[2][3];
方式二:
type i[][] = new type[2][3];
声明二维数组的时候可以只指定第一维大小,空缺出第二维大小,之后再指定不同长度的数组。但是注意,第一维大小不能空缺(不能只指定列数不指定行数)