从01开始 从01开始
首页
  • 计算机科学导论
  • 数字电路
  • 计算机组成原理

    • 计算机组成原理-北大网课
  • 操作系统
  • Linux
  • Docker
  • 计算机网络
  • 计算机常识
  • Git
  • JavaSE
  • Java高级
  • JavaEE

    • Ant
    • Maven
    • Log4j
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • Servlet
  • Spring
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC
  • SpringBoot
  • 学习网课的心得
  • 输入法
  • 节假日TodoList
  • 其他
  • 关于本站
  • 网站日记
  • 友人帐
  • 如何搭建一个博客
GitHub (opens new window)

peterjxl

人生如逆旅,我亦是行人
首页
  • 计算机科学导论
  • 数字电路
  • 计算机组成原理

    • 计算机组成原理-北大网课
  • 操作系统
  • Linux
  • Docker
  • 计算机网络
  • 计算机常识
  • Git
  • JavaSE
  • Java高级
  • JavaEE

    • Ant
    • Maven
    • Log4j
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • Servlet
  • Spring
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC
  • SpringBoot
  • 学习网课的心得
  • 输入法
  • 节假日TodoList
  • 其他
  • 关于本站
  • 网站日记
  • 友人帐
  • 如何搭建一个博客
GitHub (opens new window)
  • JavaSE

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

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

    • Java核心类

    • IO

    • Java与时间

    • 异常处理

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

    • Java8新特性

    • 网络编程

    • Java
  • JavaSenior

  • JavaEE

  • JavaWeb

  • Spring

  • 主流框架

  • SpringMVC

  • SpringBoot

  • Java并发

  • Java源码

  • JVM

  • 韩顺平

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

捕获和抛出异常

# 01.捕获和抛出异常

异常处理模型基于三种操作:声明一个异常(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。

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

‍

‍

‍

‍

在GitHub上编辑此页 (opens new window)
上次更新: 2023/3/26 10:41:39
什么是异常
深入异常

← 什么是异常 深入异常→

Theme by Vdoing | Copyright © 2022-2023 粤ICP备2022067627号-1 粤公网安备 44011302003646号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式