从 01 开始 从 01 开始
首页
  • 📚 计算机基础

    • 计算机简史
    • 数字电路
    • 计算机组成原理
    • 操作系统
    • Linux
    • 计算机网络
    • 数据库
    • 编程工具
    • 装机
  • 🎨 前端

    • Node
  • JavaSE
  • Java 高级
  • JavaEE

    • 构建、依赖管理
    • Ant
    • Maven
    • 日志框架
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • 环境管理和配置管理-科普篇
    • Servlet
  • Spring

    • Spring基础
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC

    • SpringMVC 基础
  • SpringBoot

    • SpringBoot 基础
  • Windows 使用技巧
  • 手机
  • 最全面的输入法教程
  • 最全面的浏览器教程
  • Office
  • 图片类工具
  • 效率类工具
  • RSS
  • 码字工具
  • 各大平台
  • 校招
  • 五险一金等
  • 职场规划
  • 关于离职
  • 杂谈
  • 📖 读书

    • 读书工具
    • 读书笔记
  • 🌍 英语

    • 从零开始学英语
    • 英语兔的相关视频
    • Larry 想做技术大佬的相关视频
  • 🏛️ 政治

    • 反腐
    • GFW
    • 404 内容
    • 审查与自我审查
    • 互联网
    • 战争
  • 💰 经济

    • 关于税
    • 理财
  • 💪 健身

    • 睡眠
    • 皮肤
    • 口腔健康
    • 学会呼吸
    • 健身日志
  • 🏠 其他

    • 驾驶技能
    • 租房与买房
    • 厨艺
  • 电影

    • 电影推荐
  • 漫画

    • 漫画软件
    • 漫画推荐
  • 游戏

    • Steam
    • 三国杀
    • 求生之路
  • 小说
  • 关于本站
  • 关于博主
  • 打赏
  • 网站动态
  • 友人帐
  • 从零开始搭建博客
  • 搭建邮件服务器
  • 本站分享
  • 🌈 生活

    • 2022
    • 2023
    • 2024
    • 2025
  • 📇 文章索引

    • 文章分类
    • 文章归档

晓林

程序猿,自由职业者,博主,英语爱好者,健身达人
首页
  • 📚 计算机基础

    • 计算机简史
    • 数字电路
    • 计算机组成原理
    • 操作系统
    • Linux
    • 计算机网络
    • 数据库
    • 编程工具
    • 装机
  • 🎨 前端

    • Node
  • JavaSE
  • Java 高级
  • JavaEE

    • 构建、依赖管理
    • Ant
    • Maven
    • 日志框架
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • 环境管理和配置管理-科普篇
    • Servlet
  • Spring

    • Spring基础
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC

    • SpringMVC 基础
  • SpringBoot

    • SpringBoot 基础
  • Windows 使用技巧
  • 手机
  • 最全面的输入法教程
  • 最全面的浏览器教程
  • Office
  • 图片类工具
  • 效率类工具
  • RSS
  • 码字工具
  • 各大平台
  • 校招
  • 五险一金等
  • 职场规划
  • 关于离职
  • 杂谈
  • 📖 读书

    • 读书工具
    • 读书笔记
  • 🌍 英语

    • 从零开始学英语
    • 英语兔的相关视频
    • Larry 想做技术大佬的相关视频
  • 🏛️ 政治

    • 反腐
    • GFW
    • 404 内容
    • 审查与自我审查
    • 互联网
    • 战争
  • 💰 经济

    • 关于税
    • 理财
  • 💪 健身

    • 睡眠
    • 皮肤
    • 口腔健康
    • 学会呼吸
    • 健身日志
  • 🏠 其他

    • 驾驶技能
    • 租房与买房
    • 厨艺
  • 电影

    • 电影推荐
  • 漫画

    • 漫画软件
    • 漫画推荐
  • 游戏

    • Steam
    • 三国杀
    • 求生之路
  • 小说
  • 关于本站
  • 关于博主
  • 打赏
  • 网站动态
  • 友人帐
  • 从零开始搭建博客
  • 搭建邮件服务器
  • 本站分享
  • 🌈 生活

    • 2022
    • 2023
    • 2024
    • 2025
  • 📇 文章索引

    • 文章分类
    • 文章归档
  • JavaSE

    • 我的 Java 学习路线
    • 安装 Java
    • Java数据类型

    • Java 多版本配置
    • 面向对象

    • Java核心类

    • IO

    • Java与时间

    • 异常处理

      • 什么是异常
      • 捕获和抛出异常
        • 概述
        • 声明异常
        • 抛出异常
        • 捕获异常
        • 捕获多种异常
        • 小结
      • 深入异常
      • 自定义异常
      • 空指针异常
      • return 和 finally 谁先执行
    • 哈希和加密算法

    • Java8新特性

    • 网络编程

  • JavaSenior

  • JavaEE

  • JavaWeb

  • Spring

  • 主流框架

  • SpringMVC

  • SpringBoot

  • Java
  • JavaSE
  • 异常处理
