第06部分:异常语句

throw语句

异常是一种信号,表明发生了某种异常状况或错误。抛出异常的目的是发出信号,表示有异常状况发生。捕获异常的目的是处理异常,使用必要的操作修复。在 Java 中,throw 语句用于抛出异常:

throw expression;

expression 的计算结果必须是一个异常对象,说明发生了什么异常或错误。后面会详细介绍异常的种类,现在你只需知道,异常通过有点特殊的对象表示。下面是抛出异常的示例代码:

public static double factorial(int x) {

        if (x < 0)

                throw new IllegalArgumentException("x must be >= 0");

        double fact;

        for(fact=1.0; x > 1; fact *= x, x--)

                /* empty */ ;     // 注意,使用的是空语句

        return fact;

}


Java 解释器执行 throw 语句时,会立即停止常规的程序执行,开始寻找能捕获或处理异常的异常处理程序。异常处理程序使用 try/catch/finally 语句编写,后面会介绍。Java解释器先在当前代码块中查找异常处理程序,如果有,解释器会退出这个代码块,开始执行异常处理代码。异常处理程序执行完毕后,解释器会继续执行处理程序后的语句。


如果当前代码块中没有适当的异常处理程序,解释器会在外层代码块中寻找,直到找到为止。如果方法中没有能处理 throw 语句抛出的异常的异常处理程序,解释器会停止运行当前方法,返回调用这个方法的地方,开始在调用方法的代码块中寻找异常处理程序。Java通过这种方式,通过方法的词法结构不断向上冒泡,顺着解释器的调用堆栈一直向上寻找。如果一直没有捕获异常,就会冒泡到程序的 main() 方法。如果在 main() 方法中也没有处理异常,Java 解释器会打印一个错误消息,还会打印一个堆栈跟踪,指明这个异常在哪里发生,然后退出。








try/catch/finally语句

Java 有两种稍微不同的异常处理机制。经典形式是使用 try/catch/finally 语句。这个语句的 try 子句是可能抛出异常的代码块。try 代码块后面是零个或多个 catch 子句,每个子句用于处理特定类型的异常,而且能处理多个不同类型的异常。如果 catch 块要处理多个异常,使用 | 符号分隔各个不同的异常。catch 子句后面是一个可选的 finally 块,包含清理代码,不管 try 块中发生了什么,始终都会执行。


catch 和 finally 子句都是可选的,但每个 try 块都必须有这两个子句中的一个。try、catch 和 finally 块都放在花括号里。花括号是句法必须的一部分,即使子句只包含一个语句也不能省略。


下述代码演示了 try/catch/finally 语句的句法和作用:

try {

        //正常情况下,这里的代码从上到下运行,没有问题

        // 但是,有时可能抛出异常

        // 可能是throw语句直接抛出

        // 也可能是调用的方法间接抛出

}catch (SomeException e1) {

        //这段代码中的语句用于处理SomeException或其子类类型的异常对象

        // 在这段代码中,可以使用名称e1引用那个异常对象

}catch (AnotherException | YetAnotherException e2) {

        // 这段代码中的语句用于处理AnotherException、YetAnotherException

        // 或二者的子类类型的异常。在这段代码中,使用名称e2引用传入的异常对象

}finally {

        //不管try子句的结束方式如何,这段代码中的语句都会执行:

                1)正常结束:到达块的末尾

                2)由break、continue或return语句导致

                3)抛出异常,由上述catch子句处理

                4)抛出异常,未被捕获处理

        //但是,如果在try子句中调用了System.exit(),解释器会立即退出

        //不执行finally子句

}



1. try子句

try 子句的作用很简单,组建一段代码,其中有异常需要处理,或者因某种原因终止执行后需要使用特殊的代码清理。try 子句本身没什么用,异常处理和清理操作在 catch 和finally 子句中进行。


2. catch子句

try 块后面可以跟着零个或多个 catch 子句,指定处理各种异常的代码。每个 catch 子句只有一个参数(可以使用特殊的 | 句法指明 catch 块能处理多种异常类型),指定这个子句能处理的异常类型,以及一个名称,用来引用当前处理的异常对象。catch 块能处理的类型必须是 Throwable 的子类。

