跳到主要内容

异常处理

抛出异常和捕获异常的区别

抛出异常(Throwing Exceptions)

抛出异常是当程序中的某个方法无法处理某个错误时,创建一个异常对象并将其交给运行时系统的过程。通常使用 throw 关键字来完成这一操作。可以抛出系统定义的异常,如 NullPointerExceptionIOException,也可以创建自定义异常。当一个方法抛出异常时,可以在方法签名中使用 throws 关键字声明可能抛出的异常类型。

public void myMethod() throws CustomException {
if (someCondition) {
throw new CustomException("Some condition is not met");
}
}

捕获异常(Catching Exceptions)

捕获异常指的是在程序中使用 try-catch 块来捕获并处理抛出的异常。在 try 块中放置可能会抛出异常的代码,如果异常发生,程序将跳转到相应的 catch 块执行处理逻辑。捕获异常使程序能够继续执行下去,而不会因为未处理的异常而中断。

try {
myMethod();
} catch (CustomException e) {
e.printStackTrace();
}

自定义异常

可以通过继承 Exception 类或其子类来创建自定义异常类,以表示程序中特定的异常条件。这有助于提高代码的可读性和可维护性,明确指出异常的具体原因。

public class CustomException extends Exception {
public CustomException(String message) {
super(message);
}
}

异常类型匹配

在处理异常时,理解异常类型的匹配顺序至关重要。当 try 块中可能抛出多种异常时,应该为不同的异常编写不同的处理逻辑。以下几点需要注意:

首先,catch 块按照出现的顺序进行匹配。一旦某个 catch 块匹配了异常类型,后续的 catch 块将不会被检查。其次,如果有异常类的继承关系,应该先捕获子类异常,再捕获父类异常。例如,FileNotFoundException 应该在 IOException 之前被捕获,因为它是 IOException 的子类。最后,可以使用 finally 块执行无论是否抛出异常都需要执行的代码。

try {
// 可能会抛出异常的代码
} catch (FileNotFoundException e) {
// 处理 FileNotFoundException 的代码
} catch (IOException e) {
// 处理 IOException 的代码
} catch (Exception e) {
// 处理其他类型异常的代码
}

发生异常的底层逻辑

当异常发生时,系统会按照以下步骤处理:

当异常发生,系统会调用对应的异常类创建异常实例对象。如果在 try 块中有异常发生,程序会进入相应的 catch 块匹配处理该异常。如果没有匹配的 catch 块,虚拟机会中断程序并抛出异常。

finally

finally 块用于执行无论是否发生异常都需要执行的代码,例如释放资源。无论 try 块中是否抛出异常,finally 块中的代码都会被执行。

public class TestFinally {
public static void main(String[] args) {
try {
int a = 1 / 2;
} catch (Exception e) {
System.out.println(e);
} finally {
System.out.println("finally");
}
}
}

try 中的程序是怎样运行的

try 块中的程序按照顺序执行。当程序执行到可能引发异常的代码时,如果异常发生,程序会跳转到相应的 catch 块。如果存在 finally 块,无论是否发生异常,都会执行 finally 中的代码。最后,方法返回值(如果有)会被处理。

package com.Exception;

/*
* final:声明常量
* finalize:处理垃圾回收
* finally:异常处理的出口
*/

public class TestFinallyExample {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5};
int el = getArrElement(arr, 4);
System.out.println(el);
}

public static int getArrElement(int[] arr, int index) {
try {
return arr[index];
} catch (Exception e) {
System.out.println("数组下标越界");
} finally {
System.out.println("finally");
}
return -1;
}
}

打印异常 logger 示例

在实际应用中,记录异常信息是非常重要的。以下示例展示了如何使用日志记录器来捕获和记录异常信息。

package com.Exception;

import java.util.ArrayList;

public class TestLogger {
public static void main(String[] args) {
ArrayList<Logger> loggerList = new ArrayList<>();

int a = 0, b = 0;
Logger logger = null;

try {
a = 5;
int res = division(a, b);
logger = new Logger(a, b, res);
} catch (Exception e) {
System.out.println(e.getMessage());
logger = new Logger(a, b, e);
} finally {
loggerList.add(logger);
}

try {
a = 10;
b = 2;

int res = division(a, b);
logger = new Logger(a, b, res);
} catch (Exception e) {
System.out.println(e);
logger = new Logger(a, b, e);
} finally {
loggerList.add(logger);
}
System.out.println(loggerList);
}

public static int division(int a, int b) throws Exception {
return a / b;
}
}

catch 捕获的 e 上的常见方法

getMessage()

当需要获取异常的错误信息时,getMessage() 方法非常有用,尤其是在记录错误或将错误信息展示给用户时。

try {
// 可能引发异常的代码
} catch (Exception e) {
System.out.println("An error occurred: " + e.getMessage());
}

printStackTrace()

在调试时,printStackTrace() 方法非常有用,因为它详细显示了异常的来源。

try {
// 可能引发异常的代码
} catch (Exception e) {
e.printStackTrace(); // 打印错误轨迹到控制台
}

getStackTrace()

当需要对异常的堆栈轨迹进行更高级的处理时,例如分析堆栈信息或将其写入自定义日志格式,可以使用 getStackTrace() 方法。

try {
// 可能引发异常的代码
} catch (Exception e) {
StackTraceElement[] elements = e.getStackTrace();
for (StackTraceElement element : elements) {
System.out.println(element.getClassName() + " - " + element.getMethodName() + " : " + element.getLineNumber());
}
}

getCause()

在处理包装异常时,getCause() 方法特别有用,可以追溯到“根异常”。

try {
// 可能引发异常的代码
} catch (Exception e) {
Throwable cause = e.getCause();
if (cause != null) {
System.out.println("Root cause: " + cause.toString());
}
}

toString()

当需要打印异常类的名称以及详细信息时,可以使用 toString() 方法,特别是在记录异常信息而不需要堆栈轨迹时。

try {
// 可能引发异常的代码
} catch (Exception e) {
System.out.println("Exception caught: " + e.toString());
}

getLocalizedMessage()

当应用程序需要支持多语言,即国际化时,getLocalizedMessage() 方法非常有用。

try {
// 可能引发异常的代码
} catch (Exception e) {
String localizedMessage = e.getLocalizedMessage();
System.out.println("Localized error message: " + localizedMessage);
}

fillInStackTrace()

当需要重新抛出一个异常,并保留当前调用堆栈信息时,可以使用 fillInStackTrace() 方法。

try {
// 可能引发异常的代码
} catch (Exception e) {
Throwable t = e.fillInStackTrace();
throw t; // 重新抛出异常,包含当前堆栈信息
}