2023-02-13
目录

捕获和抛出异常

# 捕获和抛出异常

异常处理模型基于三种操作:声明一个异常(declaring an exception)、抛出一个异常 (throwing an exception)和捕获一个异常(catching an exception)。

# 概述

我们来简单说明下上述三种操作

method2() throws Exception{
    if (an error occurs){
        throw new Exception();
    }
}
1
2
3
4
5

在 method2 的方法头里,声明了它可能抛出的必检异常的类型,这里简单写了 Exception,具体情况具体分析,如果是操作 IO,那么应该抛出 IOException。

在第 3 行抛出了一个异常。

在 method1 里用了 try-catch 块来捕获异常:

method1(){
    try{
        method2();
    }catch(Exception ex){
        process exception;
    }
}
1
2
3
4
5
6
7

# 声明异常

在 Java 中,当前执行的语句必属于某个方法。一个程序从 main 方法开始执行一个程序。每个方法都必须声明它可能抛出的必检异常的类型,这称为声明异常( declaring exception)。

由于任何代码都可能发生免检异常,因此无需在方法里显示声明;但必检异常必须在方法头中显式声明,这样,方法的调用者会被告知有异常。

为了在方法中声明一个异常,就要在方法头中使用关键字 throws:

public void myMethod() throws IOException
1

关键字 throws 表明 myMethod 方法可能会抛出异常 IOException。如果方法可能会抛出多个异常,就可以在关键字 throws 后添加一个用逗号分隔的异常列表:

public void myMethod() throws Exceptionl, Exception2, …, ExceptionN
1

注意:如果方法没有在父类中声明异常,那么就不能在子类中对其进行继承来声明异常,例如下述代码:

public class LearnExceptionDemo1 {
  public static void main(String[] args) {
    try {
      new Student().run();
    } catch (Exception e) {
  
    }
  
  }
}


class Person{
  void run(){
    System.out.println("person run!");
  }
}

class Student extends Person{
  
