欢迎来到 IT实训基地-南通科迅教育
咨询电话:0513-81107100
java的异常处理机制详解
2017/4/12
南通科迅教育
441
南通Web前端辅导班哪个学校好?-Web前端培训 - Web前端课程费用

异常机制已经成为判断一门编程语言是否成熟的标准,异常机制可以使程序中异常处理代码和正常业务代码分离,保证程序代码更加优雅,并提高程序健壮性。
Java异常的处理主要依赖于try,catch,finally,throws,throw这五个关键字。下面分别介绍它们:
1. try:try块中主要放置可能会产生异常的代码块。如果执行try块里的业务逻辑代码时出现异常,系统会自动生成一个异常对象,该异常对象被提交给运行环境,这个过程被称为抛出(throw)异常。Java环境收到异常对象时,会寻找合适的catch块(在本方法或是调用方法),如果找不到,java运行环境就会终止,java程序将退出
2. catch:catch块中放置当出现相应的异常类型时,程序需要执行的代码。当try中语句可能发生多个异常的时候可以由多个catch。
3. finally:finally中存放一定会执行的代码,异常机制保证finally代码总是会被执行。当遇到try或catch中return或throw之类可以终止当前方法的代码时,jvm会先去执行finally中的语句,当finally中的语句执行完毕后才会返回来执行try/catch中的return,throw语句。如果finally中有return或throw,那么将执行这些语句,不会在执行try/catch中的return或throw语句。finally块中一般写的是关闭资源之类的代码。
4. throws:在方法的签名中,用于抛出次方法中的异常给调用者,调用者可以选择捕获或者抛出,如果所有方法(包括main)都选择抛出。那么最终将会抛给JVM。JVM打印出栈轨迹(异常链)。
5. throw:用于抛出一个具体的异常对象。

Java对异常的处理是按异常分类处理的,不同异常有不同的分类,每种异常都对应一个类型(class),每个异常都对应一个异常(类的)对象。java的系统定义的大致的异常类的层次图如下(不全面,比如没有SQLException等):

异常类层次图vcPmoaNKYXZh0uyzo7SmwO27+tbGo6zU2rPM0PLUy9DQs/bP1tLizeLKsaOsz7XNs7vhyfqzydK7uPZFeGNlcHRpb2621M/zo6zAtM2o1qqzzNDyo6w8c3Ryb25nPrTTtvjKtc/WvasmbGRxdW870rXO8bmmxNzKtc/WtPrC6yZyZHF1bzu6zSZsZHF1bzu07c7ztKbA7bT6wusmcmRxdW87t9bA6zwvc3Ryb25nPqOszOG5qbj8usO1xL/JtsHQ1KGjxuTW0GNoZWNrZWTS7LOjzOXP1sHLamF2YcnovMbV3NGno7rDu9PQPHN0cm9uZz7N6snGtKbA7Twvc3Ryb25nPrXEtPrC67j5sb6yu7vhsbvWtNDQo6zM5c/WwctqYXZhtcTRz7330NShozxiciAvPg0KMi4g0uyzo9PQyrLDtNPDo78go6gxo6m/ydLUttS/ycTcs/bP1rXE0uyzo7340NC4/Mflzvq1xLSmwO26zcu1w/ejrLHIyOfU2mZpbmFsbHnW0LnYsdXXytS0u/LBrL3To6y78tXfttSz9s/WtcTS7LOjuPiz9rj8x+XO+rXEzOHKvtDFz6Kho6OoMqOp06bTw9Lss6PAtLSmwO3Stc7xwt+8raOsv8nS1NXiw7TX9qOstavKx9Xi09DOpbGz0uyzo8novMa1xLP11tSjqNLss6PKtdbKyc+/ydLUysfSu7j2aWYgZWxzZdPvvuSjrLWxyLu/ydLU08PX99K1zvG0psDto6mhozwvcD4NCjxociAvPg0KPHA+w/fIt8HLyc/D5sG9uPbOyszi1q6686OsztLDx77NwLS/tNK7z8JqYXZhtcS+38zltcTS7LOjtKbA7bv61sahozwvcD4NCjxoMiBpZD0="1使用trycatch捕获异常">1.使用try…catch捕获异常:

