☕ 第二章 Java编程基础

2.7 数组

📄 一句话概括

数组 = 一排带编号的同类型格子,一个名字管全部。编号(索引)从 0 开始;配着 循环 使用,"对每个格子做同一件事"从此只要三行。

前置知识:2.2 Java中的变量(格子思想)、2.5 循环结构语句(遍历靠它)、2.6 方法(数组能整包传进方法)。


1. 为什么需要数组

记全班 40 人的成绩,不用数组得写 40 个变量:score1、score2……命名累、求和更没法写(总不能手写 40 个加号)。数组开一排格子共用一个名字,用编号区分——配合循环,40 个格子统一处理只要三行。

2. 定义与初始化:两种开法

// ① 动态初始化:先定死格数,后填内容(没填的格子有默认值)
int[] scores = new int[5];       // 5 个格子,全是默认值 0
scores[0] = 90;
scores[1] = 85;

// ② 静态初始化:开格子的同时就把数据放进去(格数自动算出来)
int[] arr = {90, 85, 77, 92, 60};

动态初始化的默认值(没填也大概率猜得到,考试常考):

类型 默认值
int / byte / short / long 0
double / float 0.0
boolean false
char '\u0000'(空白字符)
String 等引用类型 null("什么都没有")
💡 两种写法怎么选

一开始就知道装什么 → 静态 {90, 85, ...};先知道要几个格子、数据后面再填(比如等着用户输入)→ 动态 new int[5]。

3. 访问与赋值:靠编号

int[] arr = {90, 85, 77, 92, 60};

System.out.println(arr[0]);   // 90 —— 第 1 个,编号 0
System.out.println(arr[2]);   // 77 —— 第 3 个,编号 2
arr[4] = 95;                  // 把最后一格的 60 改成 95

数组三个刻在骨头上的规则:

  1. 编号从 0 开始:第 1 个是 arr[0],第 n 个是 arr[n-1](上面那张图)
  2. arr.length 是格数(是个变量不是方法,不加括号):5 个格子 length = 5,最大编号 4
  3. 越界必炸:arr[5] 编译能过、运行时报 ArrayIndexOutOfBoundsException——5 个格子的最大编号是 4
📝 顺带补一块内存拼图

变量 arr 本身不住数据——它存在栈里,存的是"这一排格子住在堆里哪"的地址(1.4 提过堆和栈,这里终于见到了)。所以 arr 传给别人时传的是地址,不是复制一整排数据——这个细节第五章面向对象还会回头讲。

4. 遍历:数组 + 循环 = 天生一对

int[] arr = {90, 85, 77, 92, 60};

for (int i = 0; i < arr.length; i++) {     // 注意是 <,不是 <=
    System.out.println("第" + (i + 1) + "个:" + arr[i]);
}

固定三件套:i 从 0 起、i < arr.length(不越界)、arr[i] 取值。这个模板会写,数组的日常就拿下了一半。

5. 三个经典算法(数组的"会做三件事")

// ① 求和(2.5 的累加器搬过来)
int sum = 0;
for (int i = 0; i < arr.length; i++) {
    sum += arr[i];
}
System.out.println("总分:" + sum);              // 404

// ② 求最大值(擂台赛:第一个先站上擂台,后面挨个挑战)
int max = arr[0];
for (int i = 1; i < arr.length; i++) {     // 从 1 开始,0 号已经在擂台上
    if (arr[i] > max) {
        max = arr[i];
    }
}
System.out.println("最高分:" + max);            // 92

// ③ 逆序输出(倒着走一遍)
for (int i = arr.length - 1; i >= 0; i--) {
    System.out.println(arr[i]);
}

求最大值的擂台思路比代码本身值钱:设 max = arr[0](不是 0!全负数的数组会出错),逐个比、谁大谁上擂台。

6. 数组传进方法:整包带走

public static int total(int[] nums) {     // 形参写"类型[] 名字"
    int sum = 0;
    for (int i = 0; i < nums.length; i++) {
        sum += nums[i];
    }
    return sum;
}

