【第六章】Java——异常机制
-
- 前言
- 一、什么是异常
-
- 1. 异常的基本概念
- 2. 异常和语法错误的区别
-
- 编译错误
- 异常
- 3. 为什么程序需要异常机制
- 二、生活化理解异常
-
- 1. 正常流程和异常流程
- 2. 异常处理的意义
- 三、Java 中的异常体系
-
- 1. 所有异常和错误的顶层父类:`Throwable`
- 2. `Error`
- 3. `Exception`
- 四、异常的分类
-
- 1. 编译时异常
- 2. 运行时异常
- 3. 二者区别总结
-
- 编译时异常
- 运行时异常
- 五、常见运行时异常
-
- 1. `NullPointerException`
- 2. `ArithmeticException`
- 3. `ArrayIndexOutOfBoundsException`
- 4. `StringIndexOutOfBoundsException`
- 5. `ClassCastException`
- 6. `NumberFormatException`
- 六、常见编译时异常
-
- 1. `IOException`
- 2. `FileNotFoundException`
- 3. `SQLException`
- 4. `ClassNotFoundException`
- 七、异常出现后程序会怎样
-
- 1. 默认情况:程序中断
- 2. 异常信息一般包括什么
- 八、`try-catch` 异常捕获机制
-
- 1. 基本作用
- 2. 示例
- 3. 执行流程
-
- 没有异常
- 有异常
- 九、多个 `catch`
-
- 1. 匹配原则
- 2. 注意顺序
- 十、`finally` 语句块
-
- 1. 基本作用
- 2. 典型用途
- 3. 示例
- 4. `finally` 一定执行吗
- 十一、`try-catch-finally` 的完整形式
-
- 1. 组合关系
-
- 合法形式
- 非法形式
- 十二、异常对象常用方法
-
- 1. `printStackTrace()`
- 2. `getMessage()`
- 3. `toString()`
- 4. 示例
- 十三、`throw` 关键字
-
- 1. 作用
- 2. 示例
- 3. 使用场景
- 十四、`throws` 关键字
-
- 1. 作用
- 2. 示例
- 3. `throw` 和 `throws` 的区别
-
- `throw`
- `throws`
- 十五、方法重写与异常
-
- 1. 基本原则
- 2. 原因
- 3. 初学阶段怎么记
- 十六、自定义异常
-
- 1. 为什么要自定义异常
- 2. 自定义异常的方式
-
- 继承 `Exception`
- 继承 `RuntimeException`
- 3. 示例:自定义运行时异常
- 4. 示例:自定义受查异常
- 十七、`finally` 与 `return` 的关系
-
- 1. 先看示例
- 2. 为什么会这样
- 3. 更复杂的情况
- 十八、JDK7 之后的资源自动关闭
-
- 1. 优点
- 2. 前提
- 十九、异常处理中常见错误
-
- 1. 抓了异常却什么也不做
- 2. 一上来就 `catch(Exception)`
- 3. 把运行时异常全靠 `try-catch` 掩盖
- 4. 在 `finally` 中写复杂逻辑
- 5. 自定义异常时语义不清
- 二十、什么时候该处理,什么时候该抛出
-
- 1. 当前方法能解决,就处理
- 2. 当前方法解决不了,就往上抛
- 3. 一般原则
- 二十一、异常机制和程序健壮性
- 二十二、学习异常时最容易混淆的几个点
-
- 1. `throw` 和 `throws`
- 2. 编译时异常和运行时异常
- 3. `final`、`finally`、`finalize`
-
- `final`
- `finally`
- `finalize`
- 二十三、学习建议
-
- 1. 不要只背概念,要亲手制造异常
- 2. 多比较“处理前”和“处理后”
- 3. 重点抓住执行流程
- 4. 先掌握常见异常,再学自定义异常
- 二十四、本章知识总结
-
- 1. 异常的本质
- 2. 异常体系
- 3. 异常分类
- 4. 异常处理语法
- 5. 关键区别
- 6. 开发中的重点
- 二十五、结语
- 二十六、附:常见语法速查表
-
- 1. `try-catch`
- 2. `try-catch-finally`
- 3. `throw`
- 4. `throws`
- 5. 自定义异常
- 6. 自动关闭资源
前言
在编写 Java 程序的过程中,程序并不总是会按照我们的理想情况顺利运行。很多时候,代码在编译阶段看起来没有问题,但在运行阶段却可能因为各种原因出错,例如:
- 除数不能为 0
- 数组下标越界
- 字符串为 null
- 文件找不到
- 输入的数据格式不正确
- 网络连接失败
- 类型转换失败
这些问题,本质上都属于程序运行过程中的异常情况。 如果程序员对这些异常情况完全不做处理,那么轻则程序报错终止,重则影响整个系统的稳定性和用户体验。
因此,在 Java 里,异常并不是一个“可有可无”的附加知识,而是编写健壮程序必须掌握的内容。一个真正可用的程序,不能只考虑“正常情况下怎么运行”,还必须考虑:
- 出现错误时怎么办
- 错误发生后要不要继续执行
- 如何给调用者一个明确提示
- 如何避免程序直接崩溃
- 怎样把错误信息保留下来,方便排查问题
Java 之所以在企业开发中被广泛使用,其中一个重要原因,就是它对异常机制做了比较完整的设计。Java 通过 Throwable 体系,把运行中出现的问题进行了分类,并且提供了:
- try
- catch
- finally
- throw
- throws
等一整套处理异常的语法工具。
可以这样理解:
异常机制的作用,不是让程序永远不出错,而是让程序在出错时仍然可控。
本章将围绕 Java 中的异常机制进行系统讲解,尽量从概念、分类、语法、执行流程、常见异常、处理方式、自定义异常、开发规范等多个角度展开说明。 学习这一章时,不能只记住几个关键字,更重要的是要理解异常的本质、传播方式和处理思路。
通过本章学习,可以掌握:
- 什么是异常
- 异常与普通错误提示的区别
- Java 异常体系结构
- Throwable、Error、Exception 之间的关系
- 编译时异常和运行时异常的区别
- try-catch-finally 的基本用法
- 多个 catch 的匹配顺序
- finally 的作用
- throw 和 throws 的区别
- 如何自定义异常类
- 常见异常类型及触发场景
- 异常处理中常见错误与开发建议
一、什么是异常
1. 异常的基本概念
异常,英文叫 Exception。在 Java 中,异常指的是:
程序在运行过程中出现的不正常情况。
这里要特别注意两个词:
- 运行过程中
- 不正常情况
也就是说,异常通常不是语法错误,而是程序在执行时遇到了超出预期的问题。
例如:
int a = 10;
int b = 0;
System.out.println(a / b);
这段代码在编译时是可以通过的,因为语法没有问题。 但运行时会报错,因为 0 不能作为除数。
这就是异常。
2. 异常和语法错误的区别
很多初学者容易把“编译错误”和“异常”混在一起。
例如下面这段代码:
int a = "hello";
这不是异常,而是编译错误。 因为代码在编译阶段就已经不合法,程序根本无法运行。
而下面这段:
String str = null;
System.out.println(str.length());
编译没问题,但运行时会出现 NullPointerException。 这才属于异常。
所以可以这样区分:
编译错误
- 语法、类型、结构不合法
- 程序连运行机会都没有
异常
- 编译可以通过
- 运行时出现问题
3. 为什么程序需要异常机制
如果没有异常机制,程序一旦出错,通常只能:
- 直接崩溃
- 没有明确提示
- 调用者不知道哪里出问题
- 程序员也难以定位原因
有了异常机制后,可以做到:
- 捕获错误
- 给出清晰信息
- 必要时中断执行
- 必要时继续执行后续逻辑
- 统一处理错误情况
因此,异常机制本质上是在帮助程序员管理运行时风险。
二、生活化理解异常
1. 正常流程和异常流程
写程序时,通常都会先考虑一种最理想的情况:
- 用户输入正确
- 文件一定存在
- 网络一定正常
- 数据格式一定符合要求
这种情况叫正常流程。
但真实世界中常常会有一些意外:
- 用户输错数据
- 磁盘文件被删掉了
- 数据库连接失败
- 服务器超时
- 某个对象本来应该有值,但实际上是 null
这些不在正常流程里的情况,就属于异常流程。
2. 异常处理的意义
可以把异常处理理解成:
提前给程序准备“出问题时的备用方案”。
例如:
- 文件打不开,就提示“文件不存在”
- 用户输入错误,就提示“请输入合法数字”
- 网络断开,就提示“连接失败,请稍后重试”
这就是异常机制在程序设计中的现实意义。
三、Java 中的异常体系
1. 所有异常和错误的顶层父类:Throwable
在 Java 中,所有可以被“抛出”的问题对象,最终都继承自 Throwable。
也就是说:
Throwable 是 Java 异常体系的根类。
它下面主要分为两大类:
- Error
- Exception
可以简单画成:
Throwable
├── Error
└── Exception
2. Error
Error 表示比较严重的问题,通常不是程序员通过普通代码就能轻易处理的。
例如:
- StackOverflowError:栈溢出
- OutOfMemoryError:内存溢出
这些问题通常说明:
- 程序本身存在严重缺陷
- 或运行环境资源出现问题
在一般开发中,Error 通常不作为常规异常处理对象。
3. Exception
Exception 才是我们平时编程中最常处理的一类异常。
例如:
- 数组越界
- 空指针
- 文件不存在
- 类型转换错误
- 输入输出失败
这些异常大多数都和程序逻辑、输入数据、外部资源有关。
四、异常的分类
在 Java 中,Exception 又可以进一步分成两大类:
- 编译时异常
- 运行时异常
1. 编译时异常
编译时异常也叫受查异常,英文常叫:
- Checked Exception
这类异常的特点是:
编译器要求程序员必须显式处理,否则代码不能通过编译。
例如:
FileInputStream fis = new FileInputStream("a.txt");
这里可能会出现文件找不到的问题,所以编译器会要求你:
- 要么 try-catch
- 要么 throws
否则编译报错。
常见编译时异常有:
- IOException
- FileNotFoundException
- SQLException
- ClassNotFoundException
2. 运行时异常
运行时异常也叫非受查异常,通常是:
- RuntimeException 及其子类
这类异常的特点是:
编译器不强制要求处理,但运行时可能出问题。
例如:
int[] arr = {1, 2, 3};
System.out.println(arr[5]);
编译器不会阻止你,因为语法合法。 但运行时会抛出:
ArrayIndexOutOfBoundsException
常见运行时异常有:
- NullPointerException
- ArithmeticException
- ArrayIndexOutOfBoundsException
- ClassCastException
- NumberFormatException
3. 二者区别总结
编译时异常
- 编译器强制处理
- 不处理就无法通过编译
- 通常和外部资源、环境相关
运行时异常
- 编译器不强制处理
- 运行时才会暴露问题
- 通常和代码逻辑错误有关
五、常见运行时异常
这一部分是学习异常时必须重点掌握的内容,因为这些异常在平时做题和写程序时最常见。
1. NullPointerException
空指针异常。
示例:
String str = null;
System.out.println(str.length());
原因:
- str 没有真正指向对象
- 却调用了对象的方法
这是 Java 初学阶段最常见的异常之一。
2. ArithmeticException
算术异常。
示例:
int a = 10;
int b = 0;
System.out.println(a / b);
原因:
- 整数除以 0
3. ArrayIndexOutOfBoundsException
数组下标越界异常。
示例:
int[] arr = {1, 2, 3};
System.out.println(arr[3]);
原因:
- 访问了数组有效范围之外的下标
4. StringIndexOutOfBoundsException
字符串下标越界异常。
示例:
String s = "abc";
System.out.println(s.charAt(5));
5. ClassCastException
类型转换异常。
示例:
Object obj = "hello";
Integer num = (Integer) obj;
原因:
- 实际类型和强制转换目标类型不匹配
6. NumberFormatException
数字格式异常。
示例:
String s = "abc";
int num = Integer.parseInt(s);
原因:
- 字符串内容不能转换为数字
六、常见编译时异常
1. IOException
输入输出异常,是很多 IO 类操作中常见的父异常类型。
例如:
- 读文件失败
- 写文件失败
- 流关闭失败
2. FileNotFoundException
文件未找到异常。
示例:
import java.io.FileInputStream;
public class Test {
public static void main(String[] args) throws Exception {
FileInputStream fis = new FileInputStream("abc.txt");
}
}
如果文件不存在,就会出现该异常。
3. SQLException
数据库操作异常。
例如:
- SQL 语句错误
- 数据库连接失败
- 查询执行异常
4. ClassNotFoundException
类未找到异常。
常出现在反射机制或动态加载类的场景中。
七、异常出现后程序会怎样
1. 默认情况:程序中断
如果异常出现后,没有被处理,那么程序通常会:
- 停止执行
- 在控制台输出异常信息
- 给出异常类型和出错位置
例如:
public class Test {
public static void main(String[] args) {
int a = 10 / 0;
System.out.println("这句话不会执行");
}
}
运行后:
- 异常被抛出
- 程序终止
- 后面的输出语句不会执行
2. 异常信息一般包括什么
异常信息通常包括:
- 异常类型
- 错误原因说明
- 调用栈信息
例如:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Test.main(Test.java:3)
这说明:
- 异常类型是 ArithmeticException
- 原因是 / by zero
- 错误发生在 Test.java 第 3 行
八、try-catch 异常捕获机制
1. 基本作用
try-catch 用来捕获并处理异常。
语法格式:
try {
// 可能发生异常的代码
} catch (异常类型 变量名) {
// 异常处理代码
}
2. 示例
public class Test {
public static void main(String[] args) {
try {
int a = 10 / 0;
System.out.println("try 中的后续语句");
} catch (ArithmeticException e) {
System.out.println("出现了算术异常");
}
System.out.println("程序继续执行");
}
}
输出结果说明:
- 10 / 0 出现异常
- try 中异常后面的代码不再执行
- 进入 catch
- 异常被处理后,程序继续执行 catch 后面的代码
3. 执行流程
没有异常
- 执行 try
- 跳过 catch
- 继续往后执行
有异常
- 执行 try
- 发生异常,后续 try 代码停止
- 找匹配的 catch
- 执行对应 catch
- 再继续往后执行
九、多个 catch
一个 try 后面可以跟多个 catch,用于处理不同类型的异常。
示例:
public class Test {
public static void main(String[] args) {
try {
String s = null;
System.out.println(s.length());
} catch (ArithmeticException e) {
System.out.println("算术异常");
} catch (NullPointerException e) {
System.out.println("空指针异常");
} catch (Exception e) {
System.out.println("其他异常");
}
}
}
1. 匹配原则
异常会按照 catch 的顺序,从上到下匹配。
一旦匹配成功:
- 后面的 catch 不再执行
2. 注意顺序
如果写成:
catch (Exception e) { }
catch (NullPointerException e) { }
这通常是错误的,因为:
- Exception 是父类
- 它已经把后面的子类异常全接住了
因此,多个 catch 的顺序通常应该是:
子类异常在前,父类异常在后。
十、finally 语句块
1. 基本作用
finally 用于表示:
无论是否发生异常,通常都要执行的代码。
语法:
try {
// 可能异常的代码
} catch (异常类型 e) {
// 异常处理
} finally {
// 一般都会执行
}
2. 典型用途
最常见的用途是资源释放,例如:
- 关闭文件流
- 关闭数据库连接
- 关闭网络连接
因为这些资源不管程序有没有出错,最后通常都应该关闭。
3. 示例
public class Test {
public static void main(String[] args) {
try {
int a = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("捕获到异常");
} finally {
System.out.println("finally 执行");
}
}
}
输出中 finally 仍然会执行。
4. finally 一定执行吗
一般情况下会执行,但并不是绝对的。
例如下面情况可能不会执行:
- 程序在执行前被强制终止
- 调用了 System.exit(0);
- 进程被操作系统直接杀死
所以更准确地说:
在正常 JVM 执行流程下,finally 通常都会执行。
十一、try-catch-finally 的完整形式
完整写法:
try {
// 可能发生异常的代码
} catch (异常类型1 e) {
// 处理方式1
} catch (异常类型2 e) {
// 处理方式2
} finally {
// 最终执行代码
}
1. 组合关系
合法形式
- try-catch
- try-finally
- try-catch-finally
非法形式
- 只有 try
- 只有 catch
- 只有 finally
也就是说:
try 后面必须跟 catch 或 finally,至少一个。
十二、异常对象常用方法
在 catch 中,捕获到的异常其实是一个对象。
例如:
catch (Exception e) {
}
这里的 e 就是异常对象。
1. printStackTrace()
打印完整异常信息和调用栈。
e.printStackTrace();
这是调试时最常用的方法之一。
2. getMessage()
获取异常描述信息。
System.out.println(e.getMessage());
3. toString()
返回异常类名和异常信息。
System.out.println(e.toString());
4. 示例
try {
int a = 10 / 0;
} catch (Exception e) {
System.out.println(e.getMessage());
System.out.println(e.toString());
e.printStackTrace();
}
十三、throw 关键字
1. 作用
throw 用于:
手动抛出一个异常对象。
语法:
throw new 异常类名("异常信息");
2. 示例
public class Test {
public static void main(String[] args) {
int age = –1;
if (age < 0) {
throw new RuntimeException("年龄不能为负数");
}
}
}
这表示程序员主动认为这里是非法情况,于是主动抛出异常。
3. 使用场景
当程序发现某些条件不符合要求时,可以主动中断流程并抛出异常。
例如:
- 参数非法
- 数据状态不正确
- 业务条件不满足
十四、throws 关键字
1. 作用
throws 用于写在方法声明后面,表示:
当前方法可能抛出哪些异常,交给调用者处理。
语法:
返回值类型 方法名(参数列表) throws 异常类名1, 异常类名2 {
}
2. 示例
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class Test {
public static void readFile() throws FileNotFoundException {
FileInputStream fis = new FileInputStream("a.txt");
}
}
这表示:
- readFile() 方法内部可能抛出 FileNotFoundException
- 但当前方法自己不处理
- 交给调用该方法的人去处理
3. throw 和 throws 的区别
这是常考点。
throw
- 用在方法内部
- 后面跟异常对象
- 表示“抛出一个具体异常”
throws
- 用在方法声明处
- 后面跟异常类型
- 表示“声明可能抛出哪些异常”
十五、方法重写与异常
在继承中,子类重写父类方法时,也会涉及异常规则。
1. 基本原则
子类重写父类方法时:
- 抛出的检查异常不能比父类更大
也就是说,子类不能随意扩大异常范围。
2. 原因
因为父类引用调用方法时,编译器是按照父类方法声明来判断异常的。 如果子类突然抛出一个更大的受查异常,会破坏原有约定。
3. 初学阶段怎么记
可以先记一句:
重写方法时,子类抛出的受查异常范围不能超过父类。
十六、自定义异常
很多时候,Java 自带的异常类并不能完全表达我们的业务含义。 这时就可以自定义异常类。
1. 为什么要自定义异常
例如系统中有这样一种业务规则:
- 用户年龄不能小于 0
- 注册时用户名不能为空
- 转账金额必须大于 0
如果全部都直接抛:
throw new RuntimeException("出错了");
虽然也能用,但语义不清晰。 如果自定义异常类,含义会更明确。
2. 自定义异常的方式
通常可以继承:
- Exception
- RuntimeException
继承 Exception
表示受查异常。
继承 RuntimeException
表示运行时异常。
3. 示例:自定义运行时异常
class AgeIllegalException extends RuntimeException {
public AgeIllegalException() {
}
public AgeIllegalException(String message) {
super(message);
}
}
使用:
public class Test {
public static void main(String[] args) {
int age = –1;
if (age < 0) {
throw new AgeIllegalException("年龄不能小于 0");
}
}
}
这样程序的异常含义就更明确了。
4. 示例:自定义受查异常
class LoginException extends Exception {
public LoginException() {
}
public LoginException(String message) {
super(message);
}
}
方法声明中:
public void login(String password) throws LoginException {
if (!"123456".equals(password)) {
throw new LoginException("密码错误");
}
}
十七、finally 与 return 的关系
这是一个比较经典、也比较容易出错的点。
1. 先看示例
public class Test {
public static int test() {
try {
return 10;
} finally {
System.out.println("finally 执行");
}
}
}
结果:
- finally 还是会执行
2. 为什么会这样
因为 return 并不是一写出来就立刻彻底结束。 在方法真正返回前,JVM 还会去执行 finally。
3. 更复杂的情况
public class Test {
public static int test() {
try {
return 10;
} finally {
return 20;
}
}
}
最终返回值是:
20
说明:
不建议在 finally 中写 return。
因为这样会让程序逻辑变得很混乱,也可能掩盖原本的异常或返回结果。
十八、JDK7 之后的资源自动关闭
在早期写法中,IO 流通常这样处理:
FileInputStream fis = null;
try {
fis = new FileInputStream("a.txt");
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
这种写法很安全,但比较繁琐。
从 JDK7 开始,可以使用:
try(资源对象声明) {
// 使用资源
}
示例:
import java.io.FileInputStream;
public class Test {
public static void main(String[] args) {
try (FileInputStream fis = new FileInputStream("a.txt")) {
System.out.println("文件打开成功");
} catch (Exception e) {
e.printStackTrace();
}
}
}
1. 优点
- 代码更简洁
- 自动关闭资源
- 更不容易遗漏释放操作
2. 前提
资源对象需要实现:
AutoCloseable
很多流类都已经实现了这个接口。
十九、异常处理中常见错误
1. 抓了异常却什么也不做
例如:
catch (Exception e) {
}
这叫“吞异常”。 程序看似没报错,但真实问题被隐藏了,后续非常难排查。
2. 一上来就 catch(Exception)
虽然这样写不一定错,但如果不加区分地全部捕获,会让异常处理过于粗糙。 很多时候更应该优先捕获更具体的异常类型。
3. 把运行时异常全靠 try-catch 掩盖
例如明明应该先判断空值,却直接用 try-catch 去包住空指针。 这种写法不优雅,也说明代码逻辑前置校验没做好。
4. 在 finally 中写复杂逻辑
finally 本来应该用于:
- 收尾
- 释放资源
- 执行必要清理
不适合写太复杂的业务逻辑。
5. 自定义异常时语义不清
如果异常名和异常信息写得很含糊,例如:
- MyException
- "出错了"
那即使自定义了,也没有实际价值。 异常类名和异常提示应该尽量表达真实问题。
二十、什么时候该处理,什么时候该抛出
这是实际开发中很重要的一个问题。
1. 当前方法能解决,就处理
例如:
- 用户输入错误,可以在当前界面提示重新输入
- 文件路径错误,可以在当前层提示用户重新选择
这类问题当前层能消化,就可以直接处理。
2. 当前方法解决不了,就往上抛
例如底层工具方法只负责读取文件,它自己并不知道:
- 出错后是重试
- 还是提示用户
- 还是终止流程
这时它更适合把异常抛给上层,让更高层决定如何处理。
3. 一般原则
可以先记住一句:
谁更清楚如何处理这个问题,谁就应该处理异常。
二十一、异常机制和程序健壮性
一个优秀程序的重要特点之一,就是“健壮”。
所谓健壮,指的是:
- 面对异常输入不轻易崩溃
- 能给出合理提示
- 能保留必要日志
- 能保证重要资源被正确释放
异常机制正是实现程序健壮性的重要基础。
例如在开发中:
- 表单提交要校验数据
- 文件上传要校验格式
- 读取配置要考虑文件缺失
- 网络请求要考虑超时和失败
这些都需要异常思维,而不是只写“理想路径”。
二十二、学习异常时最容易混淆的几个点
1. throw 和 throws
- throw:真正抛异常对象
- throws:声明可能抛出异常类型
2. 编译时异常和运行时异常
- 编译时异常:编译器强制处理
- 运行时异常:编译器不强制,但运行时会出问题
3. final、finally、finalize
这是三个完全不同的词。
final
- 修饰类、方法、变量
finally
- 异常处理中的最终执行块
finalize
- Object 类中的方法,和垃圾回收相关
- 现代开发中基本不作为重点使用
这三者千万不要混淆。
二十三、学习建议
1. 不要只背概念,要亲手制造异常
建议自己动手写下面这些小例子:
- 除 0 异常
- 数组越界异常
- 空指针异常
- 类型转换异常
- 文件找不到异常
亲自看到异常信息,比单纯看书更容易记住。
2. 多比较“处理前”和“处理后”
例如:
- 不加 try-catch 时程序怎样
- 加了 try-catch 后程序怎样
这样才能真正理解异常机制的作用。
3. 重点抓住执行流程
异常这章最容易出错的地方,不是概念,而是流程。
一定要弄清:
- 异常在哪里产生
- try 中后面代码还会不会执行
- 会进入哪个 catch
- finally 会不会执行
- 方法到底是处理了还是抛出了异常
4. 先掌握常见异常,再学自定义异常
学习顺序建议:
这样更顺。
二十四、本章知识总结
1. 异常的本质
异常表示程序在运行过程中出现的不正常情况。 它不是单纯的“报错信息”,而是一种完整的错误处理机制。
2. 异常体系
Java 异常体系顶层是:
- Throwable
下面分为:
- Error
- Exception
3. 异常分类
Exception 主要分为:
- 编译时异常
- 运行时异常
其中:
- 编译时异常要求强制处理
- 运行时异常通常反映代码逻辑问题
4. 异常处理语法
最常见的语法有:
- try
- catch
- finally
- throw
- throws
5. 关键区别
- throw:抛出具体异常对象
- throws:声明方法可能抛出的异常类型
6. 开发中的重点
- 不要吞异常
- 能处理就处理,不能处理就抛出
- 优先捕获具体异常
- 注意资源释放
- 必要时自定义异常
二十五、结语
Java 中的异常机制,是连接“程序能运行”和“程序能稳定运行”之间的一座桥梁。很多初学者刚开始学异常时,会觉得它只是几个关键字和一些报错信息,但实际上,异常体现的是一种更完整的编程思维:
- 不只写正常情况
- 还要考虑错误情况
- 不只让代码跑起来
- 还要让代码在出问题时保持可控
真正优秀的程序,并不是从不出错,而是在出错时仍然能够:
- 给出明确信息
- 保证资源安全
- 降低故障影响
- 方便后续排查
因此,学习异常这一章时,不能只把它当成一个知识点,而要把它看成 Java 程序设计中非常关键的一种“防御性思维”。 只有真正掌握了异常机制,后面学习 IO、集合、多线程、数据库、网络编程和项目开发时,代码才会更加规范,也更加稳定。
二十六、附:常见语法速查表
1. try-catch
try {
// 可能出异常的代码
} catch (Exception e) {
e.printStackTrace();
}
2. try-catch-finally
try {
// 业务代码
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("收尾操作");
}
3. throw
throw new RuntimeException("参数非法");
4. throws
public void test() throws IOException {
}
5. 自定义异常
class MyException extends RuntimeException {
public MyException(String message) {
super(message);
}
}
6. 自动关闭资源
try (FileInputStream fis = new FileInputStream("a.txt")) {
// 使用资源
}
好的,你们本章就到此结束了,在本章我们主要理解了Java当中异常的运行机制。到此为止,Java中所有的语法部分也介绍完毕,下一章我们介绍Java中的常用类。




