[精]JAVA数组的内存结构详解
Posted sunansheng
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[精]JAVA数组的内存结构详解相关的知识,希望对你有一定的参考价值。
Java 数组是静态的
Java 语言是典型的静态语言,因此 Java 数组是静态的,即当数组被初始化之后,该数组 所占的内存空间、数组长度都是不可变的。Java 程序中的数组必须经过初始化才可使用。所 谓初始化,即创建实际的数组对象,也就是在内存中为数组对象分配内存空间,并为每个数组 元素指定初始值。数组的初始化有以下两种方式。
- 静态初始化:初始化时由程序员显式指定每个数组元素的初始值,由系统决定数组长度。
- 动态初始化:初始化时程序员只指定数组长度,由系统为数组元素分配初始值。
上面程序中的粗体字代码声明并初始化了三个数组。这三个数组的长度将会始终不变,程 序输出三个数组的长度依次为4 、3 、5 。 前面已经指出,Java 语言的数组变量是引用类型的变量,books、names 、strArr 这三个变 量,以及各自引用的数组在内存中的分配示意图如图1.1 所示。
public class ArrayTest
public static void main(String[] args)
// 采用静态初始化方式初始化第一个数组
String[] books = new String[]
"1", "2", "3", "4"
;
// 采用静态初始化的简化形式初始化第二个数组
String[] names =
"孙悟空",
"猪八戒",
"白骨精"
;
// 采用动态初始化的语法初始化第三个数组
String[] strArr = new String[5];
// 访问三个数组的长度
System.out.println("第一个数组的长度:" + books.length);
System.out.println("第二个数组的长度:" + names.length);
System.out.println("第三个数组的长度:" + strArr.length);

- 数组元素的类型是基本类型中的整数类型(byte 、short、int 和long ),则数组元素的值是0 。
- 数组元素的类型是基本类型中的浮点类型(float 、double ),则数组元素的值是0.0。
- 数组元素的类型是基本类型中的字符类型(char ),则数组元素的值是'\\u0000'。
- 数组元素的类型是基本类型中的布尔类型(boolean),则数组元素的值是false 。
- 数组元素的类型是引用类型(类、接口和数组),则数组元素的值是null 。
上面程序中粗体字代码将让books 数组变量、strArr 数组变量都指向names 数组变量所引 用的数组,这样做的结果就是books、strArr、names 这三个变量引用同一个数组对象。此时, 三个引用变量和数组对象在内存中的分配示意图如图1.2 所示。
// 让books 数组变量、strArr 数组变量指向names 所引用的数组
books = names;
strArr = names;
System.out.println("--------------");
System.out.println("books 数组的长度:" + books.length);
System.out.println("strArr 数组的长度:" + strArr.length);
// 改变books 数组变量所引用的数组的第二个元素值
books[1] = "唐僧";
System.out.println("names 数组的第二个元素是:" + books[1]);

与Java 这种静态语言不同的是,javascript 这种动态语言的数组长度是可以动态改变的,示例如下。
上面是一个简单的JavaScript 程序。它先定义了一个名为 arr的空数组,因为它不包含任 何数组元素,所以它的长度是0 。接着,为 arr数组的第三个、第五个元素赋值,该数组的长 度也自动变为5 。这就是JavaScript 里动态数组和Java 里静态数组的区别。
<script type="text/javascript">
var arr = [];
document.writeln("arr的长度是:" + arr.length + "<br/>");
// 为arr 数组的两个数组元素赋值
arr[2] = 6;
arr[4] = "孙悟空";
// 再次访问arr 数组的长度
document.writeln("arr的长度是:" + arr.length + "<br/>");
</script>
基本类型数组的初始化
对于基本类型数组而言,数组元素的值直接存储在对应的数组元素中,因此基本类型 数组的初始化比较简单:程序直接先为数组分配内存空间,再将数组元素的值存入对应内 存里。 下面程序采用静态初始化方式初始化了一个基本类型的数组对象。上面代码的执行过程代表了基本类型数组初始化的典型过程。下面将结合示意图详细介绍这段代码的执行过程。 执行第一行代码int[] iArr;时,仅定义一个数组变量,此时内存中的存储示意图如图1.4所示。
public class PrimitiveArrayTest
public static void main(String[] args)
// 定义一个int[] 类型的数组变量
int[] iArr;
// 静态初始化数组,数组长度为4
iArr = new int[]2 , 5 , -12 , 20;


对于Java 语言而言,堆内存中的对象(不管是数组对象,还是普通的 Java 对象)通常不 允许直接访问,为了访问堆内存中的对象,通常只能通过引用变量。这也是很容易混淆的地方。 例如,iArr 本质上只是main 栈区的引用变量,但使用 iArr.length 、iArr[2] 时,系统将会自动变 为访问堆内存中的数组对象。
对于很多Java 程序员而言,他们最容易混淆的是:引用类型的变量何时只是栈内存中的 变量本身,何时又变为引用实际的Java 对象。其实规则很简单:引用变量本质上只是一个指 针,只要程序通过引用变量访问属性,或者通过引用变量来调用方法,该引用变量就会由它所 引用的对象代替。
上面程序中两行粗体字代码两次访问iArr 变量。对于①行代码而言,虽然此时的iArr 数 组变量并未引用到有效的数组对象,但程序在①行代码处并不会出现任何问题,因为此时并未 通过iArr 访问属性或调用方法,因此程序只是访问iArr 引用变量本身,并不会去访问iArr 所 引用的数组对象。对于②行代码而言,此时程序通过iArr 访问了length 属性,程序将自动变 为访问iArr 所引用的数组对象,这就要求iArr 必须引用一个有效的对象。
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); //②
有过一些编程经验,应该经常看到一个Runtime 异常: NullPointerException (空指针异常)。当通过引用变量来访问实例属性,或者调 用非静态方法时,如果该引用变量还未引用一个有效的对象,程序就会引发 NullPointerException 运行时异常。
引用类型数组的初始化
引用类型数组的数组元素依然是引用类型的,因此数组元素里存储的还是引用,它指向另一块内存,这块内存里存储了该引用变量所引用的对象(包括数组和Java 对象)。 为了说明引用类型数组的运行过程,下面程序先定义一个Person 类,然后定义一个 Person[]数组,并动态初始化该Person[]数组,再显式地为数组的不同数组元素指定值。该程序代码如下。
class Person
// 年龄
publicJava细节整理——数组与内存控制