元数据卡
- 前置知识:第6章全文(数组声明、遍历、操作、多维数组)
- 预计时间:20 分钟
- 阅读模式:💪动手实操
- 完成标志:完成所有热身题和至少一道挑战题
⚠️ 常见陷阱
陷阱一:索引越界(Off-by-One Errors)
int[] arr = new int[10];
// 合法索引:0 ~ 9
arr[10] = 100; // ArrayIndexOutOfBoundsException!如何避免:每次写循环条件时默念三遍——"最大索引是 length - 1"。
// ❌ 错误:i <= arr.length
for (int i = 0; i <= arr.length; i++) { } // 最后 i=arr.length 时越界
// ✅ 正确:i < arr.length
for (int i = 0; i < arr.length; i++) { }陷阱二:空指针——数组还没建就访问
int[] scores = null;
System.out.println(scores.length); // NullPointerException
scores[0] = 85; // NullPointerException如何避免:访问前检查 null,确保 new 过。
陷阱三:引用混淆——两个变量指向同一个数组
int[] a = {1, 2, 3};
int[] b = a; // 不是复制——b 和 a 指向同一个数组!
b[0] = 999;
System.out.println(a[0]); // 999 —— 改的是同一个地方如何避免:需要独立副本时用 Arrays.copyOf() 或 a.clone()。
陷阱四:二维数组的行列混淆
// 遍历时很容易把行和列的变量名弄反
for (int col = 0; col < grid.length; col++) { // ❌ col 跑了行数
for (int row = 0; row < grid[col].length; row++) { // ❌ row 跑了列数如何避免:命名明确 row 和 col,不要只用 i 和 j。
陷阱五:Arrays.toString 不是默认的
int[] scores = {85, 92, 78};
System.out.println(scores); // [I@7f31245a(内存地址)
System.out.println(Arrays.toString(scores)); // [85, 92, 78]数组是引用类型,不重写 toString。必须用 Arrays.toString。
通关挑战
🏋️ 热身(10 分钟,必做)
1. 填充数组 声明一个 int 数组 scores,长度 10,用循环填充值为 100 到 190(步长 10)。
// 参考
int[] scores = new int[10];
for (int i = 0; i < scores.length; i++) {
scores[i] = 100 + i * 10;
}2. 增强 for 打印 用增强 for 打印上面 scores 的所有元素。
3. 排序 创建一个 int[] 数组 {3, 7, 1, 9, 4},用 Arrays.sort 排序后打印。
4. 二维乘法表 创建一个二维数组表示 4×4 的乘法表(grid[i][j] = (i+1)*(j+1)),遍历打印。
🔥 挑战(20 分钟,选做)
1. 逆序:把数组 {1, 2, 3, 4, 5} 原地逆序变成 {5, 4, 3, 2, 1}。不能创建新数组。
// 思路:第一个和最后一个交换,第二个和倒数第二个交换……
for (int i = 0; i < arr.length / 2; i++) {
int temp = arr[i];
arr[i] = arr[arr.length - 1 - i];
arr[arr.length - 1 - i] = temp;
}2. 找最大值:写一个方法 int max(int[] arr),返回数组中最大的元素。考虑数组为 null 或长度为 0 的情况。
3. 合并有序数组:给定两个已排序的数组 a = {1, 3, 5, 7} 和 b = {2, 4, 6, 8},合并成一个依然有序的数组。
4. 二维数组转置:把 3×4 的矩阵转置成 4×3(行和列互换)。
🔍 排障——下面几段代码哪里错了?
// 问题 1:
int[] arr = new int[5]{1, 2, 3, 4, 5};
// 问题 2:
int[] arr = new int[5];
System.out.println("长度:" + arr.length()); // 调用了 length()
// 问题 3:
int[][] grid = new int[3][];
grid[0][0] = 42; // 还没创建第二层数组!
// 问题 4:
int[] arr = {1, 2, 3};
arr = {4, 5, 6}; // 声明之后再赋值答案:1. 不能同时用 new int[5] 和大括号初始化;2. length 是属性不是方法——没有 ();3. new int[3][] 只建了行引用,每行还需要 new int[...];4. 声明后赋值必须用 new int[]{4, 5, 6}。
✅ 验收标准
- [ ] 你能用
new int[n]和{...}两种方式创建数组 - [ ] 你知道数组索引从 0 开始,最大索引是
length - 1 - [ ] 你能用标准 for 和增强 for 两种方式遍历数组
- [ ] 你理解数组长度一旦创建就不可改变
- [ ] 你会用
Arrays.copyOf和System.arraycopy复制数组 - [ ] 你能创建和遍历二维数组,理解
arr.length和arr[i].length的区分 - [ ] 你会用
Arrays.sort、Arrays.binarySearch、Arrays.fill、Arrays.toString、Arrays.equals - [ ] 你警惕数组索引越界、空指针、引用拷贝陷阱
❓ 常见卡点
"为什么数组索引不能从 1 开始?"
arr[i] 本质上是 *(arr + i)——从基地址偏移 i 个元素。从 1 开始就得 *(arr + (i - 1)),多一次减法运算。当年 CPU 资源珍贵,习惯就好。
"我赋值 arr[arr.length] 为啥报错?"
最后一个合法索引是 arr.length - 1。arr.length 本身是数组长度,不是合法索引。
"为什么打印数组出来是一串乱码?"
因为默认 toString 打印的是内存地址。用 Arrays.toString(arr) 替代。
"二维数组每一行一定等长吗?"
Java 中不一定——你可以有不规则数组。C++ 的 int arr[3][4] 一定是矩形。
🎒 旅人笔记
数组是同一类型元素的连续存储空间。声明 + 创建,或者静态初始化——你选。 索引从 0 开始,终点是 length-1。 建了就不能改大小——想要更大的,自己建新城、搬老住户。 循环遍历:索引版给全控制,增强版只读更简洁。 复制有三式:手写循环(原始)、System.arraycopy(极速)、Arrays.copyOf(日常)。 二维数组是数组的数组,行是外层、列是内层。 Arrays 工具类——sort、binarySearch、fill、toString——别自己造轮子。 可变参数
int...是语法糖,本质是数组。
最深刻的一句话:数组是"定容"的。这个"定"意味着分配上的紧凑和效率,也意味着增长的代价。理解数组的定容特性,你就理解了为什么 ArrayList 存在、为什么链表存在、为什么数据结构的世界如此丰富。
→ 下一站预告
数组给了你批量装数据的容器,但每次你要操作一个"人"——有姓名、有年龄、有成绩——你只能靠三个独立的数组 String[] names、int[] ages、int[] scores 硬对应。
这太原始了。三个数组各自排好序,人工确保索引对齐。一个排序操作就让三个数组散架。
下一章,我们将从"数组"跃入 面向对象 的世界。你会学会把一个人的所有信息打包成一个"对象",一条街上住的不再是孤零零的整数,而是活生生的人。
准备好迎接 类与对象 了吗?