例外(Exception)处理

1、例外的概念

所谓错误,是在程序运行过程中发生的异常事件,比如除0溢出、数组越界、文件找不到等,这些事件的发生将阻止程序的正常运行。为了加强程序的鲁棒性(强壮性,robust),程序设计时,必须考虑到可能发生的异常事件并做出相应的处理。

在C语言中,通过使用if语句来判断是否出现了错误,同时,调用函数通过被调用函数的返回值感知在被调用函数中产生的错误事件并进行处理。但是,这种错误处理机制会导致不少问题,因为在很多情况下需要知道错误产生的内部细节。通常,用全局变量Errno来存储一个异常事件的类型,这容易导致误用,因为一个Errno的值有可能在被处理前被另外的错误覆盖掉。此外,即使最优美的C语言程序,为了处理异常情况,也常常求助于goto语句。

Java通过面向对象的方法来处理程序错误,在Java中,错误被称为例外(Exception),有时也把例外称为异常。在一个方法的运行过程中,如果发生了例外,则这个方法(或者是Java虚拟机)生成一个代表该例外的对象(包含了该例外的详细信息),并把它交给运行时系统,运行时系统寻找相应的代码来处理这一例外。我们把生成例外对象并把它提交给运行时系统的过程称为抛弃(throw)一个例外。运行时系统在方法的调用栈中查找,从生成例外的方法开始进行回朔,直到找到包含相应例外处理的方法为止,这一个过程称为捕获(catch)一个例外。

例外机制的优点:

        把错误处理代码从常规代码中分离出来

        按错误类型和差别分组(类Exception,派生)

        对无法预测的错误的捕获和处理(基类)

        克服了传统方法的错误信息有限的问题(getMessage)

        把错误传播给调用堆栈(比较:全局变量,返回值)

例外机制的关键步骤:

try {…}    定义可能产生例外的代码段

catch (Etype e) {…}    用于捕获一个例外

finally {…}    用于做统一的事后处理,如释放资源

throw e;    用于抛出一个例外

throws Etype1, Etype2 ……    用于声明方法可能抛出的例外类型


2、例外的分类(Throwable/Exception)

一个例外是由一个对象来代表的,所有的例外都直接或间接地继承自Throwable类。在Java类库的每个类包中都定义了例外类,这些例外类分成两大类:Error类及Exception类,后者是Java程序中需要大量处理的。除了Java类库所定义的例外类之外,用户也可以通过继承已有的例外类来定义自己的例外类,并在程序中使用(利用throw产生,throws声明抛出,catch捕捉并处理)。

Error:由Java虚拟机生成并抛出,包括动态链接失败、虚拟机错误等,Java程序不做处理。

Runtime Exception:Java虚拟机在运行时生成的例外,如被0除等系统错误、数组下标超范围等,其产生比较频繁,处理麻烦,对程序可读性和运行效率影响太大。因此由系统检测,用户可不做处理,系统将它们交给缺省的异常处理程序(当然,必要时,用户可对其处理)。

Exception:一般程序中可预知的问题,其产生的例外可能会带来意想不到的结果,因此Java编译器要求Java程序必须捕获或声明所有的非运行时异常。

——Throwable类的方法:

Throwable()

Throwable(String message)

String toString()

        “classname” : “getMessage()”

String getMessage()

String getLocalizedMessage()

void printStackTrace()

void printStackTrace(PrintStream s)

void printStackTrace(PrintWriter s)

Throwable fillInStackTrace()

——一些常用的例外类:

Error (all in java.lang)

Exception (in java.lang)

RuntimeException

在使用能够产生异常的方法而没有捕获和处理,程序将不能通过编译。

3、捕获例外(try-catch-finally)

Java的例外处理是通过3个关键词来实现的:try-catch-finally。用try来监视执行一段程序,如果出现例外,系统就会抛出(throws)例外,可以通过例外的类型来捕捉(catch)并处理它,或最后(finally)由缺省处理方法来处理。

try代码段包含可能产生例外的代码

try代码段后跟有一个或多个catch代码段

每个catch代码段声明其能处理的一种特定类型的例外

每个catch代码段都是一段例外处理代码

程序继续执行最后一个catch代码段后的代码  (或执行完finally代码段后)

不同的代码段是不同的作用域,不可访问相互之间定义的局部变量