// 调用:int s = total(arr);  —— 一整个数组整包传进去

2.6 说"传参是复制"对数组也成立——但复制的是地址,方法里改 nums[0],外面的 arr[0] 真的会变(拿到的是同一排格子的钥匙)。这和 2.6 的 int 传值不一样,先记住现象,原理第五章收网。

7. 动手:Demo08 三件事全做一遍

第 1 步:IDEA02 项目 → src → New → Java Class → Demo08,psvm 生成主方法。

第 2 步:抄入:

public class Demo08 {
    public static void main(String[] args) {
        int[] arr = {90, 85, 77, 92, 60};

        int sum = 0;
        int max = arr[0];
        for (int i = 0; i < arr.length; i++) {
            System.out.println("arr[" + i + "] = " + arr[i]);
            sum += arr[i];
            if (arr[i] > max) {
                max = arr[i];
            }
        }
        System.out.println("总分:" + sum);        // 404
        System.out.println("最高分:" + max);      // 92
    }
}

第 3 步:绿三角 ▶ 运行,核对。

第 4 步(两个 30 秒实验):

  1. 把 i < arr.length 改成 i <= arr.length——跑一次,亲眼看越界异常长什么样(然后改回来)。见过一次,以后报这个错三秒定位
  2. 把 arr 换成 new int[5] 再打印——看 5 个 0(动态初始化的默认值)

8. 常见误区小结

⚠️ 误区 1:以为第 1 个是 arr[1]

从 0 开始。{90, 85, 77} 的 90 是 arr[0]。5 个格子的最大编号是 4,不是 5。

⚠️ 误区 2:length 后面加括号

arr.length 是变量(不加括号);arr.length() 是方法写法——数组不这么用,混了直接编译错。String 的 length() 才是方法,别记串。

⚠️ 误区 3:遍历边界写成 `i <= arr.length`

最后一圈拿 arr[length] 越界,运行时炸。条件永远是 <。

⚠️ 误区 4:求最大值时 `max = 0` 起步

数据全是负数时结果是错的 0。正确姿势 max = arr[0],从擂台上已有的选手开始比。

⚠️ 误区 5:静态和动态混着写

int[] arr = new int[5]{1,2,3,4,5}; 非法——new 带了格数就不能再列数据。要么 new int[5] 空着填,要么 {...} 直接口袋装满。


9. 自测题

❓ Q1:`int[] arr = {10, 20, 30, 40};`,`arr[1]` 和 `arr[arr.length - 1]` 各是多少?

arr[1] 是 20(编号 1 = 第 2 个);arr[arr.length - 1] = arr[3] = 40(length 是 4,减 1 正好是最后一个)——"取最后一格"的万能写法,背下来。

❓ Q2:`new double[3]` 的三个格子各是多少?`new boolean[2]` 和 `new String[2]` 呢?

double:0.0 三个;boolean:false;String:null(不是空字符串 ""——null 是"格子根本没指向任何东西",调它的方法会炸 NullPointerException)。

❓ Q3:求最大值为什么用 `max = arr[0]` 而不是 `max = 0`?

数据可能全是负数(如 {-5, -2, -9}):用 0 起步,谁都"大不过"0,答案错成 0;用 arr[0] 起步,比的一直是数组里的真成员,永远正确。

❓ Q4:`int[] arr = new int[5];` 然后 `arr[5] = 100;`——编译能过吗?运行呢?

编译能过、运行炸:ArrayIndexOutOfBoundsException。编译器只查语法,不查编号超没超(编号可以是变量,它算不过来);运行到这行才真去摸内存,摸空了就报错。

❓ Q5:方法 `total(int[] nums)` 里改 `nums[0] = 999;`,调用方(外面)的 `arr[0]` 会变吗?和 2.6 里 `int` 传参的行为一样吗?

会变——和 int 不一样。int 传的是值的复制(两套格子),数组传的是地址的复制(两把同一排格子的钥匙),改的是同一个堆里的数据。原理(引用类型)第五章收网,先记住现象。


10. 下一步学什么


11. 相关笔记