java提出了一种假设,如果try中的语句一切正常那么将不执行catch语句块,如果try中语句出现异常,则会抛出异常对象,由catch语句块根据自己的类型进行捕获。若没有相应的catch块,则抛出。
所以其执行步骤可以总结为以下两点:
(1) 如果执行try块中的业务逻辑代码时出现异常,系统自动生成一个异常对象,该异常对象被提交给java运行环境,这个过程称为抛出(throw)异常。
(2) 当java运行环境收到异常对象时,会寻找能处理该异常对象的catch块,如果找到合适的cathc块并把该异常对象交给catch块处理,那这个过程称为捕获(catch)异常;如果java运行时环境找不到捕获异常的catch块,则运行时环境终止,java程序也将退出。
下面还有几点注意事项需要大家注意:
注意一: 不管程序代码块是否处于try块中,甚至包括catch块中代码,只要执行该代码时出现了异常,系统都会自动生成一个异常对象,如果程序没有为这段代码定义任何catch块,java运行环境肯定找不到处理该异常的catch块,程序肯定在此退出。
注意二: 进行异常捕获时,一定要记住先捕获小的异常,再捕获大的异常。
注意三: 看下面一段java程序,我们来说明java对finally的处理方式:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
importjava.io.FileInputStream;
importjava.io.FileNotFoundException;
importjava.io.IOException;
publicclassTest{
    publicstaticvoidmain(String[] args) {
        FileInputStream fis=null;
        try{
            fis=newFileInputStream("a.txt");
        }catch(FileNotFoundException e) {
            System.out.println(e.getMessage());
            // return语句强制方法返回
            return;
            // 使用exit来退出虚拟机
            //System.exit(1);
        }finally{
            if(fis!=null){
                try{
                    fis.close();
                }catch(IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                fis=null;
            }
            System.out.println("fis资源已被回收");
        }
    }
}

运行这个程序,在catch中使用return而不exit可以得到如下结果:

?
1
2
a.txt (系统找不到指定的文件。)
fis资源已被回收

如果使用exit而不是return,那么将会得到如下结果:

?
1
a.txt (系统找不到指定的文件。)

以上两种情况显示:除非在try块或者catch块中调用了退出虚拟机的方法(即System.exit(1);),否则不管在try块、catch块中执行怎样的代码,出现怎样的情况,异常处理的finally块总是会被执行的。当程序执行try块,catch块时遇到return语句或者throw语句,这两个语句都会导致该方法立即结束,所以系统并不会立即执行这两个语句,而是去寻找该异常处理流程中的finally块,如果没有finally块,程序立即执行return语句或者throw语句,方法终止。如果有finally块,系统立即开始执行finally块,只有当finally块执行完成后,系统才会再次跳回来执行try块、catch块里的return或throw语句,如果finally块里也使用了return或throw等导致方法终止的语句,则finally块已经终止了方法,不用再跳回去执行try块、catch块里的任何代码了。所以,一般情况下,不要在finally块中使用return或throw等导致方法终止的语句,因为一旦使用,将会导致try块、catch块中的return、throw语句失效。
以下四种情况将会导致finally块不执行:
(1)在finally语句块中发生了异常
(2)在前面的代码中使用了System.exit()退出虚拟机
(3)程序所在线程死亡
(4)关闭cpu
- ps:在论坛上看到有朋友问:如果catch捕获了异常,那么try…catch语句块之后的语句是否会执行?
- 答案是如果catch块或finally块中没有throw语句或者return语句,那么try…catch之后的语句就一定会执行。因为异常已经被捕获和处理了呀~为什么后面的语句为什么不能执行呢。
- ps:可能又会有朋友问,如果try…catch块之后的语句中有使用到try中的引用,而try中的语句失败了,后面的怎么执行?
- 放心,如果真的有这种情况,那java一定会要求你讲这些语句和与那些可能失败的语句一起放入try…catch块中的。

 

2.使用throw(s)处理异常:

如果当前出现的异常在本方法中无法处理,我们只能抛出异常。
如果每个方法都是简单的抛出异常,那么在方法调用方法的多层嵌套调用中,Java虚拟机会从出现异常的方法代码块中往回找,直到找到处理该异常的代码块为止。然后将异常交给相应的catch语句处理(异常没有在本地处理,逻辑上throw之后的程序不会在进行)。如果Java虚拟机追溯到方法调用栈最底部main()方法时,如果仍然没有找到处理异常的代码块(这属于异常没有得到处理,将终止出现异常的线程),将按照下面的步骤处理:
第一、调用异常的对象的printStackTrace()方法,打印方法调用栈的异常信息。
第二、如果出现异常的线程为主线程,则整个程序运行终止;如果非主线程,则终止该线程,其他线程继续运行。
关于throw的用法我们有几点注意事项要注意:
注意一: throw语句后不允许有紧跟其他语句,因为这些没有机会执行(块外也不行,因为不会执行,无论是否被调用方捕获。如果异常是在本方法内部throw直接捕获,那是可以执行块后面的代码的,记住只要throw论文,throw之后的代码都不会在执行)。我以一段程序来说明这个问题:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
publicclassTestException { 
    publicstaticvoidexc()throwsArithmeticException{
        inta =1;
        intb=4;
        for(inti=-2;i<3;i++){
                    a=4/i;
                System.out.println("i="+i);
        }
    }
    publicstaticvoidcaexc(){
        try{
            exc();
        }catch(ArithmeticException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    publicstaticvoidmain(String[] args) { 
        TestException.caexc();     
    } 
}

输出结果为:

?
1
2
3
4
5
6
i=-2
i=-1
java.lang.ArithmeticException: / by zero
    at TestException.exc(TestException.java:8)
    at TestException.caexc(TestException.java:14)
    at TestException.main(TestException.java:21)

虽然捕获了异常,但由于原来的线程已经throw,所以后面的代码均不会得到执行。
注意二: 如果一个方法调用了另外一个声明抛出异常的方法,那么这个方法要么处理异常,要么声明抛出。
注意三: throw和throws关键字的区别:
throw用来抛出一个异常,在方法体内。语法格式为:throw 异常对象。
throws用来声明方法可能会抛出什么异常,在方法名后,语法格式为:throws 异常类型1,异常类型2…异常类型n
注意四: throw语句抛出异常的两种情况:
1.当throw语句抛出的异常是Checked异常,则该throw语句要么处于try块里显式捕获该异常,要么放在一个带throws声明抛出的方法中,即把异常交给方法的调用者处理。
2.当throw语句抛出的异常是Runtime异常,则该语句无须放在try块内,也无须放在带throws声明抛出的方法中,程序既可以显式使用try…catch来捕获并处理该异常,也可以完全不理会该异常,把该异常交给方法的调用者处理。
Runtime异常和Checked异常在抛出时的区别见下面这段代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
publicclassTestException { 
    publicstaticvoidthrow_checked(inta)throwsException{
        //Exception默认为checkedExcption
        if(a>0)thrownewException("Exception:a>0");
    }
    publicstaticvoidthrow_runtime(inta) {
        if(a>0)thrownewRuntimeException("runtimeException:a>0");
    }
    publicstaticvoidmain(String[] args) { 
        inta=1;
        try{
            throw_checked(a);
        }catch(Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        throw_runtime(a);
    } 
}

可见Runtime异常的灵活性比Checked的灵活性更强。因为Checked异常必须要被显式捕获或者显式抛出,所以Runtime写的更方便,我们自定义异常一般都是用Runtime异常。
 

3.自定义异常:

用户自定义异常都应该继承Exception基类,如果希望自定义Runtime异常,则应该继承RuntimeException基类。异常类通常需要提供两种构造器:一个是无参数的构造器,另一个是带一个字符串的构造器,这个字符串将作为该异常对象的详细说明(也就是异常对象的getMessage方法的返回值)。下面给出一段自定义异常MyException的代码:

?
1
2
3
4
5
6
7
publicclassMyExceptionextendsRuntimeException{
    publicMyException(){
    }
    publicMyException(String s){
        super(s);
    }
}

用throws声明方法可能抛出自定义的异常,并用throw语句在适当的地方抛出自定义的异常。捕获自定义异常的方法与捕获系统异常一致。还可以异常转型。
 

4.异常链(异常跟踪栈):

异常对象的printStackTrace方法用于打印异常的跟踪栈信息,根据printStackTrace方法的输出结果,我们可以找到异常的源头,并跟踪到异常一路触发的过程。虽然printStackTrace()方法可以很方便地追踪异常的发生状况,可以用它来调试,但是在最后发布的程序中,应该避免使用它。而应该对捕获的异常进行适当的处理,而不是简单的将信息打印出来。

 

5.总结:

(1) catch块尽量保持一个块捕获一类异常,不要忽略捕获的异常,捕获到后要么处理,要么转译,要么重新抛出新类型的异常。
(2) 不要用try…catch参与控制程序流程,异常控制的根本目的是处理程序的非正常情况。
(3) 避免过大的try块,不要把不会出现异常的代码放到try块里面,尽量保持一个try块对应一个或多个异常。
(4) 细化异常的类型,不要不管什么类型的异常都写成Excetpion。

77
关闭
先学习,后交费申请表
每期5位名额
在线咨询
免费电话
QQ联系
先学习,后交费
TOP
您好,您想咨询哪门课程呢?
关于我们
机构简介
官方资讯
地理位置
联系我们
0513-91107100
周一至周六     8:30-21:00
微信扫我送教程
手机端访问
南通科迅教育信息咨询有限公司     苏ICP备15009282号     联系地址:江苏省南通市人民中路23-6号新亚大厦三楼             法律顾问:江苏瑞慈律师事务所     Copyright 2008-