例外总是由距离产生例外最近的匹配catch代码段处理

如果没有相应的例外处理,则例外被交给上一层try代码段进行处理

例外处理的查找依据类型匹配原则顺序进行,第一个匹配的例外处理被执行,当例外处理执行完毕,程序接着最后一个catch代码段后的语句执行

例外处理的顺序影响到例外的处理,子类例外可被父类例外处理捕获,不要先捕获父类例外,再捕获子类例外

尽量避免用一般类型作为catch中指定要捕获的类型。一般应该按照try代码块中例外可能产生的顺序及其真正类型进行捕获和处理,

在例外处理中无法访问try代码段中声明的变量,因为此时try代码段已经退出了,例外处理所需要的任何信息一般都应该通过例外对象来传递

在使用方法时尽量直接处理该方法可能产生的例外,这样你的程序就会更健壮

4、声明例外(throws)

如果在一个方法中生成了例外,但是该方法并不处理它产生的例外,而是沿着调用层次向上传递,交由调用它的方法来处理这些例外,这就是声明例外。通常的情况是在该方法中并不确切知道改如何对这些例外进行处理,比如FileNotFoundException类例外,它由FileInputStream的构造方法产生,但在其构造方法中并不清楚如何处理它,是终止程序的执行还是新生成一个文件,这需要由调用它的方法来处理。

声明例外的方法:声明例外的方法是在产生异常的方法名后面加上要抛出(throws)的例外的列表:

retType methodName([paramlist]) throws exceptionList

如类FileInputStream中的read()方法是这样定义的:

        public int read() throws IOException

        { …

        }

throws子句中可以同时指明多个例外,说明该方法将不对这些例外进行处理,而是声明抛弃它们。

需要强调的是:对于非运行时例外,程序中必须要作处理,或者捕获,或者声明抛弃;而对于运行时例外,程序中则可不处理。

非运行时例外:Throwable、Exception

运行时例外:Error、RuntimeException

当在一个方法的代码中抛出一个受检查的例外时,这个例外或者被方法中的try-catch结构捕获,或者在方法的throws语句中声明编译器检查程序,保证所有非运行时例外都被程序显式地处理

5、抛出例外(throw)

声明例外首先必须生成例外。前面所提到的例外或者是由Java虚拟机生成,或者是由Java类库中的某些类生成。事实上,我们在程序中也可以生成自己的例外对象,也即是例外可以不是出错产生,而是人为地抛出。不论那种方式,生成例外对象都是通过throw语句实现:throw new ThrowableObject();       ArithmeticException e = newArithmeticException();    throw e;

注意:抛出的例外必须是Throwable或其子类的实例。

Exception和Error是Throwable的直接派生类

Exception,程序应该处理的例外    Error代表系统严重错误,一般程序不处理这类错误

例外抛出点后的代码在抛出例外后不再执行,也可以说例外的抛出终止了代码段的执行

6、创建自己的例外

当我们在设计自己的类包时,应尽最大的努力为用户提供最好的服务,并且希望用户不要滥用我们所提供的方法,当程序出现某些异常事件时,我们希望程序足够健壮以从程序中恢复,这时就需要用到例外。在选择例外类型时,可以使用Java类库中已经定义好的类,也可以自己定义例外类。自定义例外类不是由Java系统监测到的例外(如数组下标越界,被0除等),而是由用户自己定义的例外。 自定义例外同样要用try-catch-finally捕获,但必须由用户自己抛出(throw)。

例外是一个类,自定义例外必须继承自Throwable或Exception类。建议:

        例外一定是不经常发生的故障,应避免把控制流程作为例外处理

        尽量使用JDK提供的例外类:重用、便于理解

        Exception/RuntimeException类:编译时例外、运行时例外。

        一般不把自定义例外作为Error的子类,因为Error通常被用来表示系统内部的严重故障。

当自定义例外是从RuntimeException及其子类继承而来时,该自定义例外是运行时例外,程序中可以不捕获并处理它。

当自定义例外是从Throwable、Exception及其其他子类继承而来时,该自定义例外是编译时例外,也即程序中必须捕获并处理它。

自定义例外的形式:

class MyException extends Exception

{

    …

}

例:计算两个数之和,当任意一个数超出范围时,抛出自己的例外:

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

推荐阅读更多精彩内容