Java异常处理

Java异常的三种分类

  1. 受检异常(Checked Exceptions):最具代表性的是用户错误和问题引起的异常,程序员无法遇见,这些异常在编译时强制要求进行处理,比如打开一个不存在的文件。如果一个方法可能会引发受检异常,要么在方法签名中使用throws子句声明,要么使用try-catch块捕获并处理这些异常。例如,IOException就是一个受检异常,它需要在代码中进行显式处理。
  2. 运行时异常(Runtime Exceptions):这些异常是RuntimeException类及其子类的实例。与受检异常不同,编译时会被忽略,运行时异常不需要显式地捕获或声明。常见的运行时异常包括NullPointerExceptionArrayIndexOutOfBoundsExceptionArithmeticException等。
  3. 错误(Errors):错误通常指示了严重的问题,大多数情况下是程序无法处理的。与异常不同,错误通常不应该被捕获和处理。常见的错误包括StackOverflowErrorOutOfMemoryError等。

异常体系结构

Java的异常体系结构是基于Throwable类的,java.lang.Throwable作为所有异常的超类

Error

Error类是Java编程语言中表示严重问题的基类之一。

在Java中,Error对象表示的是系统级的错误和运行时环境的问题,通常由Java虚拟机(JVM)生成和抛出。

与一般的异常不同,Error及其子类通常表示虚拟机自身的问题,而不是应用程序的问题。

一些常见的Error子类包括:

  • OutOfMemoryError:当JVM无法再分配内存时抛出,表示内存耗尽。
  • StackOverflowError:当方法调用的层级太深,导致栈空间耗尽时抛出。
  • NoClassDefFoundError:当JVM或ClassLoader无法找到所需的类定义时抛出。
  • InternalError:表示JVM内部发生了严重错误。

Exception

Exception类是Java编程语言中表示一般意外情况或错误的基类之一。

在Java中,Exception及其子类通常用于表示程序运行过程中可能发生的各种异常情况。

Exception类的直接子类主要分为两种:

  • Checked Exception:这些异常必须在代码中进行处理,要么通过try-catch块捕获和处理,要么通过throws子句声明向上传播。
  • RuntimeException:这些异常通常是由程序错误引起的,不要求在代码中强制处理。

一些常见的 Checked Exception:

  1. IOException:用于表示输入输出操作中可能发生的异常情况,如文件未找到、文件权限问题等。

  2. SQLException:用于表示与数据库访问相关的异常,例如连接问题、查询问题等。

  3. ClassNotFoundException:用于表示在运行时找不到特定类的异常。

  4. FileNotFoundException:指示尝试打开指定文件名的文件失败。

  5. ParseException:用于表示解析操作中可能发生的异常,例如日期解析错误。

  6. NoSuchMethodException:指示尝试访问不存在的方法时发生的异常。

一些常见的 RuntimeException:

  1. NullPointerException:当应用程序试图在要求使用对象的地方使用 null 时,抛出该异常。

  2. ArrayIndexOutOfBoundsException:用于指示数组索引超出范围。

  3. ArithmeticException:表示在算术运算中发生的异常情况,例如除以零。

  4. IllegalArgumentException:指示向方法传递了一个不合法或不正确的参数。

  5. IllegalStateException:表示对象的状态不适合请求的操作。

  6. ClassCastException:当试图将对象强制转换为不是实例的子类时,抛出该异常。

异常处理机制

  1. try-catch块:try 用于包裹可能会引发异常的代码块,catch 用于捕获并处理特定类型的异常。如果 try 块中的代码引发了异常,Java 将会跳到匹配的 catch 块中执行相应的处理代码。

    java 复制代码
    try {  
        // 可能引发异常的代码  
    } catch (ExceptionType e) {  
        // 处理异常的代码  
    }  
  2. finally块:finally 块中的代码总是会被执行,无论是否发生异常。通常用于确保资源的释放,如关闭文件或网络连接。

    java 复制代码
    try {  
        // 可能引发异常的代码  
    } catch (ExceptionType e) {  
        // 处理异常的代码  
    } finally {  
        // 无论是否发生异常都会执行的代码  
    }  
  3. throws关键字:throws 用于在方法声明中指定可能会抛出的异常,将异常的处理责任交给调用者。这就是告诉调用者,调用这个方法时,需要考虑到可能会抛出的异常情况。

    java 复制代码
    void someMethod() throws SomeException {  
        // 方法可能抛出 SomeException  
    }  
    java 复制代码
    void readFile() throws IOException {  
        // 读取文件的代码  
    }  

    在这个例子中,readFile 方法声明了可能会抛出 IOException 异常。当其他代码调用 readFile 方法时,调用者需要考虑到可能会出现 IOException 异常的情况。

  4. throw关键字:throw 用于手动抛出一个异常。这意味着当程序执行到 throw 语句时,会创建一个异常对象并将其抛出。这个异常可以是 Java 标准库中已经定义好的异常类型,也可以是你自己定义的异常类型。

    java 复制代码
    if (someCondition) {  
        throw new SomeException("Error message");  
    }  

    举个例子,假设你正在编写一个函数,需要检查传入的参数是否满足某些条件,如果条件不满足,你可以使用 throw 抛出一个异常来中断程序的执行并给出相应的提示或错误信息。

    java 复制代码
    void checkAge(int age) {  
        if (age < 0) {  
            throw new IllegalArgumentException("Age cannot be negative");  
        }  
    }  

    在这个例子中,如果传入的年龄是负数,throw 语句将会创建一个 IllegalArgumentException 异常对象并将其抛出。

  5. 自定义异常:可以通过扩展 Exception 类或其子类来创建自定义异常类,以便应用程序可以根据特定的需求创建和使用异常类型。

    如果不加throws MyException会报错,这是因为:在Java中,如果一个方法可能抛出已检查异常(checked exception),但没有使用try-catch块捕获该异常,也没有使用throws子句将该异常传播出去,编译器会报错。

