创建日期:2026-09-08 | 最近更新:2026-09-08 主线 JDK 11(本机实测可跑)。
Java 复习手册 4:集合框架与异常处理
现实里你几乎不会用「固定长度数组」,而是用集合(能增删);代码不可能不出错,所以要有异常。这篇学完,你的 Java 就能写「正常项目」了。
1. 三个必须会的集合(全部要写泛型 <类型>)
import java.util.*;
// List:有序可重复,能按下标取(增删多用它)
List<String> names = new ArrayList<>();
names.add("张三"); // 追加
names.add(0, "李四"); // 插到 0 位
names.get(0); // 按下标取
names.size(); // 长度(不是 length!)
names.contains("张三"); // 是否包含
names.remove("张三"); // 按内容删
names.get(names.size() - 1); // 最后一位
// Set:无序不重复(去重神器)
Set<Integer> set = new HashSet<>();
set.add(1); set.add(1); set.add(2);
// set.size() == 2(第二个 1 没进去)
// Map:键值对(其它语言的 object/字典)
Map<String, Integer> scores = new HashMap<>();
scores.put("语文", 90);
scores.put("数学", 85);
scores.get("语文"); // 90
scores.getOrDefault("英语", 0); // 没有就返回默认值 0
scores.containsKey("数学"); // true
最容易忘的三连:
- 泛型
<>里只能放类,不能放基本类型:List<Integer>(不是List<int>)——靠自动装箱把 int 变 Integer; Set去重依赖元素的equals/hashCode(所以上一篇让你两个一起覆写);Map用put/get,不是add。
2. 遍历三件套
// List / Set:增强 for
for (String n : names) { System.out.println(n); }
// Map:遍历 entry(键值对)
for (Map.Entry<String, Integer> e : scores.entrySet()) {
System.out.println(e.getKey() + "=" + e.getValue());
}
// 全部支持:forEach + lambda(JDK8+)
names.forEach(n -> System.out.println(n));
scores.forEach((k, v) -> System.out.println(k + "=" + v));
lambda
(参数) -> 表达式就是匿名函数;scores.forEach((k,v) -> ...)这里的k,v是遍历时自动传进来的键值。先会读,再会写。
3. 大量拼接字符串:用 StringBuilder
String 不可变,循环里 s += x 每次都是新建字符串 → 慢。循环拼接用:
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100; i++) {
sb.append(i).append(","); // 追加,不新建对象
}
String result = sb.toString();
4. 异常:代码出错时的「电梯报警」
Java 让「错误处理」结构化——出错时抛异常,调用方可以选择捕获或继续抛。
// 可能抛异常的代码,包进 try,出错走 catch,finally 一定执行
try {
int r = 10 / 0; // 抛 ArithmeticException
System.out.println("这行不会执行");
} catch (ArithmeticException e) {
System.out.println("数学异常:" + e.getMessage()); // / by zero
} finally {
System.out.println("无论成不成功都执行(如关资源)");
}
两种异常,记这一个区别就行
| 例子 | 要不要处理 | |
|---|---|---|
| 受检异常(checked) | 读文件 IOException | 编译器逼你 try/catch 或方法 throws |
| 非受检/运行时异常(unchecked) | NullPointerException、ArrayIndexOutOfBounds、ArithmeticException | 不用提前声明,多数是程序 bug |
方法声明「我可能抛这个异常」用 throws(调用方就得处理或继续抛):
public String readFile(String path) throws IOException {
// 不在这里处理,让调用方处理
return new String(Files.readAllBytes(Path.of(path)));
}
关闭资源的现代姿势:try-with-resources
凡是要关闭的东西(文件、流、连接),用这个——自动帮你关:
try (var reader = Files.newBufferedReader(Path.of("a.txt"))) {
System.out.println(reader.readLine());
} // 不用 finally 手动关,自动 close
抛自己的异常 & 处理策略
if (amount <= 0) {
throw new IllegalArgumentException("金额必须 > 0"); // 抛运行时异常,最常见
}
// 要自定义:extends RuntimeException(多数场景够用)
public class NotEnoughBalanceException extends RuntimeException {
public NotEnoughBalanceException(String msg) { super(msg); }
}
实战建议:业务上「不该发生的事」直接抛运行时异常(IllegalArgumentException 等),别吞异常(catch 后啥也不干是最坏的写法),要记日志或转成有意义的错误。
5. 一页「高频异常速查」
| 异常 | 什么时候发生 |
|---|---|
NullPointerException(NPE) | 在 null 上调方法/取字段(最最常见) |
ArrayIndexOutOfBoundsException | 数组下标越界 |
IndexOutOfBoundsException | List 越界 |
NumberFormatException | 把非数字字符串 Integer.parseInt("abc") |
ClassCastException | 强转成不对的类型 |
IllegalArgumentException | 方法参数不合法(你自己 throw 的常用它) |
IOException | 读写文件/网络失败(受检) |
NPE 预防:不确定是不是 null 就判一下
if (x != null);或方法契约里写明「不允许传 null」。
动手 3 分钟
写一个 统计成绩:用 Map<String,Integer> 存 3 门课,遍历打印总分与平均;对 Integer.parseInt("abc") 做 try/catch 打印友好提示。
自测
List/Set/Map分别管什么?去重用哪个?- 为什么
List<int>报错? String循环拼接为什么慢?换什么?- 受检异常和运行时异常谁必须处理?
finally一定执行吗?try-with-resources 帮你做了什么?