Java 异常相关:异常体系结构,异常与错误的区别等

一、前言

记录时间 [2024-05-21]

本文讲述 Java 异常相关知识,包含异常体系结构,以及异常与错误的区别等。

在 Java 中,异常(Exception)是程序在执行过程中发生的意外事件或错误条件。当程序出现异常时,它会中断正常的执行流程,并且可能导致程序崩溃或产生不正确的结果。异常可以是由程序错误、输入错误、系统故障或其他不可预测的情况引起的。

异常是 Java 程序设计中处理错误的一种重要机制,它帮助开发者识别并应对运行时可能出现的问题,比如文件找不到、网络连接失败、数组越界、空指针访问等。

Java 中的异常通过对象来表示,这些对象是 java.lang.Throwable 类或其子类的实例。

异常处理机制提高了程序的健壮性、稳定性和可维护性。


Java 的异常体系结构建立在 java.lang.Throwable 类的基础上,这是所有异常和错误的顶级父类。从 Throwable 类派生出两个主要的子类:Error Exception,它们分别代表了不同的异常类型和处理策略。


二、异常体系结构

1. Throwable 类层次结构

  • Throwable - 所有异常和错误的基类,包含基本的异常信息描述和传播机制。它有两个直接子类:ErrorException
  • Error - 表示系统级错误,通常是不可恢复的,如虚拟机错误、资源耗尽等。程序不应该试图捕获或处理这类错误,因为它们通常暗示着应用程序无法继续执行下去。例如,VirtualMachineErrorOutOfMemoryError 等属于此类。
  • Exception - 表示程序可以捕获并可能从中恢复的异常情况,它又分为两大类:
    • Runtime Exception - 无需在编译时显示声明。它们通常由编程错误引起,如 NullPointerExceptionArrayIndexOutOfBoundsException 等。
    • Checked Exception - 这类异常在编译时就需要处理,要么通过 try-catch 块捕获,要么用 throws 关键字声明方法可能抛出的异常,让调用者处理。例如,IOExceptionSQLException 等。

2. 异常处理机制

Java 通过 try-catch-finally 语句块来捕获和处理异常。5 个处理机制关键字如下:

  • try - 用于包围可能会抛出异常的代码块。
  • catch - 紧跟在 try 块之后,用于捕获并处理特定类型的异常。
  • finally - 无论是否发生异常,都会执行的代码块,通常用于释放资源等善后工作
  • throw - 用于在代码中手动抛出一个异常实例。
  • throws - 用于声明方法可能抛出的异常类型,将异常处理的责任转移给方法的调用者。

3. 异常处理原则

  • 精确捕获 - 尽量捕获具体的异常类型,而不是捕获过于宽泛的异常。
  • 避免空 catch - 捕获异常后至少要有日志记录或合理的错误处理逻辑,避免异常被默默吞没。
  • 资源清理 - 使用 try-with-resources(Java 7起)或其他方式确保资源被妥善关闭。
  • 适当传播 - 如果当前方法无法处理异常,应该通过 throws 声明将其向上层抛出,直到找到合适的处理位置。

三、异常 Exception

异常(Exception)是程序本身可以处理的异常,进一步分为两大类:运行时异常非运行时异常

异常种类非常丰富,涵盖了各种可能的错误情况。开发者在编写代码时,应根据具体的操作和逻辑需求,合理地处理这些异常,以增强程序的健壮性和用户体验。

1. 运行时异常

运行时异常(Runtime Exception 或 Unchecked Exception)是指那些在 Java 程序编译时不需要捕获或声明的异常。这类异常通常是由程序逻辑错误引起的,比如空指针访问、数组越界等。

Java 虚拟机(JVM)在遇到运行时异常时会自动抛出(throw)这些异常,即使没有通过 try-catch 块显式捕获也不会影响编译过程。开发人员可以选择捕获并处理这些异常,但不是强制要求。

常见类型

常见的运行时异常有如下这些:

  • NullPointerException - 当试图访问或操作一个 null 对象的成员时抛出。
  • ArrayIndexOutOfBoundsException - 访问数组时使用的索引超出数组边界时抛出。
  • ClassCastException - 尝试将对象强制转换为不是该对象实际类型的类或接口时抛出。
  • NumberFormatException - 字符串转换为数字类型时,字符串格式不正确时抛出。
  • ArithmeticException - 算术运算时发生错误,如除以零时抛出。
  • IllegalArgumentException - 方法接收到不合法或不合适的参数时抛出。
  • NullPointerException - 访问或操作 null 对象的属性或方法时抛出。
  • InputMismatchException - 输入的数据类型与预期不符时,在 Scanner 类的使用中常见。

案例分析

下面将以几个简单的代码示例来展示如何遇到运行时异常以及如何处理它们。

示例 1:NullPointerException

public class NullPointerExceptionExample {
    public static void main(String[] args) {
        String text = null;
        try {
            System.out.println(text.length()); // 这里会抛出 NullPointerException
        } catch (NullPointerException e) {
            System.out.println("错误:对象为null,无法获取长度。");
        }
    }
}

在这个例子中,尝试访问一个 null 字符串的长度,这将抛出 NullPointerException。我们通过 try-catch 块捕获并处理这个异常。


示例 2:ArrayIndexOutOfBoundsException

public class ArrayIndexOutOfBoundsExceptionExample {
    public static void main(String[] args) {
        int[] numbers = {1, 2, 3};
        try {
            System.out.println(numbers[3]); // 访问不存在的数组索引
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("错误:数组索引超出界限。");
        }
    }
}