几种异常处理方式的区别:

  1. throw 用于手动抛出异常,throws 用于声明可能会抛出的异常类型,而 try-catch 用于实际处理异常情况。
  2. throwtry-catch 是用于实际处理异常的,而 throws 是在方法声明中指明可能会抛出的异常类型。

举例:

java 复制代码
import java.io.BufferedReader;  
import java.io.FileReader;  
import java.io.IOException;  
  
public class FileHandlingExample {  
    public static void main(String[] args) {  
        BufferedReader reader = null;  
        try {  
            reader = new BufferedReader(new FileReader("example.txt"));  
            String line = reader.readLine();  
            int number = Integer.parseInt(line);  
            System.out.println("Read number: " + number);  
        } catch (FileNotFoundException e) {  
            System.err.println("File not found: " + e.getMessage());  
        } catch (NumberFormatException e) {  
            System.err.println("Invalid number format: " + e.getMessage());  
        } catch (IOException e) {  
            System.err.println("IOException: " + e.getMessage());  
        } finally {  
            try {  
                if (reader != null) {  
                    reader.close();  
                }  
            } catch (IOException e) {  
                System.err.println("Error while closing the reader: " + e.getMessage());  
            }  
        }  
    }  
}  

在这个例子中,我们尝试打开一个文件并读取其中的内容。在 try 块中,我们打开文件并尝试读取文件中的一行内容,并将其转换为整数。在 catch 块中,我们分别捕获了 FileNotFoundException(文件未找到)、NumberFormatException(无效的数字格式)和 IOException(输入输出异常)。

finally 块中,我们确保关闭了文件读取器,即使在 trycatch 块中发生了异常,也会执行这一步。这是非常重要的,因为它确保资源得到了正确的释放。

Java 编译器会按照 catch 块的顺序来匹配异常,而一旦找到了匹配的 catch 块,后续的 catch 块将不再执行。 因此,如果将最一般的异常类型放在最前面,那么可能会导致后续的 catch 块永远无法执行。

在前面提供的示例代码中,我们先捕获了 FileNotFoundException,然后是 NumberFormatException,最后是 IOException。这是符合最佳实践的顺序。

如果按照相反的顺序,即先捕获 IOException,然后是 NumberFormatException,最后是 FileNotFoundException,那么由于 IOExceptionFileNotFoundExceptionNumberFormatException 的父类,IOException 的 catch 块将会捕获到所有这些异常,而后续的 catch 块将无法执行。

因此,捕获异常时应该遵循从最具体的异常类型到最一般的异常类型的顺序,以确保程序能够按预期进行异常处理。

书写异常处理语句的快捷键:ctrl+alt(option)+T

手动结束程序语句:

实际使用中的经验总结:

  1. 处理运行时异常时,采用逻辑去合理规避同时辅助try-catch处理
  2. 在多重catch块后面,可以加上一个catch(Exception)来处理可能会被遗漏的异常
  3. 对于不确定的代码,也可以加上try-catch处理异常
  4. 尽量去处理异常,切忌仅使用printStackTrace()打印输出
  5. 具体处理异常,根据业务需求和异常类型的不同决定
  6. 尽量添加finally释放占用的资源(IO、Scanner)
相关推荐
Scott9999HH7 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔7 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社8 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海8 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖9 小时前
JDK 26 新特性详解
java·开发语言
马优晨10 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区11 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大12 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai12 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说12 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json