- 0
- 545 words
开篇引子——为什么需要数组?
想象一下:你要存班上 50 个人的成绩。没有数组的话——
int score1 = 85;
int score2 = 92;
int score3 = 67;
// ... 写 50 行?
int score50 = 78;
而想求个平均分?手写 score1 + score2 + ... + score50?写到第四十个你就怀疑人生了。这就是数组存在的理由——把同类型数据组织起来,用下标统一访问。
一、一维数组的定义
1.1 声明和初始化
Java 里数组是一个引用类型,有固定的长度,一旦创建就不能改变。这是它和集合类的最大区别。
// 方式一:声明 + 分配空间
int[] scores = new int[5];
// scores[0]~scores[4] 默认都是 0
// 方式二:声明 + 直接赋值
int[] scores2 = {85, 92, 67, 78, 90};
// 方式三:先声明,再 new
int[] scores3;
scores3 = new int[]{85, 92, 67, 78, 90}; // 注意这里不能省略 new int[]
// 不推荐的写法(c语言风格,Java也支持但别这么写)
int scores4[] = new int[5];
三种声明方式的区别:
| 方式 | 使用场景 |
|---|---|
new int[5] | 先占位,后面再逐个赋值 |
{85, 92, ...} | 创建时就确定所有值 |
new int[]{85, 92, ...} | 变量声明和赋值分两行,或者需要传参 |
数组的长度的获取用 数组名.length,注意不是方法,是属性。
int[] arr = {10, 20, 30, 40, 50};
System.out.println(arr.length); // 5
1.2 默认值
只 new 了空间但不赋值的话,元素有默认值:
| 类型 | 默认值 |
|---|---|
| byte/short/int/long | 0 |
| float/double | 0.0 |
| char | ‘u0000’(空字符) |
| boolean | false |
| 引用类型(String、对象等) | null |
这个默认值机制挺方便的,比如你统计某个范围内数字出现的次数,整型数组一创建全是 0,可以直接自增。
二、数组的遍历
遍历就是”把数组每个元素过一遍”。Java 提供了两种方式:
2.1 for 循环(下标遍历)
int[] scores = {85, 92, 67, 78, 90};
for (int i = 0; i < scores.length; i++) {
System.out.println("第" + (i + 1) + "个成绩:" + scores[i]);
}
这是最经典的遍历方式。优点:能拿到下标,方便修改元素。缺点:写法啰嗦了点。
2.2 增强 for 循环(foreach)
for (int score : scores) {
System.out.println(score);
}
Java 5 引入的语法糖。优点:代码简洁、不会有下标越界问题。缺点:拿不到下标,也不能修改数组元素(修改的是临时变量)。
2.3 用 Arrays.toString() 快速打印
调试时最常用的方式:
import java.util.Arrays;
int[] scores = {85, 92, 67, 78, 90};
System.out.println(Arrays.toString(scores));
// 输出:[85, 92, 67, 78, 90]
千万别直接打印数组变量:
System.out.println(scores);
// 输出:[I@1b6d3586 ← 哈希地址,不是内容!
💡 我在大学上 Java 课时,第一次打印数组直接传了变量名,输出一串看不懂的东西,以为程序出 bug 了……后来同学告诉我要用 Arrays.toString()。如果你也踩过这个坑,你不是一个人。
三、数组的常见操作
3.1 求最大值和最小值
int[] arr = {5, 3, 9, 1, 7, 6};
// 求最大值
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
System.out.println("最大值:" + max); // 9
// 求最小值同理,把 > 换成 <
int min = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] < min) {
min = arr[i];
}
}
System.out.println("最小值:" + min); // 1
注意:初始值设为 arr[0],不要设成 0 或 Integer.MAX_VALUE——万一数组里全是负数,max = 0 就错了。
3.2 求和与平均值
int[] scores = {85, 92, 67, 78, 90};
int sum = 0;
for (int score : scores) {
sum += score;
}
double avg = (double) sum / scores.length;
System.out.println("总分:" + sum); // 412
System.out.println("平均分:" + avg); // 82.4
这里要把 sum 强转成 double,否则两个 int 相除结果会被截断为整数。
比如 412 / 5 = 82(而不是 82.4),这坑当年我也踩过。
3.3 查找元素
int[] arr = {5, 3, 9, 1, 7, 6};
int target = 7;
int index = -1; // -1 表示未找到
for (int i = 0; i < arr.length; i++) {
if (arr[i] == target) {
index = i;
break; // 找到了就退出,别继续白找
}
}
if (index != -1) {
System.out.println(target + " 在位置 " + index);
} else {
System.out.println(target + " 不在数组中");
}
这是线性查找,最朴素的查找方式。对于无序数组这是唯一的选择;对于有序数组,后面会学到二分查找,效率更高。
3.4 数组元素反转
int[] arr = {1, 2, 3, 4, 5};
for (int i = 0, j = arr.length - 1; i < j; i++, j--) {
// 交换 arr[i] 和 arr[j]
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
System.out.println(Arrays.toString(arr)); // [5, 4, 3, 2, 1]
关键点:只遍历一半,用两个指针从两端向中间走。i < j 保证了奇数长度时中间元素不动。
3.5 数组拷贝
int[] src = {1, 2, 3, 4, 5};
// 方式一:for 循环手动拷贝
int[] dest1 = new int[src.length];
for (int i = 0; i < src.length; i++) {
dest1[i] = src[i];
}
// 方式二:System.arraycopy()(原生方法,效率最高)
int[] dest2 = new int[src.length];
System.arraycopy(src, 0, dest2, 0, src.length);
// 方式三:Arrays.copyOf()(最方便)
int[] dest3 = Arrays.copyOf(src, src.length);
// 方式四:clone()
int[] dest4 = src.clone();
几个 copyOf 的妙用:
// 截取前3个元素
int[] part1 = Arrays.copyOf(src, 3); // [1, 2, 3]
// 扩展长度(多余位置补0)
int[] expanded = Arrays.copyOf(src, 7); // [1, 2, 3, 4, 5, 0, 0]
// 复制指定范围(from 包含,to 不包含)
int[] range = Arrays.copyOfRange(src, 1, 4); // [2, 3, 4]
3.6 冒泡排序(入门级排序)
排序算法是面试常客,但作为初学者,先掌握冒泡排序就够了——它最直观,也最容易理解排序的”比较-交换”思想。
int[] arr = {5, 3, 9, 1, 7};
// 升序排列
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
System.out.println(Arrays.toString(arr)); // [1, 3, 5, 7, 9]
原理图解:每一轮把当前未排序部分的最大值”冒泡”到最后。arr.length - 1 - i 表示每轮比较次数递减——最后面的元素已经排好了,不需要再比。
四、常见陷阱和注意事项
4.1 数组下标越界
数组下标从 0 开始,到 length - 1 结束。访问 arr[arr.length] 会抛出 ArrayIndexOutOfBoundsException——这是初学者最常见的异常之一。
int[] arr = {1, 2, 3};
System.out.println(arr[3]); // ❌ 运行报错!下标最大是 2
4.2 空指针访问
int[] arr = null;
System.out.println(arr.length); // ❌ NullPointerException
// 数组是引用类型,不 new 就是 null,调 length 会崩
4.3 foreach 里不能修改原数组
int[] arr = {1, 2, 3};
for (int x : arr) {
x = 100; // ❌ 这只是改了一个临时变量,原数组没变
}
System.out.println(Arrays.toString(arr)); // 还是 [1, 2, 3]
想修改数组元素,必须用下标 for 循环。
五、综合示例:学生成绩管理系统(简易版)
把今天学的操作串起来,写一个小程序:
import java.util.Arrays;
public class ScoreManager {
public static void main(String[] args) {
// 5 个学生的成绩
int[] scores = {76, 88, 62, 95, 81};
// 1. 打印原始成绩
System.out.println("原始成绩:" + Arrays.toString(scores));
// 2. 统计
int sum = 0;
int max = scores[0];
int min = scores[0];
for (int score : scores) {
sum += score;
if (score > max) max = score;
if (score < min) min = score;
}
double avg = (double) sum / scores.length;
System.out.println("总分:" + sum);
System.out.println("平均分:" + avg);
System.out.println("最高分:" + max);
System.out.println("最低分:" + min);
// 3. 查找 95 分的位置
int target = 95;
int index = -1;
for (int i = 0; i < scores.length; i++) {
if (scores[i] == target) {
index = i;
break;
}
}
System.out.println(target + " 分在第 " + (index + 1) + " 位");
// 4. 按成绩从低到高排序
int[] sorted = Arrays.copyOf(scores, scores.length);
Arrays.sort(sorted); // 直接用工具类排序
System.out.println("按成绩排序:" + Arrays.toString(sorted));
// 5. 找出不及格(低于 60 分)的人数
// 这里没有不及格的,但方法学会了
int failCount = 0;
for (int score : scores) {
if (score < 60) failCount++;
}
System.out.println("不及格人数:" + failCount);
}
}
运行结果:
原始成绩:[76, 88, 62, 95, 81]
总分:402
平均分:80.4
最高分:95
最低分:62
95 分在第 4 位
按成绩排序:[62, 76, 81, 88, 95]
不及格人数:0
总结
- 数组是长度固定的同类型数据容器,下标从 0 开始,访问越界会抛异常
- 声明方式有
int[] arr = new int[n]、{值1, 值2}、new int[]{值1, 值2}三种 - 遍历用 for 下标循环(能修改元素)或 foreach(只读)
- 常用操作:求和、求最值、查找、反转、拷贝、排序
- 工具类
Arrays.toString()和Arrays.sort()让日常操作简洁很多
下一篇我们聊多维数组和 Arrays 工具类的更多用法,还会讲到可变参数——到时候你会发现很多操作根本不需要手写循环了。