Java异常详解大全(2023版)

2023-12-14 16:57:23

异常分类

在这里插入图片描述

1.Throwable

概念:Throwable 是 Java 语言中所有错误与异常的超类。(详细如上图所示)

  • Throwable 包含两个子类:Error(错误)和 Exception(异常)
  • Throwable 包含了其线程创建时线程执行堆栈的快照,它提供了 printStackTrace() 等接口用于获取堆栈跟踪数据等信息。

2. Error(错误)

定义: 发生时候应用程序是不会处理此类错误的

Error 类及其子类是程序中无法处理的错误,表示运行应用程序中出现了严重的错误。

特点:

此类错误一般表示代码运行时 JVM 出现问题。

  • 通常有 Virtual MachineError(虚拟机运行错误)、NoClassDefFoundError(类定义错误)等。

  • 比如 OutOfMemoryError:内存不足错误;StackOverflowError:栈溢出错误。此类错误发生时,JVM 将终止线程。

总结:

这些错误是不受检异常,非代码性错误。因此,当此类错误发生时,应用程序不应该去处理此类错误。按照Java惯例,我们是不应该实现任何新的Error子类的!

3. Exception(异常)

Exception是程序本身可以捕获并且可以处理的异常

Exception 这种异常又分为两类:运行时异常 和 编译时异常

受检异常除 RuntimeException 及其子类外,其他的 Exception 异常都属于受检异常

非受检异常该类异常包括运行时异常(RuntimeException极其子类)和错误(Error)。

  • 3.1 运行时异常 RuntimeException

一般是由程序逻辑错误引起的,在程序中可以选择捕获处理,也可以不处理。

  • 定义:RuntimeException 类及其子类,表示 JVM 在运行期间可能出现的异常。

  • 特点:Java 编译器不会检查它。 也就是说,当程序中可能出现这类异常时,倘若既"没有通过throws声明抛出它",也"没有用try-catch语句捕获它",还是会编译通过。

    比如NullPointerException空指针异常、ArrayIndexOutBoundException数组下标越界异常、ClassCastException类型转换异常、ArithmeticExecption算术异常。此类异常属于不受检异常.虽然 Java 编译器不会检查运行时异常,但是我们也可以通过 throws 进行声明抛出,也可以通过 try-catch 对它进行捕获处理。如果产生运行时异常,则需要通过修改代码来进行避免。例如,若会发生除数为零的情况,则需要通过代码避免该情况的发生!
    RuntimeException 异常会由 Java 虚拟机自动抛出并自动捕获(就算我们没写异常捕获语句运行时也会抛出错误!!),此类异常的出现绝大数情况是代码本身有问题应该从逻辑上去解决并改进代码。

  • 3.2 编译时异常(受检查异常)ClassNotFoundException + IOException

    • 定义: Exception 中除 RuntimeException 及其子类之外的异常。
    • 特点: Java 编译器会检查它。如果程序中出现此类异常,比如 ClassNotFoundException(没有找到指定的类异常),IOException(IO流异常),要么通过throws进行声明抛出,要么通过try-catch进行捕获处理**,否则不能通过编译。**在程序中,通常不会自定义该类异常,而是直接使用系统提供的异常类。该异常我们必须手动在代码里添加捕获语句来处理该异常。

4.常见的运行时异常

常见的运行时异常分类_运行时异常有哪些_-CSDN博客

都是java.lang包下的

  • NullPointerException空指针异常:调用了未经初始化的对象或者是不存在的对象-
  • ArrayIndexOutBoundException数组下标越界异常
  • ClassCastException类型转换异常
  • ArithmeticExecption算术异常
  • NumberFormatException 数字格式化异常 (字符串转换为数字异常):字符型数据中包含非数字型字符
  • ClassNotFoundException 指定的类找不到 :类的名称和路径加载错误;通常都是程序试图通过字符串来加载某个类时可能引发异常
  • IllegalArgumentException 传递非法参数异常

5.异常如何处理

Java 的异常处理是通过 5 个关键词来实现的:try、catch、throw、throws 和 finally。

5.1异常关键字

? try :用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当try语句块内发生异常时,异常就被抛出。
? catch :用于捕获异常。catch用来捕获try语句块中发生的异常。
? finally :finally语句块总是会被执行。它主要用于回收在try块里打开的物力资源只有finally块,执行完成之后,才会回来执行try或者catch块中的return或者throw语句,如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止。
? throw : 用于抛出异常。
? throws :用在方法签名中,用于声明该方法可能抛出的异常。

