Java二维数组本质是“数组的数组”:规则数组一次性分配固定大小的多块内存,行为近似矩阵;不规则数组外层存null引用,内层独立分配、地址离散,需手动初始化且访问前必须判空。

Java 中的二维数组不是“真正的二维”,而是“数组的数组”。规则与不规则二维数组在内存中根本不是同一种结构——前者像一块整砖,后者像几块散砖用绳子串起来。关键差异不在语法写法,而在 JVM 分配时机、内存连续性与访问安全性。
外层数组:都连续,但用途不同
无论规则还是不规则,外层数组(即 int[][] 本身)都是一个一维引用数组,在堆中连续存放,每个元素存的是指向内层数组的引用(或 null)。
- 规则数组:
int[][] a = new int[3][4]→ 外层数组长度为 3,且创建时立即为每行分配好长度为 4 的int[]对象,所有内层数组引用非 null - 不规则数组:
int[][] b = new int[3][]→ 外层数组长度也为 3,但每个元素初始值都是 null,后续需手动赋值,比如b[0] = new int[2]
内层数组:规则统一,不规则离散
内层数组是真正存数据的地方,它们的分配方式决定了内存布局的本质差异:
- 规则数组的三行
int[4]虽然物理地址未必紧挨着,但大小相同、分配时机一致,JVM 可按行主序逻辑组织,行为接近传统矩阵 - 不规则数组的各行(如
new int[2]、new int[5]、new int[3])是独立对象,在堆中任意位置分配,彼此地址无关联,长度互不影响 - 没有“列对齐”概念:访问
b[1][4]合法(因该行长度为 5),但b[1][5]报ArrayIndexOutOfBoundsException;而b[2][3]直接抛NullPointerException(若b[2]还没初始化)
访问过程:两步解引用,安全校验分离
每次 arr[i][j] 访问都拆成两个独立步骤,这是理解差异的核心:
立即学习“Java免费学习笔记(深入)”;
- 第一步:查外层数组第
i项 → 得到一个引用值(可能是地址,也可能是 null) - 第二步:用该引用去访问对应一维数组的第
j个元素 → 此时只校验该一维数组自身的.length - 所以规则数组不会空指针(所有行已初始化),而不规则数组必须先判空再查长度,例如:
if (b[i] != null && j < b[i].length) { ... }
典型内存布局示意(逻辑视图)
假设执行:
int[][] jagged = new int[3][]; jagged[0] = new int[2]; // 地址 A jagged[1] = new int[5]; // 地址 B jagged[2] = new int[3]; // 地址 C
堆中实际存在:
- 一个连续的引用数组:
[A, B, C](3 个地址,挨着存放) - 三个独立的
int[]对象:分别占 2、5、3 个int单元,散落在堆不同区域 - 栈中只有变量
jagged,它存储的是外层数组首地址(即[A,B,C]的起始位置)


















