【Java基础|Day04】Java数组详解:从定义到内存原理(2026最新实用版)
Java数组是最基础、最常用的引用数据类型,几乎所有集合框架(如ArrayList底层)都依赖它。掌握数组 = 掌握了Java中连续内存 + 引用机制 + 堆栈区别的核心。
本篇按定义 → 初始化 → 访问/遍历 → 常见操作 → 内存原理顺序拆解,带图文 + 代码 + 内存图 + 面试高频坑。
1. 数组是什么?核心特性速记(5句话记住)
- 同类型元素的有序集合(固定长度,不可变)
- 引用类型:数组变量本身是引用,真实数据在堆里
- 长度固定:创建后不可改变(不像List)
- 下标从0开始,范围
[0, length-1] - 默认初始化:创建后元素有默认值(int→0, 对象→null)
2. 数组的定义与声明(三种写法都懂)
// 推荐写法(类型在前,[]可放变量名后)
int[] scores; // 声明(推荐,阅读性好)
double[] prices;
String[] names;
// 也合法(C/C++风格,但不推荐)
int scores[];
注意:声明时不指定长度,长度在初始化时确定。
3. 数组初始化(两种方式 + 简化写法)
| 初始化方式 | 语法示例 | 特点 | 适用场景 | 默认值情况 |
|---|---|---|---|---|
| 动态初始化 | int[] arr = new int[5]; | 先new分配空间,系统赋默认值 | 后期赋值,长度已知 | 有(0 / false / null / 0.0) |
| 静态初始化 | int[] arr = new int[]{1,2,3}; | 直接写元素,长度由元素个数决定 | 元素已知,少量数据 | 无需默认值 |
| 简化静态 | int[] arr = {1,2,3}; | 最常用,只能声明+初始化一起写 | 常量数组、测试数据 | 无需默认值 |
代码示例(三种方式对比):
public class ArrayInit {
public static void main(String[] args) {
// 动态初始化
int[] arr1 = new int[4]; // 长度4,元素默认0
System.out.println(arr1[0]); // 0
// 静态初始化(完整写法)
double[] arr2 = new double[]{3.14, 2.718, 1.414};
// 简化静态初始化(最常用)
String[] arr3 = {"苹果", "香蕉", "橙子"};
// 二维数组(不规则也行)
int[][] matrix = {
{1, 2, 3},
{4, 5},
{6, 7, 8, 9}
};
}
}
二维数组本质:一维数组的数组(每个元素又是一个一维数组引用)。
4. 数组访问、遍历、长度(length属性)
- 访问:
arr[下标],下标越界 →ArrayIndexOutOfBoundsException - 长度:
arr.length(属性,不是方法!)
四种遍历方式对比(面试常问):
int[] arr = {10, 20, 30, 40};
// 1. 普通for(最灵活,可改值)
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
arr[i] *= 2; // 可以修改
}
// 2. 增强for(foreach,只读,简洁)
for (int num : arr) {
System.out.println(num);
// num = 100; // 无效!只是副本
}
// 3. Arrays工具类(快捷)
import java.util.Arrays;
System.out.println(Arrays.toString(arr)); // [20, 40, 60, 80]
// 4. Java 8+ Stream(函数式,进阶)
Arrays.stream(arr).forEach(System.out::println);
5. 数组常见操作(面试/实战高频)
- 求最大/最小/和/平均
- 查找(线性、二分)
- 排序(Arrays.sort())
- 拷贝(System.arraycopy() / Arrays.copyOf())
- 反转(双指针 / Collections.reverse() 转List后)
- 扩容(本质new新数组 + copy)
经典面试题代码模板:
// 数组反转(双指针)
public static void reverse(int[] arr) {
int left = 0, right = arr.length - 1;
while (left < right) {
int temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
left++;
right--;
}
}
// 查找元素第一次出现位置(线性)
public static int indexOf(int[] arr, int target) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] == target) return i;
}
return -1;
}
6. 数组的内存原理(最重要!画图理解)
Java内存分为栈(Stack) 和 堆(Heap):
- 栈:存放局部变量、方法调用帧、数组的引用(地址)
- 堆:存放new出来的对象、数组的真实元素(所有数组元素都在堆)
内存图示意(一维数组):
栈内存(main方法帧) 堆内存
int[] scores → 0x1234 ------------→ [数组对象头]
长度: 5
元素0: 0 ← scores[0]
元素1: 85 ← scores[1]
元素2: 92
元素3: 0
元素4: 0
二维数组内存图(本质是“数组的数组”):
栈
int[][] matrix → 0xABCD → [外层数组对象]
长度: 3
元素0 → 0x1111 → [内层数组1: {1,2,3}]
元素1 → 0x2222 → [内层数组2: {4,5}]
元素2 → 0x3333 → [内层数组3: {6,7,8,9}]
关键结论:
- 数组变量(引用)在栈,指向堆中的数组对象
- 数组对象包含:对象头 + 长度(4字节) + 连续的元素数据
- 元素是基本类型 → 直接存值;元素是引用类型 → 存地址(又指向别的堆对象)
- 数组长度不可变 → 扩容必须new新数组 + 复制
- 多维数组不一定是矩形(jagged array),每行长度可不同
经典面试追问:
int[] a = new int[0];合法吗? → 合法,长度0,不报错arr = null;后arr.length? → NullPointerException- 数组拷贝是浅拷贝(引用类型只拷地址)
7. Day04 速成自测题(答案在文末)
- 下面哪种初始化方式是错误的?
A.int[] a = new int[3];
B.int[] b = {1,2,3};
C.int[] c = new int[]{ };← 空数组合法 - 增强for循环中修改变量,能改变原数组吗?为什么?
- 二维数组
int[][] arr = new int[3][];后,arr[0]是什么?能直接arr[0][0]吗?
答案:
- C合法(空数组)
- 不能,foreach是值拷贝(基本类型)或引用拷贝(但赋值是局部变量)
- arr[0]是null,arr[0][0] → NullPointerException(需先arr[0] = new int[长度];)
数组看似简单,但内存模型是Java面向对象、JVM运行时的基石。下一讲(Day05)我们直接进入方法、参数传递(值传递 vs 引用传递的本质),数组就是最好的切入点!
有具体代码想调试、内存图想细化、或面试题想刷的,直接贴上来,我继续陪练!