4.1 数组:一组相同类型的数据
数组是"一排格子",格子编号从 0 开始,长度一旦确定就不能改。
// ① 声明并开辟空间(元素取默认值)
int[] a = new int[5]; // 5 个 int,默认值全是 0
String[] s = new String[3]; // 默认值全是 null
boolean[] f = new boolean[2]; // 默认值全是 false
// ② 声明时直接赋初值(长度由元素个数决定)
int[] b = {3, 1, 4, 1, 5}; // 长度 5
int[] c = new int[]{3, 1, 4}; // 等价写法
// ③ 访问与遍历
System.out.println(b[0]); // 第一个元素:3
System.out.println(b.length); // 长度:5(length 是属性,不是方法)
for (int i = 0; i < b.length; i++) {
System.out.print(b[i] + " ");
}
for (int v : b) { // for-each:只读遍历,拿不到下标
System.out.print(v + " ");
}
三条必记
- 下标范围
0 ~ length-1,越界抛ArrayIndexOutOfBoundsException(最常见的新手异常)。 - 数组长度不可变;要"变长"就用集合(第 10 章)或
Arrays.copyOf造一个新数组。 - 数组是引用类型:
int[] x = y;是让 x 和 y 指向同一块数据,改 x[0] 会影响 y[0]。
二维数组(数组的数组)
int[][] m = new int[3][4]; // 3 行 4 列,共 12 个 int
System.out.println(m.length); // 3(行数)
System.out.println(m[0].length); // 4(第 0 行的列数)
int[][] tri = new int[3][]; // 「不规则数组」:先定行数,每行长度可不同
tri[0] = new int[1];
tri[1] = new int[2];
tri[2] = new int[3];
// 遍历二维数组
for (int i = 0; i < m.length; i++) {
for (int j = 0; j < m[i].length; j++) {
System.out.print(m[i][j] + " ");
}
System.out.println();
}
课堂练习(3 题)
int[] a = new int[4];中 a 的元素默认值是多少?String[] s = new String[2];呢? 答:a 元素默认 0;s 元素默认 null(引用类型默认 null)。- 访问
a[a.length]会发生什么? 答:越界,抛出 ArrayIndexOutOfBoundsException(最后一个合法下标是 length-1)。 - 二维数组
int[][] m = new int[2][3];一共有几个元素?m.length是多少? 答:共 2×3=6 个元素;m.length 是 2(行数)。
4.2 Arrays 工具类(排序、查找、拷贝)
写题必用,记住这几个方法能省一半代码。
import java.util.Arrays;
int[] a = {5, 2, 9, 1};
Arrays.toString(a) // "[5, 2, 9, 1]" 直接打印要转成字符串,否则打印地址
Arrays.sort(a) // 原地升序排序 → a 变成 [1, 2, 5, 9]
Arrays.binarySearch(a, 5) // 二分查找,返回下标(找不到返回负数)★ 必须先排序
Arrays.fill(a, 0) // 全部填 0
Arrays.copyOf(a, 6) // 复制并扩容到长度 6,多出的位置补 0
Arrays.equals(a, b) // 比较两个数组内容是否相同(不能用 == 比数组!)
易错
- 直接
System.out.println(a)打印的是地址(类似[I@1b6d3586),要打印内容用Arrays.toString(a);二维数组用Arrays.deepToString(m)。 - 数组比较用
Arrays.equals,==比的是地址(两个内容相同的数组用 == 也是 false)。 binarySearch必须在已排序的数组上用,否则结果不可信。
课堂练习(2 题)
- 打印数组内容为什么要套一层
Arrays.toString()? 答:数组是引用类型,直接打印输出的是哈希地址(如 [I@1b6d3586),转成字符串才能看到元素。 - 把数组
{3,1,2}排序后查找 2 的下标,两步怎么写? 答:Arrays.sort(a); int idx = Arrays.binarySearch(a, 2);(排序后 a=[1,2,3],idx=1)。
4.3 方法:定义、重载与参数传递
"值传递"这一条是所有 Java 考试都要考的。
// 定义:修饰符 返回值类型 方法名(参数列表) { 方法体 }
public static int add(int x, int y) {
return x + y; // 有返回值就必须 return 对应类型的值
}
public static void printHi() { // void 表示不返回值
System.out.println("hi");
// return; 可省略
}
// 调用
int sum = add(3, 5);
printHi();
方法重载(Overload)
- 同一个类里方法名相同、参数列表不同(个数、类型或顺序不同)。
- 与返回值类型无关:只有返回值不同不算重载,会编译报错。
static int add(int a, int b) { return a + b; }
static double add(double a, double b) { return a + b; }
static int add(int a, int b, int c) { return a + b + c; } // 三个都是重载
add(1, 2); // 调 int 版
add(1.5, 2.5); // 调 double 版
参数传递:值传递(重点)
static void change(int n) { n = 100; } // 改的是副本
static void change(int[] arr) { arr[0] = 100; } // 改的是同一块数组
int x = 10;
change(x); // x 仍是 10(基本类型:传副本,改不动原值)
int[] a = {1, 2, 3};
change(a); // a[0] 变成 100(引用类型:传地址副本,能改到对象内容)
- Java 只有值传递:基本类型传"值的副本",引用类型传"地址的副本"——所以改对象内容会生效,但让引用指向新对象不会生效。
可变参数与递归
// 可变参数:本质是数组,只能放参数列表最后一个
static int sum(int... nums) {
int s = 0;
for (int n : nums) s += n;
return s;
}
sum(1, 2, 3); // 6
sum(); // 0(可以不传)
// 递归:方法自己调自己,必须有终止条件
static long fact(int n) {
if (n <= 1) return 1; // 终止条件,缺了就栈溢出
return n * fact(n - 1);
}
易错
- 方法内定义的变量是局部变量,没有默认值,不赋值就用会编译报错。
- 递归必须有终止条件且每次向终止靠近,否则
StackOverflowError。 static方法里不能直接调用非 static 成员(第 6 章详讲)。
课堂练习(3 题)
- 判断:
int f(int a)和double f(int a)构成重载吗? 答:不构成,重载只看参数列表;仅返回值不同会报"方法已定义"。 - 下面代码执行后 x 的值是多少?
int x=1; change(x);(change 里 n=100) 答:x 仍是 1。基本类型参数传的是副本,方法内改动不影响实参。 - 递归求阶乘,n=0 时应该返回什么?为什么? 答:返回 1(0! = 1,且作为递归终止条件)。
4.4 综合示例:成绩统计
把数组 + 方法 + 遍历串起来,建议自己在沙盒里敲一遍。
import java.util.Arrays;
public class ScoreStat {
// 求最大值
static int max(int[] a) {
int m = a[0];
for (int v : a) if (v > m) m = v;
return m;
}
// 求平均值(注意:要先转浮点再除)
static double avg(int[] a) {
int s = 0;
for (int v : a) s += v;
return s * 1.0 / a.length;
}
// 统计及格人数
static int passCount(int[] a) {
int c = 0;
for (int v : a) if (v >= 60) c++;
return c;
}
public static void main(String[] args) {
int[] scores = {72, 55, 88, 61, 90};
System.out.println("成绩:" + Arrays.toString(scores));
System.out.println("最高分:" + max(scores));
System.out.println("平均分:" + avg(scores));
System.out.println("及格人数:" + passCount(scores));
Arrays.sort(scores);
System.out.println("排序后:" + Arrays.toString(scores));
}
}
这段代码的三个好习惯
- 一个方法只做一件事(max/avg/passCount 分开),比全塞进 main 好读好改。
- 求平均值用
s * 1.0 / 长度,避免整数除法截断(第 2 章的坑)。 - 方法参数用数组,让同一段逻辑能处理任意长度的数据。
复制到 Java 在线运行环境 直接跑,试试改成绩或加个"最低分"方法。
课堂练习(2 题)
- 在上面程序里再加一个方法
min(int[] a),思路是什么? 答:与 max 对称——取 a[0] 为初始最小值,遍历中遇到更小就替换,最后返回。 avg方法里如果写成s / a.length会怎样? 答:整数除法截断,例如总分 366/5 得到 73 而不是 73.2(返回 double 也只是 73.0)。