异常
1. 什么是异常
1.1 异常的概念
在Java中,异常(Exception)是指程序执行过程中可能出现的不正常情况或错误。它是一个事件,它会干扰程序的正常执行流程,并可能导致程序出现错误或崩溃。
异常在Java中是以对象的形式表示的,这些对象是从java.lang.Throwable类或其子类派生而来。Throwable是异常类层次结构的根类,它有两个主要的子类:java.lang.Exception和java.lang.Error。
Exception(异常):java.lang.Exception是表示可检查异常的基类。可检查异常是指在编译时需要显式处理的异常。Exception类及其子类用于表示程序运行过程中可能出现的外部条件、错误或其他可恢复的情况。例如,文件未找到、网络连接中断、输入格式错误等。开发人员需要通过捕获或声明这些异常来确保在程序中进行适当的异常处理。
Error(错误):java.lang.Error是表示严重问题或系统级错误的基类。错误是指那些程序通常无法处理或恢复的情况,例如内存溢出、堆栈溢出、虚拟机错误等。与异常不同,错误不需要在程序中显式处理,因为它们通常表示了无法解决的问题。
异常在Java中通过抛出(throw)和捕获(catch)的方式进行处理。当程序执行到可能引发异常的代码时,可以使用throw语句手动抛出异常对象。然后,可以使用try-catch语句块来捕获异常,并在catch块中提供相应的异常处理逻辑。在catch块中,可以根据异常的类型执行适当的操作,如日志记录、错误报告或异常处理。如果异常没有在当前方法中被捕获处理,它将继续向上级调用栈传播,直到找到合适的异常处理代码或导致程序终止。
1.2 异常的分类
在Java中,异常可以按照其类型进行分类。下面是Java中异常的主要分类:
可检查异常(Checked Exceptions):可检查异常是指在编译时会被检查的异常,程序必须显式地处理它们,否则编译器会报错。可检查异常通常表示程序在运行过程中可能出现的外部条件或错误。例如,文件不存在、网络连接问题等。可检查异常是Exception类(及其子类)的实例。
运行时异常(Runtime Exceptions):运行时异常也被称为非检查异常(Unchecked Exceptions)。这些异常在编译时不会被强制检查,而是在程序运行时才会抛出。运行时异常通常表示程序内部的错误或逻辑错误,例如,空指针引用、除以零等。运行时异常是RuntimeException类(及其子类)的实例。
错误(Errors):错误表示Java虚拟机(JVM)本身出现的严重问题,通常无法恢复或处理。错误可能是内存溢出、堆栈溢出等严重问题。与异常不同,错误一般不应该被捕获和处理,因为它们指示了无法解决的问题。错误是Error类(及其子类)的实例。
这些异常类型的区别在于编译器对它们的检查方式以及程序员对它们的处理要求。可检查异常在编译时要求显式处理,要么通过try-catch块捕获并处理,要么通过在方法签名中声明该异常并由调用者处理。运行时异常可以选择捕获和处理,但不是强制要求。而错误通常不应该被捕获和处理。
2. Java异常类结构图
在Java中,异常类的体系结构是通过继承关系组织的。以下是Java异常类的体系结构图及其说明:
Throwable是异常类体系结构的根类。它是所有异常类的超类,直接或间接地派生了Error和Exception两个主要子类。
Error表示严重的问题或系统级错误,它们通常是由Java虚拟机(JVM)本身引起的,例如内存溢出、堆栈溢出等。程序通常无法恢复或处理这些错误。
Exception是表示可检查异常的基类。它包括两个主要的分支:
RuntimeException是运行时异常的基类,它表示程序内部错误或逻辑错误。这些异常通常是由编程错误引起的,例如空指针引用、除以零等。运行时异常在编译时不会被强制检查,因此可以选择捕获和处理它们,但也可以选择不处理。
Exception的其他子类表示其他可检查异常,例如输入/输出异常(IOException)、SQL异常(SQLException)等。这些异常在编译时会被强制检查,程序必须显式地处理它们,否则会导致编译错误。
Java异常类体系结构的组织方式使得开发人员可以根据异常的类型和性质来选择适当的异常类来表示和处理不同类型的异常情况。这种结构使得异常处理更加灵活和可扩展,并且提供了一致的异常处理机制。
3. Exception分类
在Java中,Exception(异常)是程序运行过程中发生的不正常情况,用于指示错误或异常状态的发生。Java的异常体系基于Throwable类,它是所有错误和异常的基类。Exception类及其子类主要用于描述程序中的异常情况,这些异常是程序本身可以处理的。根据Java的异常处理机制,Exception可以分为几个主要类别:
1. 可检查异常(Checked Exceptions)
可检查异常,也称为编译时异常,是Java编译器强制要求程序员必须捕获或声明的异常。这类异常在编译时期就会被检查,如果程序中可能抛出可检查异常,但既没有捕获也没有声明抛出,那么编译器将报错。常见的可检查异常包括:
IOException
:输入输出异常,如文件读写错误。SQLException
:SQL异常,数据库操作错误。ClassNotFoundException
:类未找到异常,尝试加载类时找不到指定的类。
2. 不可检查异常(Unchecked Exceptions)
不可检查异常包括运行时异常(RuntimeException及其子类)和错误(Error)。这类异常编译器不要求必须捕获或声明,但程序员在编写代码时仍然需要关注并尽可能避免这类异常的发生。
运行时异常(RuntimeException及其子类)
NullPointerException
:空指针异常,尝试在需要对象的地方使用null时抛出。IndexOutOfBoundsException
:索引越界异常,访问数组或字符串等集合时使用了无效的索引。ArithmeticException
:算术异常,如整数除零。ClassCastException
:类型转换异常,尝试将对象强制转换为不是实例的子类时抛出。
错误(Error)
错误是JVM内部的严重问题,如内存溢出(OutOfMemoryError
)、堆栈溢出(StackOverflowError
)等。这些错误通常与程序本身的逻辑无关,而是由系统资源或JVM内部问题引起的。程序员通常不需要,也无法在程序中捕获或处理这些错误。
3. 自定义异常
除了Java标准库中的异常类,程序员还可以根据自己的需要定义自定义异常类。自定义异常类通常继承自Exception
或其子类(如RuntimeException
)。通过定义自定义异常,可以更加清晰地表达程序中的特定错误情况,提高代码的可读性和可维护性。
总结
Java的异常体系基于Throwable类,主要分为可检查异常、不可检查异常(包括运行时异常和错误)以及自定义异常。程序员在编写Java程序时,需要根据异常的类型和特点,合理地使用try-catch语句块来捕获和处理异常,以确保程序的健壮性和可靠性。
4. 异常处理机制
Java的异常处理是Java程序设计中不可或缺的一部分,它提供了一种结构化的、统一的方式来处理程序中可能出现的错误。Java的异常处理机制主要包括抛出异常(throwing exceptions)、捕获异常(catching exceptions)和异常传播(exception propagation)。
抛出异常(Throwing Exceptions)
当程序执行过程中遇到无法处理的错误情况时,可以通过抛出异常的方式,将错误信息传递给调用者。在Java中,你可以使用throw
关键字来抛出异常。你可以抛出检查型异常(checked exceptions)或运行时异常(runtime exceptions)。检查型异常在编译时就需要被处理(即捕获或再次抛出),而运行时异常则不需要。
public void doSomething(int number) throws IllegalArgumentException {
if (number < 0) {
throw new IllegalArgumentException("Number must be non-negative");
}
// 正常逻辑
}
捕获异常(Catching Exceptions)
捕获异常是通过try-catch
块来实现的。在try
块中,你放置可能引发异常的代码。在紧随其后的catch
块中,你处理这些异常。你可以有多个catch
块来处理不同类型的异常。
try {
// 尝试执行的代码
doSomething(-1);
} catch (IllegalArgumentException e) {
// 处理IllegalArgumentException异常
System.out.println("An illegal argument was provided: " + e.getMessage());
}
异常传播(Exception Propagation)
异常传播是指当一个方法内部的代码抛出了异常,而这个异常没有被当前方法捕获处理时,异常会被自动传递给该方法的调用者。这个过程会一直持续,直到找到能够处理该异常的catch
块,或者直到异常到达main
方法(如果还没有被捕获),这时程序会终止并打印出异常信息和堆栈跟踪。
异常传播是Java异常处理机制中的一个重要特性,它允许你将错误处理逻辑与业务逻辑分离开来,使得代码更加清晰、易于维护。
public void methodA() {
try {
// 尝试执行的代码
methodB();
} catch (SomeException e) {
// 处理SomeException异常
}
}
public void methodB() throws SomeException {
// 可能抛出SomeException的代码
throw new SomeException("Something went wrong");
}
在上面的例子中,methodB
抛出了SomeException
异常,而methodA
尝试捕获这个异常。如果methodA
没有捕获到异常(或者捕获后又抛出了异常),那么异常会继续向上传播,直到被另一个方法捕获或到达程序的最顶层。
5. 自定义异常
5.1、什么是自定义异常
自定义异常是继承自标准异常类(如 Exception、RuntimeException 或 Throwable)的用户定义的异常类。通过创建自定义异常类,开发者可以自定义异常消息、添加额外的属性和方法,以及更好地组织和处理程序中的异常情况。
5.2、如何创建自定义异常
创建自定义异常类很简单,只需按照以下步骤进行:
步骤 1:选择基类
首先,确定你的自定义异常类应该继承自哪个基类。通常情况下,可以选择继承自 Exception 或 RuntimeException。如果希望异常成为受检异常,需要在方法签名中声明或捕获,那么继承自 Exception 是合适的选择。如果希望异常成为运行时异常,不需要在方法签名中声明或捕获,那么继承自 RuntimeException 是更常见的选择。
步骤 2:定义异常类
接下来,在你的代码中创建一个新的类,并使其继承自所选择的基类。你可以添加自定义的属性、构造函数和方法,以满足特定的需求。确保为异常类提供有意义的错误消息,以便在异常被捕获时提供有用的信息。
下面是一个自定义异常类的示例:
public class FileNotFoundCustomException extends Exception {
public FileNotFoundCustomException(String message) {
super(message);
}
}
在这个示例中,我们创建了一个名为 FileNotFoundCustomException 的自定义异常类,它继承自 Exception。构造函数接受一个异常消息作为参数,并调用父类的构造函数来设置异常消息。
步骤 3:抛出自定义异常
在代码的适当位置,使用 throw 关键字抛出自定义异常。可以在方法中使用条件语句来判断何时抛出异常。当满足特定条件时,使用 throw 关键字创建并抛出自定义异常对象。
下面代码展示了如何抛出自定义异常:
public class FileManager {
public void readFile(String filePath) throws FileNotFoundCustomException {
if (!fileExists(filePath)) {
throw new FileNotFoundCustomException("文件未找到: " + filePath);
}
// 读取文件的逻辑
// ...
}
private boolean fileExists(String filePath) {
// 省略文件存在性检查的逻辑
return false;
}
}
步骤 4:捕获自定义异常
在调用可能抛出自定义异常的方法时,使用 try-catch 块捕获异常。在 catch 块中,提供适当的异常处理逻辑,如打印错误消息、记录日志或采取其他适当的操作。
public class Main {
public static void main(String[] args) {
FileManager fileManager = new FileManager();
try {
fileManager.readFile("path/to/file.txt");
} catch (FileNotFoundCustomException e) {
System.out.println(e.getMessage());
// 其他异常处理逻辑
}
}
}
在上面示例中,在 main 方法中使用 FileManager 类的实例,并在 try-catch 块中捕获 FileNotFoundCustomException 异常。当异常被捕获时,我们可以执行相应的处理逻辑,比如打印异常消息或采取其他措施。
5.3、自定义异常继承哪个类的问题
一般来说,如果你希望自定义的异常是受检异常,需要在方法签名中声明或捕获,那么继承自 Exception 是合适的选择。这样可以明确告诉调用者需要处理该异常。比如处理文件操作时的异常情况。
如果你希望自定义的异常是运行时异常,不需要在方法签名中声明或捕获,那么继承自 RuntimeException 是更为常见的选择。这样可以让异常的使用更加灵活,不需要在每个方法中显式处理。比如处理数学计算中的非法参数异常。
标签:Java,自定义,错误,处理,捕获,异常 From: https://www.cnblogs.com/zpjd/p/18343387