数组下标越界:这段代码试图访问数组 numbers 的第四个元素,但数组只有三个元素,因此会抛出 ArrayIndexOutOfBoundsException,我们通过捕获它来处理这种错误情况。


示例 3:NumberFormatException

public class NumberFormatExceptionExample {
    public static void main(String[] args) {
        String input = "abc";
        try {
            int number = Integer.parseInt(input); // 尝试将非数字字符串转换为整数
            System.out.println(number);
        } catch (NumberFormatException e) {
            System.out.println("错误:输入的字符串无法转换为数字。");
        }
    }
}

这里尝试将一个非数字字符串转换为整数,这将引发 NumberFormatException,我们通过异常处理来优雅地告知用户输入错误。


2. 非运行时异常

非运行时异常,也称为已检查异常(Checked Exception),在 Java 中是一类必须在编译时显式处理的异常

如果代码可能抛出这样的异常:

  • 要么使用 try-catch 块捕获它;
  • 要么用 throws 关键字声明方法可能会抛出这个异常,让调用者去处理。

常见类型

常见的非运行时异常有如下这些:

  • IOException - 输入输出操作时发生的异常,如文件读写错误。
  • SQLException - 数据库访问时发生的异常,如SQL语句执行错误。
  • ClassNotFoundException - 加载类时找不到对应的类定义。
  • FileNotFoundException - 尝试打开一个不存在的文件时抛出。
  • InterruptedException - 线程被中断时抛出,常发生在等待、休眠操作中。

案例分析

下面通过代码示例说明几种常见的非运行时异常处理方式。

示例 1:IOException

import java.io.*;

public class IOExceptionExample {
    public static void main(String[] args) {
        try {
            FileInputStream file = new FileInputStream("file.txt"); // 假设file.txt不存在
            // ...读取文件操作...
        } catch (FileNotFoundException e) {
            System.out.println("错误:文件未找到。");
        } catch (IOException e) {
            System.out.println("错误:读取文件时发生错误。");
        }
    }
}

此例中,尝试打开一个可能不存在的文件,这将抛出 FileNotFoundException,它是 IOException 的子类,也是非运行时异常。我们使用 try-catch 捕获并处理这些异常。


示例 2:SQLException

import java.sql.*;

public class SQLExceptionExample {
    public static void main(String[] args) {
        Connection conn = null;
        Statement stmt = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection(DB_URL, USER, PASS);
            stmt = conn.createStatement();
            String sql = "SELECT id, name FROM employees"; 
            ResultSet rs = stmt.executeQuery(sql);
            // ...处理结果集...
        } catch (ClassNotFoundException | SQLException e) {
            System.out.println("数据库操作错误:" + e.getMessage());
        } finally {
            try {
                if (stmt != null) stmt.close();
                if (conn != null) conn.close();
            } catch (SQLException se2) {
                se2.printStackTrace();
            }
        }
    }
}

这段代码演示了数据库操作可能抛出的 SQLException。由于数据库连接或查询可能会失败,我们通过 try-catch 来处理这些异常,并在 finally 块中确保资源被正确关闭。


四、错误 Error

1. 基本概念

在 Java 中,Error 类及其子类代表了系统级的错误和故障,这些通常是在正常情况下不应试图捕获或处理的异常情况。它们指示了 JVM(Java 虚拟机)本身的严重问题或者应用程序无法恢复的情况。

Exception 不同,Error 不是期望程序员在编写代码时常规处理的错误类型,而是留给系统进行处理或者用于标识应用程序应该立即终止的状况。

Error 体现了 Java 异常体系中那些严重到足以影响程序正常执行流程,且通常超出了应用层面控制范围的错误情况。

2. 常见类型

常见的 Error 类型有如下这些:

  • VirtualMachineError - 这类错误表明 Java 虚拟机遇到了无法克服的问题,如:
    • OutOfMemoryError - 内存耗尽;
    • InternalError - 请求的操作无法完成;
    • VirtualMachineError - 虚拟机本身错误。
  • ThreadDeath - 当线程被终结时抛出,虽然它是一个 Error,但实际上它经常被程序处理,特别是当需要清理资源时。
  • LinkageError - 表示加载类时出现的问题,这可能是因为类的验证、准备或初始化期间的某个链接操作失败。例如,NoClassDefFoundError 表示类定义无法找到。
  • AssertionError - 当使用 assert 关键字进行断言失败时抛出,虽然它是一个 Error,但在开发和测试过程中,断言失败通常被认为是需要修正的编程错误。

3. 处理策略

由于 Error 通常涉及到了系统的底层故障或不可恢复的状态,推荐的做法是尽量避免它们的发生,而不是通过异常处理机制来捕获和处理。

  • 优化程序以减少内存消耗,避免造成 OutOfMemoryError
  • 确保类路径设置正确,防止 NoClassDefFoundError

然而,对于一些特殊场景,比如监控系统健康状态,可能需要通过日志记录等方式来追踪 Error 的发生,以便于诊断问题。


五、总结

本文讲述 Java 异常相关知识,包含异常体系结构,以及异常与错误的区别等。


一些参考资料

狂神说 Java 零基础:https://www.bilibili.com/video/BV12J41137hu/
TIOBE 编程语言走势: https://www.tiobe.com/tiobe-index/
Typora 官网:https://www.typoraio.cn/
Oracle 官网:https://www.oracle.com/
Notepad++ 下载地址:https://notepad-plus.en.softonic.com/
IDEA 官网:https://www.jetbrains.com.cn/idea/
Java 开发手册:https://developer.aliyun.com/ebook/394
Java 8 帮助文档:https://docs.oracle.com/javase/8/docs/api/

  • 29
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值