在java中Throwable类是异常和错误的超类,继承它的是Error和Exception两个子类。一个Error对象表示一个程序错误,指的是底层的不可恢复的严重错误,遇到Error,程序一定会退出,因为已经失去了运行所必须的底层物理环境。对于Error我们无法进行处理。而Exception是由特定因素导致程序无法继续,但不影响虚拟机的正常执行。而Exception又分为运行时异常(未检查异常)和非运行时异常(已检查异常)。运行时异常是由于程序员的疏忽或没有进行必要的检查而引起的程序问题,对于未检查异常可以不处理,编译可以通过。而非运行时异常是不可避免的,对于已检查异常必须做处理,否则编译通不过。对于继承自RuntimeException的异常都算是运行时异常,比如最常见到的IndexOutOfBoundsException或NullPointerException异常,这种异常都是由于程序在写程序时由于疏忽而导致的异常,这种异常编译器不会强制要求处理。
public static void main(String[] args) {
// TODO Auto-generated method stub
int a=4;
int b=0;
System.out.println("a/b:"+a/b);
}
Exception in thread "main" java.lang.ArithmeticException:
at zhang12.DivisionExceptionTest.main
像上述情况就属于运行时异常,编译器不会强制要求去检查除数是否为0,而只有当运行时才能知道是否会抛出异常。
在有可能抛出异常的地方放置try{}语句块,抛出的异常必须在某处得到处理。这个地点就是异常处理程序,而且针对每个要捕获的异常,得准备相应的处理程序。 异常处理程序在try语句后以关键字catch表示。
try{
//code that might generate exceptions
}catch(Exception e1){
//Handle exception1
}catch(Exception e2){
//Handle exception2
}
当异常被抛出时,异常处理机制将负责搜寻参数与异常类型相匹配的第一个处理程序。只有匹配的catch子句才能得到执行。有一点需要注意,不能将父类型的exception的位置写在子类型的excepiton之前。比如:
Object t=null;
try{
if(t==null){
throw new NullPointerException();
}
}catch(Exception e){
}catch(NullPointerException e){
//这里编译不会通过,会提示Unreachable catch block for NullPointerException
}
异常处理的机制:
生成异常对象,包含异常信息。当一个方法中有一条语句出现了异常,它就会throw一个异常对象,然后后面的语句不会执行,而返回上一级方法,其上一级方法接受到了异常对象之后,有可能对这个异常进行处理(进行处理则不会上抛),也可能将这个异常传到它的上一级,如果最上一级(main方法)不处理就会传给虚拟机,虚拟机就会终止程序的运行 。
对于一些代码,可能会希望无论try块中的异常是否抛出,它们都能得到执行。通常可以在异常处理程序后面加上finally子句达到这个效果。 程序样式如:
try{
//might throw the Exception
}catch(Exception e1){
//Handle the Exception e1
}catch(Exception e2){
//Handle the Exception e2
}finally{
//fianlly happen every time
}
有段代码可以很形象的描述出try、catch和finally之间的关系:
class ThreeException extends Exception{
}
public class FinallyWorks {
static int count=0;
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
while(true){
try{
if(count++==0){
throw new ThreeException();
}
System.out.println("No exception");
}catch(ThreeException e){
System.out.println("ThreeException");
}finally{
System.out.println("In finally clause");
if(count==2){
break;
}
}
}
}
}
最后运行的结果是:
ThreeException
In finally clause
No exception
In finally clause
程序中当count为0时首先抛出了个异常,然后执行catch语句后的打印,因此首先打印出一个ThreeException语句。然后catch执行完后接着就执行了finally中的语句打印出In finally clause语句。接着执行try中的语句没有抛出异常,try语句执行完后还是会去执行finally语句,因此又打印出In finally clause。当count为2时,整个程序执行结束。
有一点需要注意就是在reture中使用finally时。
public class MultipleReturns {
public static void f(int i){
System.out.println("Initialization that requires cleanup");
try{
System.out.println("point 1");
if(i==1){
return;
}
System.out.println("end");
}finally{
System.out.println("performing cleanup");
}
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
f(1);
}
}
执行的结果是:
Initialization that requires cleanup
point 1
performing cleanup
通过上个程序可以说明finally语句会在return语句执行之前执行,而return语句之后的语句不会被执行,可以看出finally语句是先于return语句执行的。
下面说说throws/throw的区别:
throw 写在方法内,后面跟一个异常对象
throws 在方法的定义中说明方法可能抛出的异常,后面跟异常类的名字,声明这个方法将不处理异常,把异常交给上一级方法处理
调用时,调用者不能抛出范围更小的异常
throws没有真正处理异常,为了已检查异常的编译通过,真正处理时还是用try-catch
throws时最好用具体异常类
对于方法a,如果它定义了throws Exception。那么当它调用的方法b返回异常对象时,方法a并不处理,而将这个异常对象向上一级返回,如果所有的方法均不进行处理,返回到主方法,如主方法也不进行处理,则到虚拟机中,程序中止
抛出异常的好处是让调用者依据实际情况灵活处理异常
如果在方法的程序中有一行throw new Exception()(包括了已检查异常),那么其后的程序不执行,如果没有对这个可能出现的检查结果进行处理,那么程序就会报错。有一点要注意如果throw后还有finally语句还是会执行的。
throws和throw没有必然的联系
public class Rethrowing {
public static void f() throws Exception{
System.out.println("origination the exception in f()");
throw new Exception("throw from f()");
}
public static void g()throws Exception{
try {
f();
} catch (Exception e) {
System.out.println("Inside g(),e.printStackTrace()");
throw e;
}finally{
System.out.println("finish g method");
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
g();
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.println("main:printStackTrace()");
}
System.out.println("ends main method");
}
}
执行的结果是:
origination the exception in f()
Inside g(),e.printStackTrace()
finish g method
main:printStackTrace()
ends main method
具体的执行流程就不再赘述了。