跳到内容

元数据卡

  • 前置知识:第6章全文(数组声明、遍历、操作、多维数组)
  • 预计时间:20 分钟
  • 阅读模式:💪动手实操
  • 完成标志:完成所有热身题和至少一道挑战题

⚠️ 常见陷阱

陷阱一:索引越界(Off-by-One Errors)

java
int[] arr = new int[10];
// 合法索引:0 ~ 9

arr[10] = 100; // ArrayIndexOutOfBoundsException!

如何避免:每次写循环条件时默念三遍——"最大索引是 length - 1"。

java
// ❌ 错误: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++) { }

陷阱二:空指针——数组还没建就访问

java
int[] scores = null;
System.out.println(scores.length); // NullPointerException
scores[0] = 85;                    // NullPointerException

如何避免:访问前检查 null,确保 new 过。

陷阱三:引用混淆——两个变量指向同一个数组

java
int[] a = {1, 2, 3};
int[] b = a; // 不是复制——b 和 a 指向同一个数组!

b[0] = 999;
System.out.println(a[0]); // 999 —— 改的是同一个地方

如何避免:需要独立副本时用 Arrays.copyOf()a.clone()

陷阱四:二维数组的行列混淆

java
// 遍历时很容易把行和列的变量名弄反
for (int col = 0; col < grid.length; col++) {        // ❌ col 跑了行数
    for (int row = 0; row < grid[col].length; row++) { // ❌ row 跑了列数

如何避免:命名明确 rowcol,不要只用 ij

陷阱五:Arrays.toString 不是默认的

java
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,用循环填充值为 100190(步长 10)。

java
// 参考
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}。不能创建新数组。

java
// 思路:第一个和最后一个交换,第二个和倒数第二个交换……
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(行和列互换)。


🔍 排障——下面几段代码哪里错了?

java
// 问题 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.copyOfSystem.arraycopy 复制数组
  • [ ] 你能创建和遍历二维数组,理解 arr.lengtharr[i].length 的区分
  • [ ] 你会用 Arrays.sortArrays.binarySearchArrays.fillArrays.toStringArrays.equals
  • [ ] 你警惕数组索引越界、空指针、引用拷贝陷阱

❓ 常见卡点

"为什么数组索引不能从 1 开始?"

arr[i] 本质上是 *(arr + i)——从基地址偏移 i 个元素。从 1 开始就得 *(arr + (i - 1)),多一次减法运算。当年 CPU 资源珍贵,习惯就好。

"我赋值 arr[arr.length] 为啥报错?"

最后一个合法索引是 arr.length - 1arr.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[] namesint[] agesint[] scores 硬对应。

这太原始了。三个数组各自排好序,人工确保索引对齐。一个排序操作就让三个数组散架。

下一章,我们将从"数组"跃入 面向对象 的世界。你会学会把一个人的所有信息打包成一个"对象",一条街上住的不再是孤零零的整数,而是活生生的人。

准备好迎接 类与对象 了吗?

Built with VitePress | Software Systems Atlas