钩子
任务:存一个班 100 个人的成绩。
int score1; int score2; int score3; ... 写到第 20 个你自己就崩了。变量适合存"一个"数据,装"一批"同类型数据,Java 给的工具是:数组。
顺便预告一下,今天还是你和新朋友见面的日子——ArrayIndexOutOfBoundsException,江湖人称新手第一异常。学完这篇,你不仅认识它,还知道它为什么来。
01 数组:一排编了号的格子
数组 = 同一种类型的数据,排成一排,每个格子有个编号(下标)。
// 静态初始化:创建时就把值定好
int[] scores = {92, 87, 65, 100, 78};
// 动态初始化:先开 3 个格子,值以后再填
int[] arr = new int[3];
两个必须知道的规矩:
下标从 0 开始。上面 scores 里,scores[0] 是 92,scores[4] 是 78。第一个是 0 号不是 1 号——这是无数 bug 的源头,现在就刻进脑子。
动态初始化的格子不是"空"的。new int[3] 开出来的三个格子默认是 0;如果是 String[],默认是 null。不是什么都没有,是有默认值。

02 遍历:数组 + 循环才是完整形态
上篇刚学的循环,这里立刻派上用场:
int[] scores = {92, 87, 65, 100, 78};
for (int i = 0; i < scores.length; i++) {
System.out.println(scores[i]);
}
注意是 scores.length——不带括号。但字符串取长度是 "abc".length()——带括号。
为什么?数组是"格子"本身,length 是它身上一个写死的"属性";String 是类,length() 是它提供的一个"功能"。现在记结论,为什么这样设计,第 13 篇讲面向对象就懂了。新手期写错编译器会提醒,但知道原因的人不慌。
另外推荐一种更稳的写法——增强 for,不用碰下标:
for (int s : scores) {
System.out.println(s);
}
"挨个取出 scores 里的每个值给 s"——不需要写 i,自然也不会越界。
03 越界:新手第一异常登场
int[] arr = {92, 87, 65};
System.out.println(arr[3]);
编译没问题,一运行直接炸:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
用第 6 篇的三步定位法读它:Index 3 out of bounds for length 3——你要 3 号格子,但人家总共只有 0、1、2 三个。人话:下标最大只能到 length - 1。
这个异常的价值在于它是"运行时才炸"的:编译器不拦你,写的时候 IDEA 可能都不标红,一运行才报错。所以养成习惯:拿不准长度时,用 length 做边界,永远别手写数字。i < scores.length 就是免疫越界的写法。
04 栈和堆:new 出来的东西住哪
int[] scores = new int[5]; 这一行,其实发生在两个地方:
scores这个变量住在栈里,里面存的不是数据,是一个地址;new int[5]开出来的那 5 个格子住在堆里,scores 里的地址指向它们。
所以数组变量之间赋值,传的是地址不是数据:
int[] a = {1, 2, 3};
int[] b = a; // b 和 a 指向同一排格子
b[0] = 100;
System.out.println(a[0]); // 100!a 也变了
现在看不懂没关系,混个脸熟——这是面向对象的大门,第 13 篇会从这里正式讲起。
本文示例代码均已本地实测通过,可直接运行。
对应文件:ArrayDemo.java
橙码酱,分享更有用的技术知识,助你成为更优秀的开发者。