异常处理机制
目录
抛出异常
当一个方法出现错误引发异常时,方法创建异常对象并交付运行时系统,异常对象中包含了异常类型和异常出现时的程序状态等异常信息。运行时系统负责寻找处置异常的代码并执行。
throw与throws的用法
throws:通常被用在声明方法时,用来指定方法可能抛出的异常,多个异常可使用逗号分隔。throws关键字将异常抛给上一级,如果不想处理该异常,可以继续向上抛出,但最终要有能够处理该异常的代码。
public class Shoot {
static void pop()throws NegativeArraySizeException{
int[] arr = new int[-3];
}
public static void main(String[] args) {
try{
pop();
}catch(NegativeArraySizeException e){
System.out.println("pop()方法抛出的异常");
}
}
}//输出:pop()方法抛出异常
throw:通常用在方法体中或者用来抛出用户自定义异常,并且抛出一个异常对象。程序在执行到throw语句时立即停止,如果要捕捉throw抛出的异常,则必须使用try-catch语句块或者try-catch-finally语句。
public class TestException {
public static void main(String[] args) {
int a = 6;
int b = 0;
try {
if (b == 0) throw new ArithmeticException(); // 通过throw语句抛出异常
System.out.println("a/b的值是:" + a / b);
}
catch (ArithmeticException e) { // catch捕捉异常
System.out.println("程序出现异常,变量b不能为0。");
}
System.out.println("程序正常结束。");
}
}//输出:程序出现异常,变量b不能为0
//输出:程序正常执行
捕获异常
在方法抛出异常之后,运行时系统将转为寻找合适的异常处理器(exception handler)。潜在的异常处理器是异常发生时依次存留在调用栈中的方法的集合。当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,即为合适的异常处理器。运行时系统从发生异常的方法开始,依次回查调用栈中的方法,直至找到含有合适异常处理器的方法并执行。当运行时系统遍历调用栈而未找到合适的异常处理器,则运行时系统终止。同时,意味着Java程序的终止。
package Exception;
public class exception4 {
public exception4() {
}
boolean testEx() throws Exception {
boolean ret = true;
try {
ret = testEx1();
} catch (Exception e) {
System.out.println("testEx, catch exception");
ret = false;
throw e;
} finally {
System.out.println("testEx, finally; return value=" + ret);
return ret;
}
}
boolean testEx1() throws Exception {
boolean ret = true;
try {
ret = testEx2();
if (!ret) {
return false;
}
System.out.println("testEx1, at the end of try");
return ret;
} catch (Exception e) {
System.out.println("testEx1, catch exception");
ret = false;
throw e;
} finally {
System.out.println("testEx1, finally; return value=" + ret);
return ret;
}
}
boolean testEx2() throws Exception {
boolean ret = true;
try {
int b = 12;
int c;
for (int i = 2; i >= -2; i--) {
c = b / i;
System.out.println("i=" + i);
}
return true;
} catch (Exception e) {
System.out.println("testEx2, catch exception");
ret = false;
throw e;
} finally {
System.out.println("testEx2, finally; return value=" + ret);
return ret;
}
}
public static void main(String[] args) {
exception4 testException1 = new exception4();
try {
testException1.testEx();
} catch (Exception e) {
e.printStackTrace();
}
}
}
//输出:
//testEx2, catch exception
//testEx2,finally; return value=false
//testEx1,finally; return value=false
//testEx,finally; l return 'value=false
处理顺序
从具体到一般:当你有多个catch
(或except
)块时,应该按照从具体到一般的顺序来排列它们。这意呀着你应该首先捕获最具体的异常类型,然后逐渐向下捕获更一般的异常类型。这是因为如果先捕获了一个一般性的异常,那么更具体的异常就永远不会被捕获到。
避免捕获Exception
:尽管Exception
是所有异常类的基类(在Java和C#中),但直接捕获Exception
通常被认为是一个不好的做法,因为它会捕获到所有的异常,包括那些你可能不想或不应该捕获的异常。更好的做法是只捕获那些你能够处理或需要处理的异常类型。
确保资源释放:使用finally
块(或在支持的语言中使用自动资源管理的结构,如Java的try-with-resources
)来确保无论是否发生异常,资源(如文件句柄、数据库连接等)都能被正确释放。
异常传播
在Java中,异常传播(Exception Propagation)是一个非常重要的概念,它描述了当一个方法内部发生异常时,这个异常如何被传递到方法的调用者,并最终可能由Java虚拟机(JVM)来处理的过程。异常传播是Java异常处理机制的核心之一,它允许程序在遇到错误时能够优雅地恢复或终止,而不是突然崩溃。
异常传播的基本规则:
-
抛出异常:当一个方法内部发生错误时,它会创建并抛出一个异常对象。这个异常可以是Java预定义的异常类(如
NullPointerException
、IOException
等)的实例,也可以是自定义异常类的实例。 -
查找处理器:一旦异常被抛出,JVM会暂停当前方法的执行,并开始在调用栈中查找可以处理该异常的代码。这个查找过程是从当前方法开始,向上(即向调用者方向)逐级进行的。
-
匹配处理器:在调用栈中,JVM会查找与抛出的异常类型相匹配的
catch
块。如果找到了匹配的catch
块,那么异常就会被传递到这个catch
块中,然后执行该catch
块中的代码。如果没有找到匹配的catch
块,那么JVM会继续向上查找,直到找到为止。 -
异常处理:一旦找到了匹配的
catch
块,异常就被认为是被“捕获”了。然后,catch
块中的代码会被执行,用于处理异常。这个过程可能包括记录错误日志、向用户显示错误消息、尝试恢复错误等。 -
继续执行:如果
catch
块后面还有代码(即没有return
、throw
等语句终止当前方法的执行),那么这些代码会在异常处理完成后继续执行。但是,请注意,由于异常打断了正常的控制流,因此通常建议在catch
块后面使用return
语句来明确退出方法,除非确实需要继续执行后续代码。 -
未捕获的异常:如果JVM在调用栈的顶端(即
main
方法或线程启动方法)仍然没有找到匹配的catch
块,那么JVM会打印出异常的堆栈跟踪信息,并终止程序的执行(对于非守护线程)。
异常传播与throws
关键字:
在Java中,如果一个方法不处理它抛出的异常,那么它必须使用throws
关键字来声明这些异常。这样,该方法的调用者就知道需要处理这些异常,或者再次声明这些异常(使用throws
),或者在一个try-catch
块中捕获它们。
throws
关键字不会处理异常,它只是将异常向上传播给方法的调用者。这是处理那些在当前上下文中无法合理处理或不应该由当前方法处理的异常的一种方式。