异常处理
抛出异常和捕获异常的区别
抛出异常(Throwing Exceptions)
抛出异常是当程序中的某个方法无法处理某个错误时,创建一个异常对象并将其交给运行时系统的过程。通常使用 throw
关键字来完成这一操作。可以抛出系统定义的异常,如 NullPointerException
或 IOException
,也可以创建自定义异常。当一个方法抛出异常时,可以在方法签名中使用 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; // 重新抛出异常,包含当前堆栈信息
}