跳到主要内容

创建日期: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

最容易忘的三连

  1. 泛型 <> 里只能放,不能放基本类型:List<Integer>(不是 List<int>)——靠自动装箱把 int 变 Integer;
  2. Set 去重依赖元素的 equals/hashCode(所以上一篇让你两个一起覆写);
  3. Mapput/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)NullPointerExceptionArrayIndexOutOfBoundsArithmeticException不用提前声明,多数是程序 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数组下标越界
IndexOutOfBoundsExceptionList 越界
NumberFormatException把非数字字符串 Integer.parseInt("abc")
ClassCastException强转成不对的类型
IllegalArgumentException方法参数不合法(你自己 throw 的常用它)
IOException读写文件/网络失败(受检)

NPE 预防:不确定是不是 null 就判一下 if (x != null);或方法契约里写明「不允许传 null」。

动手 3 分钟

写一个 统计成绩:用 Map<String,Integer> 存 3 门课,遍历打印总分与平均;对 Integer.parseInt("abc") 做 try/catch 打印友好提示。

自测

  1. List/Set/Map 分别管什么?去重用哪个?
  2. 为什么 List<int> 报错?
  3. String 循环拼接为什么慢?换什么?
  4. 受检异常和运行时异常谁必须处理?
  5. finally 一定执行吗?try-with-resources 帮你做了什么?