Java:异常机制(概念、分类、处理、常见异常)
一、什么是异常
在程序运行过程中,阻止程序正常执行的错误情况,就叫做异常。
如果程序出现异常且不处理,会发生两件事:
- 程序直接崩溃终止
- 后面的代码不再执行
public class Demo01 {
public static void main(String[] args) {
System.out.println("第一行");
int a = 10 / 0; // 这里抛异常
System.out.println("第二行"); // 永远不会执行
}
}
运行结果:
第一行
Exception in thread "main" java.lang.ArithmeticException: / by zero
可以看到,"第二行" 没有打印出来——异常一旦抛出,它之后的代码就停了。
异常不等于语法错误
- 语法错误:编译就失败,程序根本跑不起来。比如少写分号、括号不配对。
- 异常:代码语法完全没问题,编译能通过,但运行时出了错。
int a = 10 / 0; // 语法没错,运行才炸 → 异常
int b // 少了分号 → 语法错误,编译不过
二、异常的体系结构
Java 中所有异常都继承自 Throwable。Throwable 下面只有两个直接子类:
Throwable
├── Error (错误:管不了,交给 JVM)
└── Exception (异常:能处理,我们自己管)
├── 编译时异常(受检) 必须处理
└── 运行时异常(非受检) 运行才暴露
1. Error(错误)
非常严重的问题,程序一般处理不了,通常只能让程序终止。
// StackOverflowError:方法一直调自己,栈撑爆了
public static void die() { die(); }
常见的 Error:OutOfMemoryError(内存溢出)、StackOverflowError(栈溢出)。遇到 Error,不要去 catch。
2. Exception(异常)
程序本身可以处理的异常,这是我们要重点掌握的,它又分为两类。
① 编译时异常(受检异常)
编译阶段就会报错,不处理就编不过。
// 编译报错:未处理的异常 IOException
FileInputStream fis = new FileInputStream("a.txt");
典型代表:IOException、SQLException、FileNotFoundException。
② 运行时异常(非受检异常)
编译阶段不报错,运行到才会暴露。
String s = null;
System.out.println(s.length()); // 运行才炸:NullPointerException
典型代表:NullPointerException、ArrayIndexOutOfBoundsException、ArithmeticException。
一句话记住:外部环境惹的祸(文件、网络、数据库)→ 编译时异常;自己代码写错(空指针、越界、除零)→ 运行时异常。
三、异常的处理方式
1. 捕获:try-catch
自己把异常处理掉,程序就不会崩。
public class Demo02 {
public static void main(String[] args) {
try {
int r = 10 / 0;
System.out.println("结果是" + r);
} catch (ArithmeticException e) {
System.out.println("除数不能为0");
}
System.out.println("程序继续运行");
}
}
运行结果:
除数不能为0
程序继续运行
多个 catch 时,子类要写在父类前面,否则编译器报错:
try {
int n = Integer.parseInt(s);
System.out.println(10 / n);
} catch (NumberFormatException e) {
System.out.println("不是数字");
} catch (ArithmeticException e) {
System.out.println("除数为0");
}
2. 抛出:throws
自己不处理,声明“我会抛”,交给调用者去管。
static void readFile() throws IOException {
FileInputStream fis = new FileInputStream("a.txt"); // 编译时异常,必须处理或声明
}
3. 手动抛:throw
主动制造并抛出一个异常,常用于参数不合法时。
static void setAge(int age) {
if (age < 0) {
throw new IllegalArgumentException("年龄不能为负");
}
System.out.println("年龄:" + age);
}
4. finally:无论怎样都会执行
try {
System.out.println(10 / 0);
} catch (ArithmeticException e) {
System.out.println("出错了");
} finally {
System.out.println("我一定会被执行");
}
运行结果:
出错了
我一定会被执行
finally 常用于关闭资源。注意:finally 里不要写 return,否则会把 try 里的值吞掉:
static int bad() {
try { return 1; }
finally { return 2; } // 调用者拿到 2,1 被吞了
}
5. try-with-resources(推荐)
只要类实现了 AutoCloseable,放进 try() 的小括号,无论是否异常都会自动调用 close()。
try (FileInputStream fis = new FileInputStream("a.txt")) {
fis.read();
} catch (IOException e) {
e.printStackTrace();
}
四、throw 与 throws
这是最容易混的一对,区别只看两点:写在哪、写什么。
| 对比 | throw | throws |
|---|---|---|
| 本质 | 动作:真的抛一个异常 | 声明:预告自己可能抛 |
| 位置 | 方法体内部 | 方法签名的括号之后 |
| 后面跟 | new 异常类(...) | 异常类的名字 |
| 口诀 | 真扔 | 报备 |
// throws 在头上报备,throw 在体内真扔
static void quQian(int money) throws IllegalArgumentException {
if (money < 0) {
throw new IllegalArgumentException("金额不能负");
}
System.out.println("取钱成功:" + money);
}
public static void main(String[] args) {
try {
quQian(-10);
} catch (IllegalArgumentException e) {
System.out.println("金额不合法");
}
}
五、常见异常速查
运行时异常(最常见)
| 异常 | 什么时候出现 |
|---|---|
NullPointerException |
调用 null 对象的方法 |
ArrayIndexOutOfBoundsException |
数组下标越界 |
StringIndexOutOfBoundsException |
字符串下标越界 |
ClassCastException |
类型强制转换错误 |
ArithmeticException |
算术错误,如整数除以 0 |
NumberFormatException |
字符串转数字失败 |
IllegalArgumentException |
参数不合法 |
编译时异常(必须处理)
| 异常 | 什么时候出现 |
|---|---|
IOException |
IO 操作失败 |
FileNotFoundException |
文件找不到 |
SQLException |
数据库访问错误 |
ClassNotFoundException |
找不到类 |
六、总结
- 异常是运行时的错误,语法没问题,跑起来才出错;不处理就会崩溃,后面的代码不再执行。
- 所有异常都继承自
Throwable,分Error(管不了)和Exception(能处理)。 Exception又分编译时异常(必须处理)和运行时异常(运行才暴露)。- 处理方式有三种:try-catch 自己捕获、throws 声明抛出、throw 手动抛出;资源关闭优先用 try-with-resources。
throw是真扔,throws是报备;多个 catch 要把子类写在前面。

浙公网安备 33010602011771号