  @Override
  void run() throws Exception {
    System.out.println("student run!");
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

编译会报错:

$ javac LearnExceptionDemo1.java
LearnExceptionDemo1.java:22: 错误: Student中的run()无法覆盖Person中的run()
  void run() throws Exception {
       ^
  被覆盖的方法未抛出Exception
1 个错误
1
2
3
4
5
6

# 抛出异常

检测到错误的程序可以创建一个合适的异常类型的实例并抛出它,这就称为抛出一个异常(throwing an exception)。

例如,假如程序发现传递给方法的参数与方法的合约不符(例如,方法中的参数必须是非负的,但是传入的是一个负参数),这个程序就可以创建 IllegalArgumentException 的一个实例:

IllegalArgumentException ex = new II1egalArgumentException("Wrong Argument");
1

tips:Java 中每个异常类至少有两个构造方法:一个无参构造方法和一个带有描述这个异常的 String 参数的构造方法。该参数称为异常消息(exception message),它可以用 getMessage() 获取。

然后可以抛出(throw)该异常:

throw ex;
1

或者简化为一条语句:

throw new IllegalArgumentException ("Wrong Argument");
1

# 捕获异常

当抛出一个异常时,可以在 try-catch 代码块中捕获和处理它,格式如下:

try {
    statements; // Statements that may throw exceptions
}
catch (Exception1 exVarl){
    handler for exceptionl; 
}
catch (Exception2 exVar2){
    handler for exception2; 
}
...
catch (ExceptionN exVarN){
    handler for except!onN; 
}
1
2
3
4
5
6
7
8
9
10
11
12
13

首先,我们将可能发生异常的代码,用 try 块包住,然后会发生两种情况:一种是发生了异常,另一种没有发生。我们来分别看看两种情景,try-catch 代码块的处理逻辑

# 如果发生了异常

我们运行代码,如果发生了异常,则:

  1. 跳过 try 块中剩余的代码,然后开始查找处理这个异常的代码块,也就是 catch 块。
  2. 每个 catch 处理一种异常,例如 IOExcetion,NullPointerException,每个 catch 块可以称为一个异常处理器(exception handler)。
  3. 查找的过程是从上往下,例如判断抛出的异常是否 Exception1,是则由第一个 catch 块处理,并且该异常会赋值给 exvarl 参数;如果不是,则继续看是抛出的异常是否 Exception2;以此类推。
  4. 对应的 catch 块处理完后,则执行 try-catch 块之后的代码。多个 catch 语句只有一个能被执行
  5. 如果没有发现异常处理器,Java 会退出这个方法,把异常抛给调用这个方法的方法,然后用同样的过程来查找处理器;
  6. 如果在调用方法链中找不到异常处理器,Java 程序会终止。

# 捕获异常举例

假设 main 方法调用 method1, method1 调用 method2,method2 调用 methods3, method3 抛出一个异常,如图 所示。

我们分几种情况来看,异常的捕获是怎么样的:

  • 首先发生了异常,try 块中剩下的 statement5 代码就不会再被执行了
  • 如果异常类型是 Exceprion3,它就会被 method2 中处理异常 ex3 的 catch 块捕获并处理。既然异常已经被处理了,那么程序可以继续执行其他代码了,然后执行 statement6 处的代码。
  • 如果异常类型是 Exception2,则退出 method2,控制被返回给 method1,然后跳过 statement3,异常就会被 method1 中处理异常 ex2 的 catch 块捕获,然后执行 statement4
  • 如果异常类型是 Exception1, 则退出 method1, 控制被返回给 main 方法,跳过 statement1,异常会被 main 方法中处理异常 ex1 的 catch 块捕获。然后执行 statement2
  • 如果异常类型没有在 method2、method1 和 main 方法中被捕获,程序就会终止。不执行 statement1 和 statement2

# 如果没有发生异常

如果在执行 try 块的过程中没有出现异常,则跳过 catch 子句,也就是继续执行 try-catch 块之后的代码。

# 注意点

  1. 一个通用的异常父类有很多的子类,如果使用 catch 块捕获父类,那么只要父类和其子类的异常对象都满足被捕获的条件(就好比用 instanceof 父类 )
  2. 注意父类的 catch 块,要出现在子类的 catch 块之前,否则会编译报错。例如 RuntimeException是 Exception的子类,必须先捕获 RuntimeException,再捕获 Exception。这很好理解,如果父类现在前面,那么子类的异常是永远捕获不到的
  3. 如果一个方法声明了一个必检异常,那么必须在 try-catch 块中调用它,或者在调用的方法中声明异常。例如,InputStream 类的 read 方法就声明了异常 IOException,因此几乎所有调用 IO 的方法都要声明异常。

# 捕获多种异常

Java7 版本出了一个新特性,可以在一个 catch 块中捕获多种类型的异常:每个异常类型使用竖线( | )与下一个分隔。如果其中一个异常被捕获,则执行处理的代码。

catch (Exceptionl | Exception2 | … | Exceptionk ex){

}
1
2
3

这样消除了重复的代码,例如下面的例子中,每个 catch 块都有重复的代码:

catch (IOException ex) {
     logger.log(ex);
     throw ex;
catch (SQLException ex) {
     logger.log(ex);
     throw ex;
}
1
2
3
4
5
6
7

可以简写为:

catch (IOException|SQLException ex) {
    logger.log(ex);
    throw ex;
}
1
2
3
4

注意:

  1. 如果 catch 块处理多种异常类型,那么 catch 参数隐式的声明为 final,因此在 catch 块中不能给它指定任意的值
  2. 编译一个处理多种异常类型的 catch 块产生的字节码,比编译多个 catch 块但每个只能处理一种异常类型产生的字节码要少(因此优越一些)。在由编译器生成的字节码中,一个处理多种异常类型的 catch 块没有重复; 字节码没有复制异常处理器。

# 小结

异常处理模型基于三种操作:声明一个异常、抛出一个异常和捕获一个异常

声明异常的关楗字是 throws, 抛出异常的关键字是 throw。

异常的处理器是通过从当前的方法开始,沿着方法调用链,按照异常的反向传播方向找到的。

上次更新: 2024/10/1 16:58:10
什么是异常
深入异常

← 什么是异常 深入异常→

最近更新
01
2025 年 4 月记
04-30
02
山西大同 “订婚强奸案” 将会给整个社会带来的影响有多严重? - 知乎 转载
04-26
03
一个小技巧,让电子书阅读体验翻倍!
04-18
更多文章>
Theme by Vdoing | Copyright © 2022-2025 | 粤 ICP 备 2022067627 号 -1 | 粤公网安备 44011302003646 号 | 点击查看十年之约
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式