异常处理

Java 的异常处理机制很大一部分来自 C++。它允许程序员跳过暂时无法处理的问题,以继续后续的开发,或者让程序根据异常做出更加聪明的处理。

Java 使用一些特殊的对象来代表异常状况,这样对象称为 异常对象。当异常状况发生时,Java 会根据预先的设定,抛出(throw)代表当前状况的对象。所谓的抛出是一种特殊的返回方式。该线程会暂停,逐层退出方法调用,直到遇到 异常处理器(Exception Handler)。异常处理器可以 捕捉(catch)的异常对象,并根据对象来决定下一步的行动。

异常处理器看起来如下,它由 try, catch, finally 以及随后的程序块组成。finally 不是必须的。

try {
  ...;
}

catch() {
  ...;
}

catch() {
  ...;
}

finally {
  ...;
}

这个异常处理器监视 try 后面的程序块。catch 的括号有一个参数,代表所要捕捉的异常的类型。catch 会捕捉相应的类型及其衍生类。try 后面的程序块包含了针对该异常类型所要进行的操作。try 所监视的程序块可能抛出不止一种类型的异常,所以一个异常处理器可以有多个 catch 模块。finally 后面的程序块是无论是否发生异常,都要执行的程序。

我们在 try 中放入可能出错,需要监视的程序,在 catch 中设计应对异常的方案。




异常的类型

Java 中的异常类都继承自 Throwable 类。一个 Throwable 类的对象都可以 抛出(throw)。

橙色:unchecked, 蓝色:checked

Throwable 对象可以分为两组。一组是 unchecked 异常,异常处理机制往往不用于这组异常,包括:

  • Error 类通常是指 Java 的内部错误以及如资源耗尽的错误。当 Error(及其衍生类)发生时,我们不能在编程层面上解决 Error,所以应该直接退出程序。

  • Exception 类有特殊的一个衍生类 RuntimeExceptionRuntimeException(及其衍生类)是 Java 程序自身造成的,也就是说,由于程序员在编程时犯错。RuntimeException 完全可以通过修正 Java 程序避免。比如将一个类型的对象转换成没有继承关系的另一个类型,即 ClassCastException。这类异常应该并且可以避免。

剩下的是 checked 异常。这些类是由编程与环境互动造成程序在运行时出错。比如读取文件时,由于文件本身有错误,发生 IOException。再比如网络服务器临时更改 URL 指向,造成 MalformedURLException。文件系统和网络服务器是在 Java 环境之外的,并不是程序员所能控制的。如果程序员可以预期异常,可以利用异常处理机制来制定应对预案。比如文件出问题时,提醒系统管理员。再比如在网络服务器出现问题时,提醒用户,并等待网络服务器恢复。异常处理机制主要是用于处理这样的异常。




抛出异常

在上面的程序中,异常来自于我们对 Java IO API 的调用。我们也可以在自己的程序中抛出异常,比如下面的 battery 类,有充电和使用方法:

class Battery 
{
    // 充电方法,最大电量为1
    public void chargeBattery(double p)
    {
        if (this.power + p < 1.) {
            this.power = this.power + p;
        }
        else {
            this.power = 1.;
        }
    }

    // 放电方法
    public boolean useBattery(double p)
    {
        // 先检查参数 p 是否为正数
        // 否则捕获错误
        try {
            test(p);
        }
        catch(Exception e) {
            System.out.println("catch Exception");
            System.out.println(e.getMessage());
            p = 0.0;
        }

        if (this.power >= p) {
            this.power = this.power - p;
            return true;
        }
        else {
            this.power = 0.0;
            return false;
        }
    }

    // 如果参数 p < 0 捕捉错误并抛出 
    private void test(double p) throws Exception 
    {
        if (p < 0) {
            Exception e = new Exception("p 必须为正数");
            throw e;
        }
    }

    private double power = 0.0; // percentage of battery
}


public class Test
{
    public static void main(String[] args)
    {
        Battery aBattery = new Battery();
        aBattery.chargeBattery(0.5);
        aBattery.useBattery(-0.5);
    }
}

输出:

catch Exception
p 必须为正数

useBattery() 表示使用电池操作。useBattery() 方法中有一个参数 p,表示使用的电量。我们使用 test() 方法测试该参数。如果该参数为负数,那么我们认为有异常,并抛出。

test 中,当有异常发生时(p < 0),我们创建一个 Exception 对象 e,并用一个字符串作为参数。字符串中包含有异常相关的信息,该参数不是必需的。使用 throw 将该 Exception 对象抛出。

我们在 useBattery() 中有异常处理器。由于 test() 方法不直接处理它产生的异常,而是将该异常抛给上层的 useBattery(),所以在 test() 的定义中,我们需要 throws Exception 来说明。

假设异常处理器并不是位于 useBattery() 中,而是在更上层的 main() 方法中,我们也要在 useBattery() 的定义中增加 throws Exception

异常处理器中,我们会捕捉任意 Exception 类或者其衍生类异常。这往往不利于我们识别问题,特别是一段程序可能抛出多种异常时。我们可以提供一个更加具体的类来捕捉。

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

推荐阅读更多精彩内容

  • 六种异常处理的陋习 你觉得自己是一个Java专家吗?是否肯定自己已经全面掌握了Java的异常处理机制?在下面这段代...
    Executing阅读 1,308评论 0 6
  • Java异常类型 所有异常类型都是Throwable的子类,Throwable把异常分成两个不同分支的子类Erro...
    予别她阅读 907评论 0 2
  • 初识异常(Exception) 比如我们在取数组里面的某个值得时候,经常会出现定义的取值范围超过了数组的大小,那么...
    iDaniel阅读 1,857评论 1 2
  • -1- 在微博上刷到一句语录感慨颇深——渐渐才知道,尽量不给别人添麻烦,别人最好也别麻烦我,这句话不是冷漠,而是成...
    用时间酿酒阅读 4,956评论 81 162
  • 阅读不能改变人生的长度,但可以改变人生的宽度; 阅读不能改变人生的起点,但可以改变人生的终点。 让阅读成为一种习惯...
    乾立风中阅读 130评论 0 0