第6章:数组
本章只讲 Java。Python/C++ 的数组和集合在末尾统一对比。
一个故事开头
你今天做了 5 杯奶茶:波波奶茶、杨枝甘露、柠檬茶、抹茶拿铁、红豆奶茶。
如果你用变量来记,你得写:
String drink1 = "波波奶茶";
String drink2 = "杨枝甘露";
String drink3 = "柠檬茶";
String drink4 = "抹茶拿铁";
String drink5 = "红豆奶茶";如果有 100 杯呢?声明 100 个变量?先不说懒不懒,你甚至没法把"这组数据"当做一个整体来操作——比如遍历一遍算总价。
数组(Array)就是解决这个问题的——把一堆同类型的数据排成一排,编上号,用一个名字来管理。
String[] drinks = {"波波奶茶", "杨枝甘露", "柠檬茶", "抹茶拿铁", "红豆奶茶"};
// 编号: 0 1 2 3 41. 数组长什么样
// 声明 + 赋值一步到位
int[] scores = {85, 90, 78, 92, 88};数组的好处是:所有元素通过编号(索引)访问,用循环遍历。
int[] scores = {85, 90, 78, 92, 88};
for (int i = 0; i < 5; i++) {
System.out.println("第" + (i + 1) + "个成绩: " + scores[i]);
}2. 创建数组的三种方式
方式1:已知所有元素
int[] numbers = {10, 20, 30, 40, 50};方式2:先定长度,再逐个赋值
int[] numbers = new int[5]; // 创建长度为5的数组,默认值都是0
numbers[0] = 10;
numbers[1] = 20;
numbers[2] = 30;
numbers[3] = 40;
numbers[4] = 50;方式3:分开声明和赋值
int[] numbers; // 声明
numbers = new int[]{10, 20, 30}; // 赋值(不能省略 new int[])3. 索引从 0 开始
这是新手最容易搞错的地方。Java 数组的索引从 0 开始,不是 1。
String[] drinks = {"波波奶茶", "杨枝甘露", "柠檬茶"};
// 索引: 0 1 2
System.out.println(drinks[0]); // 波波奶茶
System.out.println(drinks[1]); // 杨枝甘露
System.out.println(drinks[2]); // 柠檬茶
System.out.println(drinks[3]); // ❌ 数组越界!长度为 3,合法索引 0~2💥 拆了它:数组越界
int[] arr = {1, 2, 3};
System.out.println(arr[3]); // ArrayIndexOutOfBoundsException运行结果:程序崩溃,报红色错误。Java 说:"你访问了索引 3,但这个数组只有 3 个元素(索引 0~2)。"
4. length——数组知道自己有多长
int[] scores = {85, 90, 78, 92, 88};
System.out.println(scores.length); // 5
// 用 length 遍历——不用把数字写死
for (int i = 0; i < scores.length; i++) {
System.out.println(scores[i]);
}length 是数组的属性(不是方法,所以没有括号)。
5. 遍历数组
普通 for 循环
int[] scores = {85, 90, 78, 92, 88};
for (int i = 0; i < scores.length; i++) {
System.out.println("索引 " + i + ": " + scores[i]);
}for-each 循环(增强 for)
当你只需要元素值,不需要索引时用:
for (int score : scores) {
System.out.println(score);
}读作:"对于 scores 里的每个 int,把它赋值给 score,然后执行循环体。"
什么时候用哪种
| 场景 | 用哪个 |
|---|---|
| 只需要元素值 | for-each |
| 需要知道索引位置 | 普通 for |
| 要修改元素的值 | 普通 for |
| 倒序遍历 | 普通 for |
6. 数组的默认值
创建数组但没赋值时,Java 会给每个位置一个默认值:
| 类型 | 默认值 |
|---|---|
int[] | 0 |
double[] | 0.0 |
boolean[] | false |
String[](及其他引用类型) | null |
int[] nums = new int[3];
System.out.println(nums[0]); // 0
System.out.println(nums[1]); // 0
System.out.println(nums[2]); // 0
String[] names = new String[3];
System.out.println(names[0]); // null7. 数组的常见操作
找最大值
int[] scores = {85, 90, 78, 92, 88};
int max = scores[0]; // 假设第一个是最大的
for (int i = 1; i < scores.length; i++) {
if (scores[i] > max) {
max = scores[i]; // 找到更大的就更新
}
}
System.out.println("最高分: " + max); // 92求和与平均
int sum = 0;
for (int score : scores) {
sum += score;
}
double avg = (double) sum / scores.length;
System.out.println("平均分: " + avg);增强型数组工具(Java 8+)
import java.util.Arrays;
int[] scores = {85, 90, 78, 92, 88};
Arrays.sort(scores); // 排序(修改原数组)
System.out.println(Arrays.toString(scores)); // 打印数组内容
int total = Arrays.stream(scores).sum(); // 求和
double average = Arrays.stream(scores).average().orElse(0); // 平均8. 二维数组
奶茶店有 3 家分店,每家卖了 5 杯奶茶。用二维数组存:
int[][] sales = {
{15, 12, 18, 10, 14}, // 分店1:5杯的销售额
{20, 22, 19, 15, 17}, // 分店2
{10, 8, 14, 12, 11} // 分店3
};
// 访问
System.out.println(sales[0][2]); // 分店1的第3杯销售额:18
System.out.println(sales[1][4]); // 分店2的第5杯销售额:17
// 双重循环遍历
for (int i = 0; i < sales.length; i++) {
int storeTotal = 0;
for (int j = 0; j < sales[i].length; j++) {
storeTotal += sales[i][j];
}
System.out.println("分店" + (i + 1) + "总销售额: " + storeTotal);
}9. 坑与注意点
数组长度不可变
int[] arr = new int[3];
// arr.length 永远是 3,不能扩容,不能缩容要动态增删?后面第10章讲 ArrayList。
引用类型数组存的是引用
String[] names = new String[2];
names[0] = "小明";
String temp = names[0];
temp = "小张";
System.out.println(names[0]); // 还是"小明",不是"小张"
// String 是不可变的,temp 指向了新对象,不影响原数组for-each 不能修改数组元素
int[] scores = {85, 90, 78};
for (int score : scores) {
score = 100; // 只改了临时变量 score,没改原数组
}
System.out.println(scores[0]); // 还是 85要修改元素内容?用普通 for + 索引。
10. 完整例子:奶茶店日销售统计
import java.util.Arrays;
public class DailySales {
public static void main(String[] args) {
// 一天的销售记录(每杯的销售额)
double[] sales = {15.0, 18.0, 12.0, 15.0, 16.0, 18.0, 12.0};
// 计算总销售额
double total = 0;
for (double sale : sales) {
total += sale;
}
System.out.println("今日总销售额: " + total + "元");
// 计算平均单价
double avg = total / sales.length;
System.out.println("平均单价: " + avg + "元");
// 查找最高单笔
double max = sales[0];
for (int i = 1; i < sales.length; i++) {
if (sales[i] > max) {
max = sales[i];
}
}
System.out.println("最高单笔: " + max + "元");
// 排序
Arrays.sort(sales);
System.out.println("销售额排序: " + Arrays.toString(sales));
// 输出每笔超过 15 元的交易
System.out.println("超过15元的交易:");
for (double sale : sales) {
if (sale > 15) {
System.out.println(" " + sale + "元");
}
}
}
}我猜你会问
Q: 为什么数组索引从 0 开始? A: 因为数组在内存里是连续排列的,数组名就是第一个元素的地址。访问 arr[i] 相当于 地址 + i × 元素大小。从 0 开始计算更高效(省一次减法)。
Q: 数组和后面要学的 ArrayList 有什么区别? A: 数组定长,ArrayList 自动扩容。日常用 ArrayList 更多,但在性能敏感的场景(几百万数据)数组更快。
你现在学会的东西
- 数组 = 同类型数据排成一排,用索引(从 0 开始)访问
- 创建方式三种:
{值列表}、new int[长度]、new int[]{值列表} length告诉你数组多长,遍历时用它,别写死数字- for-each 遍历更简洁,但不能改元素内容
- 数组越界 = 程序崩溃,
ArrayIndexOutOfBoundsException - 数组长度不可变——需要变长容器?学完数组学 ArrayList
✅ 验收标准
完成本章后,你应该能:
- [ ] 用三种方式创建数组并初始化
- [ ] 用索引读写数组元素
- [ ] 用 for-each 遍历数组
- [ ] 声明和遍历二维数组
- [ ] 写出数组求最大/求和等常用操作
📌 常见卡点
- 数组索引从 0 开始——最后一个元素的索引是 length-1
- 数组越界——访问不存在的索引抛出 ArrayIndexOutOfBoundsException
- 数组长度固定——创建后不能增删元素
- 数组变量是引用——赋值是传引用,不是复制
🔜 现在不需要理解
- 多维数组的底层存储布局
- Arrays 工具类的所有方法——用到时查表
- 数组的反射操作——高级话题
🧪 练习
1. 声明数组:声明一个 double[] prices = {12.5, 15.0, 18.0, 10.0},用 for 循环计算总价。
2. 找最大值和最小值:有一个 int[] numbers = {34, 78, 12, 90, 45, 67},找出最大值和最小值。
3. 数组反转:声明 int[] arr = {1, 2, 3, 4, 5},写一个循环把它反转为 {5, 4, 3, 2, 1}。(提示:交换首尾对应的元素)