5.2 异常处理方法

在Java应用中,异常的处理机制分为

  • 声明异常
  • 抛出异常
  • 捕获异常

我们来看看流程图

5.2.1 声明异常 throws关键字 (不能处理Error和运行时异常)

通常,应该捕获那些知道如何处理的异常,**将不知道如何处理的异常继续传递下去。**传递异常可以在方法签名处使用 throws 关键字声明可能会抛出的异常。

注意:

  • 非检查异常(Error、RuntimeException 或它们的子类)不可使用 throws 关键字来声明要抛出的异常。
  • 一个方法出现编译时异常,就需要 try-catch/ throws 处理,否则会导致编译错误。
//举例
private static void readFile(String filePath) throws IOException {
    File file = new File(filePath);
    String result;
    BufferedReader reader = new BufferedReader(new FileReader(file));
    while((result = reader.readLine())!=null) {
        System.out.println(result);
    }
    reader.close();
}
5.2.2 抛出异常 throw关键字 一般放在方法体里面

?解决不了异常时,且不需要调用者处理,我们就用throw抛出运行时异常

如果你觉得解决不了某些异常问题,且不需要调用者处理,那么你可以抛出异常。throw关键字作用是在方法内部抛出一个Throwable类型的异常。任何Java代码都可以通过throw语句抛出异常。

5.2.3 捕获异常 try/catch/finally关键字 (经常用来处理运行时异常)

?捕获知道如何处理的异常

程序通常在运行之前不报错,但是运行后可能会出现某些未知的错误,但是还不想直接抛出到上一级,那么就需要通过try…catch…的形式进行异常捕获,之后根据不同的异常情况来进行相应的处理。

😊捕获多个异常时,总是优先捕获最具体的异常类,并将不太具体的 catch 块添加到列表的末尾。

//在一个 try-catch 语句块中可以捕获多个异常类型,并对不同类型的异常做出不同的处理
private static void readFile(String filePath) {
    try {
        // code
    } catch (FileNotFoundException e) {
        // handle FileNotFoundException
    } catch (IOException e){
        // handle IOException
    }
}
//同一个 catch 也可以捕获多种类型异常,用 | 隔开
若处理IOExceptionNumberFormatException的代码是相同的,所以我们可以把它两用|合并到一起
private static void readFile(String filePath) {
    try {
        // code
    } catch (IOException | NumberFormatException e) {
        //  IOException或NumberFormatException
        // 相同的处理逻辑
    } catch (IOException e){
        // handle IOException
    }
}
5.2.4 自定义异常

习惯上,定义一个异常类应包含两个构造函数:

  • 一个无参构造函数
  • 一个带有详细描述信息的构造函数(Throwable 的 toString 方法会打印这些详细信息,调试时很有用)
public class MyException extends Exception {
    public MyException(){ }
    public MyException(String msg){
        super(msg);
    }
}
5.2.5 最佳实践
1. 优先捕获最具体的异常类,将不太具体的 catch 块添加到列表末尾

在多个catch的时候,catch的顺序非常重要:子类必须写在前面

多catch语句

  • 可以使用多个catch语句,每个catch分别捕获对应的Exception及其子类。

  • JVM在捕获到异常后,会从上到下匹配catch语句,匹配到某个catch后,执行catch代码块,然后不再 继续匹配。

简单地说就是:多个catch语句只有一个能被执行。例如

😊eg:如果首先捕获 IllegalArgumentException ,则永远不会到达应该处理更具体的 NumberFormatException 的 catch 块,其是 IllegalArgumentException 的子类。

2. 在 finally 块中清理资源

也就是说我们要在finally块中关闭我们的输入流等资源!

3.优先明确异常

😊抛出的异常越明确越好

  • 永远记住,你的同事或者几个月之后的你,将会调用你的方法并且处理异常。
  • 因此需要保证提供给他们尽可能多的信息。这样你的 API 更容易被理解。你的方法的调用者能够更好的处理异常并且避免额外的检查。
  • 例如,抛出一个 NumberFormatException 来替换一个 IllegalArgumentException 。避免抛出一个不明确的异常。 NumberFormatException 是IllegalArgumentException的子类

6.try、catch、finally使用

详见这篇博客

try-catch-finally | 加上return语句时执行顺序:https://blog.csdn.net/m0_48904153/article/details/126651573

文章来源:https://blog.csdn.net/m0_48904153/article/details/134918537
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。