对于基本类型数组而言,数组元素的值直接存储在对应的数组元素中,因此基本类型数组的初始化比较简单:程序直接先为数组分配内存空间,再将数组元素的值存入对应内存里。
下面程序采用静态初始化的方式初始化了一个基本类型的数组对象。
public class PrimitiveArrayTest { public static void main(String[] args) { //定义一个 int[]类型的数组变量 int[] iArr; //静态初始化数组,数组长度为4 iArr = new int[]{2 , 5 , -12 , 20}; } }
上面代码执行了int[] iArr;代码后,仅在main方法栈中定义了一个iArr 数组变量,它是一个引用类型的变量,并未指向任何有效的内存,没有真正指向实际的数组对象。此时还不能使用该数组对象。 当执行 iArr = new int[]{2,5,−12,20};静态初始化后,系统会根据程序员指定的数组元素来决定数组的长度。一旦该数组对象创建成功,该数组的长度将不可改变,程序只能改变数组元素的值。此时静态初始化完成后,iArr 数组变量所引用的数组所占用的内存空间被固定下来,程序员只能改变各数组元素内的值,但不能移动该数组所占用的内存空间–既不能扩大该数组对象所占用的内存,也不能缩减该数组对象所占用的内存。
对于程序运行过程中的变量,可以将它们形容为具体的瓶子–瓶子可以存储水,而变量也用于存储值,也就是数据。对于强类型语言如 Java,它有一个要求–什么样的瓶子只能装什么样的水,也就是说,指定类型的变量只能存储指定类型的值。
有些书籍中总是不断地重复:基本类型变量的值存储在栈内存中,其实这句话不完全正确。例如上述代码中的2、5、−12、20,它们都是基本类型的值,但实际上它们却存储在堆内存中。正确地应该说:所有局部变量都是放在栈内存里保存的,不管其是基本类型的变量,还是引用类型的变量,都是存储在各自的方法栈区中;但引用类型变量所引用的对象(包括数组、普通Java对象)则总是存储在堆内存中。
对于Java 语言而言,堆内存中的对象(不管是数组对象,还是普通的Java 对象)通常不允许直接访问,为了访问堆内存中的对象,通常只能通过引用变量。这也是很容易混淆的地方。例如,iArr 本质上只是 main 栈区的引用变量,但使用 iArr.length、iArr[2]时,系统将会自动变为访问堆内存中的数组对象。
对于很多 Java 程序员而言,他们最容易混淆的是:引用类型变量何时只是栈内存中的变量本身,何时又变为引用实际的Java对象。其实规则很简单:引用变量本质上只是一个指针,只要程序通过引用变量访问属性,或者通过引用变量来调用方法,该引用变量将会由它所引用的对象代替。示例代码如下。
public class PrimitiveArrayTest2 { public static void main(String[] args) { //定义一个 int[]类型的数组变量 int[] iArr = null; //只要不访问 iArr 的属性和方法,程序完全可以使用该数组变量 System.out.println(iArr); //① //动态初始化数组,数组长度为5 iArr = new int[5]; //只有当 iArr 指向有效的数组对象后,下面才可访问iArr的属性 System.out.println(iArr.length); //② } }
上面程序中两行粗体字代码两次访问 iArr 变量。对于①行代码而言,虽然此时的iArr 数组变量并未引用到有效的数组对象,但程序在①处并不会出现任何问题,因为此时并未通过iArr 访问属性或调用方法,所以系统不会去访问 iArr 所引用的数组对象。对于②行代码而言,此时程序通过 iArr 访问了length属性,程序将自动变为访问 iArr 所引用的数组对象,这就要求 iArr 必须引用一个有效的对象。
如果读者有过一些编程经验,应该经常看到一个 Runtime 异常:NullPointerException(空指针异常)。当通过引用变量来访问实例属性,或者调用非静态方法时,如果该引用变量还未引用一个有效的对象,程序就会引发NullPointerException 运行时异常。
转载请注明:零五宝典 » Java中基本类型数组的初始化