有异常抛出时,Java 解释器会寻找一个 catch 子句,它的参数要和异常对象的类型相同,或者是这个类型的子类。解释器会调用它找到的第一个这种 catch 子句。catch 块中的代码应该执行处理异常状况所需的任何操作。假如异常是 java.io.FileNotFoundException,此时或许要请求用户检查拼写,然后重试。

不是所有可能抛出的异常都要有一个 catch 子句处理,有些情况下,正确的处理方式是让异常向上冒泡,由调用方法捕获。还有些情况,例如表示程序错误的NullPointerException 异常,正确的处理方式或许是完全不捕获,随它冒泡,让 Java 解释器退出,打印堆栈跟踪和错误消息。


3. finally子句

finnaly 子句放在 try 子句后面,一般用来执行清理操作(例如关闭文件和网络连接)。finally 子句很有用,因为不管 try 块中的代码以何种方式结束执行,只要有代码执行,finally 子句中的代码就会执行。事实上,只有一种方法能让 try 子句退出而不执行finally 子句——调用 System.exit() 方法,让 Java 解释器停止运行。

正常情况下,执行到 try 块的末尾后会继续执行 finally 块,做必要的清理工作。如果因为 return、continue 或 break 语句而离开 try 块,会先执行 finally 块,然后再转向新的目标代码。

如果 try 块抛出了异常,而且有处理该异常的 catch 块,那么先执行 catch 块,然后在执行 finally 块。如果本地没有能处理该异常的 catch 块,先执行 finally 块,然后再向上冒泡到能处理该异常最近的 catch 子句。

如果 finally 块使用 return、continue、break 或 throw 语句,或者调用的方法抛出了异常,从而转移了控制权,那么待转移的控制权中止,改为执行新的控制权转移。例如,如果 finally 子句抛出了异常,这个异常会取代任何正在抛出的异常。如果 finally 子句使用了 return 语句,就算抛出的异常还没处理,方法也会正常返回。

try 和 finally 子句可以放在一起使用,不处理异常,也没有 catch 子句。此时,finally 块只是负责清理的代码,不管 try 子句中有没有 break、continue 或 return 语句,都会执行。






处理资源的try语句

try 块的标准形式很通用,但有些常见的情况需要开发者小心编写 catch 和 finally 块。这些情况是清理或关闭不再需要使用的资源。

Java(从第 7 版起)提供了一种很有用的机制,能自动关闭需要清理的资源——处理资源的 try 语句(try-with-resources,TWR)。后面会详细介绍 TWR,现在先介绍它的句法。下面的示例展示了如何使用 FileInputStream 类打开文件(得到的对象需要清理):

try (InputStream is = new FileInputStream("/Users/ben/details.txt")) {

                // ......处理这个文件

}

这种新型 try 语句的参数都是需要清理的对象(严格来说,这些对象必须实现 AutoCloseable 接口)。 这些对象的作用域在 try 块中,不管 try块以何种方式退出,都会自动清理。开发者无需编写任何 catch 或 finally 块,Java 编译器会自动插入正确的清理代码。


所有处理资源的新代码都应该使用 TWR 形式编写,因为这种形式比自己动手编写 catch块更少出错,而且不会遇到麻烦的技术问题,例如终结。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,366评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,521评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,689评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,925评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,942评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,727评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,447评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,349评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,820评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,990评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,127评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,812评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,471评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,017评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,142评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,388评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,066评论 2 355

推荐阅读更多精彩内容

  • 八、深入理解java异常处理机制 引子try…catch…finally恐怕是大家再熟悉不过的语句了, 你的答案是...
    壹点零阅读 1,567评论 0 0
  • 引言 在程序运行过程中(注意是运行阶段,程序可以通过编译),如果JVM检测出一个不可能执行的操作,就会出现运行时错...
    Steven1997阅读 2,432评论 1 6
  • Kotlin 的异常处理机制主要依赖于try、catch、finally、throw 这四个关键字,其中try关键...
    凌寒天下独自舞阅读 666评论 0 0
  • 元认知高效学习五维调控技术P99---101 培养学习者思维的深加工能力是学习的关键,也是学与教的重要目标。
    百合花开2018阅读 156评论 0 0
  • 姓名:李淑瑛 224期学员 289期志工 325期志工 公司:绍兴翔鹰纺织品有限公司 部门:人事行政部 【坚持日精...
    李淑瑛阅读 205评论 0 0