java 异常与错误处理
在C和C++里,程序员必须非常小心编写代码,对于程序发生的错误尤其是运行时未知的,必须得加以各种判断以避免程序错误,这带来了程序的复杂性和开发人员无比痛苦。有没办法使得业务层级错误统一能集中处理,而开发人员能集中处理正常业务代码呢?答案肯定是有,引用异常捕获机制。在现代高级语言里,异常捕获机制是必备的。
java中的错误基类是Throwable,Throwable又分为两大类,Error和Exception。Error是系统级的错误,比如JVM发生内存错误,这时我们一般不用处理这类错误,也处理不了。Exception是异常,是针对业务上发生的异常做处理。所有异常类都继承于Exception。
一、语法定义:
try {
// 业务处理代码
} catch(异常类) {
// 处理异常逻辑
} catch (异常父类) {
// 处理异常逻辑
} finally {
// 无论有没发生异常,这里都会执行,主要用来关闭一些资源
}
由上面的语法可看出,凡是try里的代码发生有异常,则会终止运行并往上抛出,它会自动匹配catch里的异常,找到合适异常会执行异常里的逻辑代码,当然也可以再往上一级抛,则使用throw e; (注:多种异常处理,一定要从子类开始,否则会有编译错误)
二、异常说明:
在java方法体里,如有明显的抛出异常,必须为该方法声明对应的异常说明(除RuntimeException外,因为运行时异常只在运行时才能检测到)。异常说明用throws关键字,如果有多个则用,分隔。具体代码如下:
void fun() throws IOException, IllegalClassFormatException {
if (true) {
throw new IOException();
} else {
throw new IllegalClassFormatException();
}
}
当然,也可以直接用一父类代替,如下:
void fun() throws Exception
...
如果是RuntimeException以及它的子类,则不用写异常说明。
三、异常堆栈:
在每一层级抛出异常时,会记录每一层级的异常信息,这个层级称为帧,每一帧通常代表一个方法,在java里用StackTraceElement堆栈跟踪元素表示,包含信息有发生类名,方法名,行号,文件名等。可通过异常里的getStackTrace()获取数组。代码如下:
class Test {
private static void exec1() {
exec2();
}
private static void exec2() {
throw new RuntimeException();
}
public static void main(String[] args) {
try {
exec1();
} catch (Exception e) {
StackTraceElement[] exData = e.getStackTrace(); // 0: exec2的帧, 1:exec1的帧,2: main的帧
}
}
}
堆栈跟踪信息都是从最底层异常为出发点,一层层往上抛,直到有catch捕获为止;如果需要在exec1为出发点,则使用fillInStackTrace()方法即可,如代码:
void exec1() {
try {
exec2();
} catch(RuntimeException e) {
throw (RuntimeException)e.fillInStackTrace();
}
}
这时则完全抛弃了exec2的帧,但有时我们需要查找根源,java里还有个cause属性可记录由哪里发起的异常,可在构造器里设置,在客户端要通过getCause()获取,如下:
void exec1() {
try {
exec2();
} catch(RuntimeException e) {
throw new RuntimeException(e);
}
}
四、自定义异常:
java已经提供足够丰富的异常类给我们处理,但很多时候针对业务不同,现有的异常类往往不同满足我们,这时就需要自定义异常类。所有自定义异常类都必须继承于Exception根类,(不要继承Throwable,因为此类还包含Error类,Error不是我们要处理的类)。自定义异常类原则上取最接近的现有异常类。
浙公网安备 33010602011771号