java开发:异常你了解多少
一、异常体系
1、error/exception
异常是 Throwable 这个父类实现的,下面有两大子类,Error与Exception
Error表示错误,exception表示异常
Error类以及他的子类的实例,代表了JVM本身的错误。错误不能被程序员通过代码处理,
Exception以及他的子类,代表程序运行时发送的各种不期望发生的事件。可以被Java异常处理机制使用,是异常处理的核心。
2、unckecked exception/checked exception
非检查异常(unckecked exception):
Error 和 RuntimeException 以及他们的子类。javac在编译时,不会提示和发现这样的异常,不要求在程序处理这些异常。所以如果愿意,我们可以编写代码处理(使用try…catch…finally)这样的异常,也可以不处理。对于这些异常,我们应该修正代码,而不是去通过异常处理器处理 。这样的异常发生的原因多半是代码写的有问题。如除0错误ArithmeticException,错误的强制类型转换错误ClassCastException,数组索引越界ArrayIndexOutOfBoundsException,使用了空对象NullPointerException等等。
检查异常(checked exception):
除了Error 和 RuntimeException的其它异常。javac强制要求程序员为这样的异常做预备处理工作(使用try…catch…finally或者throws)。在方法中要么用try-catch语句捕获它并处理,要么用throws子句声明抛出它,否则编译不会通过。这样的异常一般是由程序的运行环境导致的。因为程序可能被运行在各种未知的环境下,而程序员无法干预用户如何使用他编写的程序,于是程序员就应该为这样的异常时刻准备着。如SQLException , IOException,ClassNotFoundException 等。
二、异常使用
1、运行java异常处理机制
- try…catch语句
- finaly 任何情况下都会执行(健壮性)
- throws 方法声明处抛出多个异常,用逗号隔开【public void foo() throws ExceptionType1 , ExceptionType2 ,ExceptionTypeN】
- throw 抛出异常
2、异常处理原理
java虚拟机用方法调用栈(method invocation stack)来跟踪每个线程中一系列的方法调用过程。该堆栈保存了每个调用方法的本地信息(比如方法的局部变量)。每个线程都有一个独立的方法调用栈。对于Java应用程序的主线程,堆栈底部是程序的入口方法main()。当一个新方法被调用时,Java虚拟机把描述该方法的栈结构置入栈顶,位于栈顶的方法为正在执行的方法。
当一个方法正常执行完毕,Java虚拟机会从调用栈中弹出该方法的栈结构,然后继续处理前一个方法。如果在执行方法的过程中抛出异常,则Java虚拟机必须找到能捕获该异常的catch代码块。它首先查看当前方法是否存在这样的catch代码块,如果存在,那么就执行该catch代码块;否则,Java虚拟机会从调用栈中弹出该方法的栈结构,继续到前一个方法中查找合适的catch代码块。在回溯过程中,如果Java虚拟机在某个方法中找到了处理该异常的代码块,则该方法的栈结构将成为栈顶元素,程序流程将转到该方法的异常处理代码部分继续执行。当Java虚拟机追溯到调用栈的底部的方法时,如果仍然没有找到处理该异常的代码块,按以下步骤处理。
(1)调用异常对象的printStackTrace()方法,打印来自方法调用栈的异常信息。
(2)如果该线程不是主线程,那么终止这个线程,其他线程继续正常运行。如果该线程是主线程(即方法调用栈的底部为main()方法),那么整个应用程序被终止。
总结:方法进栈,只要没运行完就一直进栈,运行完出栈,一旦出现问题,找catch,当前代码块没找到就出栈,找到执行catch,未找到判断是否是主线程,不是则杀死当前线程,其他安全,是则退出。
3、异常流程的运行过程
finaly不执行的情况:
try{
System.out.println("try");
System.exit(0);
}catch (Exception e){
}finally {
System.out.println("finally");
}
System.exit(0);关闭虚拟机,不会执行finally
catch块中有catch
public static void bar2()
{
try {
System.out.println("try");
int a = 5 / 0;
} catch (Exception e){
System.out.println("catch");
throw new NullPointerException();
}finally {
System.out.println("finally");
}
public static void main(String[] args){
try {
bar2();
}catch (Exception e){
System.out.println("out catch");
}
}
执行结果
try
catch
finally
out catch
finally代码块会在return之前执行:
public static void main(String[] args){
int i = bar3();
System.out.println(""+i);
}
try{
return 1;
} catch (Exception e){
System.out.println("catch");
} finally{
System.out.println("finally");
}
return 0;
输出:
catch
finally
1
但是无法在finaly中改变返回值
public static int bar3(){
int a = 4;
try{
return a;
} catch (Exception e){
System.out.println("catch");
} finally{
a++;
System.out.println("finally");
}
return a;
}
输出:
finally
4
finally会在return前执行,但也无法改变return变量的值。
finally中的return 会覆盖 try 或者catch中的返回值。
int m = foo();
System.out.println(""+m);
int n = bar();
System.out.println(""+n);
}
public static int foo() {
try{
int a = 5 / 0;
} catch (Exception e){
System.out.println("catch");
return 1;
} finally{
System.out.println("finally");
return 2;
}
}
public static int bar()
{
try {
System.out.println("try");
return 1;
}finally {
System.out.println("finally");
return 2;
}
}
输出:
catch
finally
2
try
finally
2
finally中有return 会导致catch中的异常丢失:
public static int bar2()
{
try {
System.out.println("try");
int a = 5 / 0;
} catch (Exception e){
System.out.println("catch");
throw new NullPointerException();
}finally {
System.out.println("finally");
return 1;
}
}
结果:finally中绝对不要使用return语句