学习的”道“:接收-思考-质疑-接受
目录
页码:315-352
2018年07月25日09:47:46
11 有风险的行为
基本
Java的异常处理(exception-handling)机制是个简捷、轻量化的执行期间例外状况处理方式,它让你能够将处理错误状况的程序代码摆放在一个容易阅读的位置。这要以来你已经知道所调用的方法是有风险的(也就是可能产生异常),因此你可以编写处理此异常可能的程序代码。
你要如何得知某个方法会抛出异常呢?看到该方法的声明有throws
语句就知道了。
异常是一种Exception类型的对象,Exception类型的对象可以是任何他的子类的实例。
try{
//危险动作
}catch(Exception ex){
//尝试修复,这一段只会在有抛出异常时执行
}
如果你的程序代码会抓住异常,那是谁抛出的异常?
方法可以抓住其他方法所抛出的异常。异常总是会丢回给调用方。
会抛出异常的方法必须要声明它有可能会这么做。
//1.有风险,会抛出异常的程序代码
public void takeRisk() throws BadException{
if(abandonAllHope){
throw new BadException();
}
}
//2. 调用该方法的程序代码
public void crossFingers(){
try{
anObject.takeRisk();
}catch(BadException ex){
System.out.println("Aaargh!");
ex.printStackTrace();
}
}
受检查与不受检查的异常
编译器会核对每件事,除了RuntimeException之外。
RuntimeException被称为不检查异常,你可以直接抛出与抓住它们,但是没有这个必要,编译器也不管。
除了RuntimeException这种特例之外,编译器会关照Exception所有的子类。
大部分的RuntimeException都是因为程序逻辑的问题,而不是你所无法预测或防止的方法的出现的执行期失败状况,你无法保证文件一直都在。你无法保证服务器不会死机,但是你可以确保程序不会运行不合理的逻辑。
try/catch是用来处理真正的异常,而不是你程序的逻辑错误,该块要做的是恢复的尝试,或者至少会优雅的列出错误的信息。
异常与流程控制
try/catch
1. 若执行成功则执行try内内容跳过catch;
2. 若try执行过程中抛出异常,会跳过其余程序运行到catch块执行,然后继续下去;
finally:无论如何都要执行的部分
finally块是用来存放不管有没有异常都得执行的程序。
try{
turnOvenOn();
x.bake();
}catch(BakingException ex){
ex.printStackTrace();
}finally{
turnOvenOff();
}
如果没有finally,你得同时吧turnOvenOff()摆在try与catch两处。finally块可以让你把所有重要的清理程序代码集中在一处。
如果try或catch块有return指令,finally还是会执行!流程会跳到finally然后再回到return指令。
如果有必要的话,方法可以抛出多个异常。
多态的异常
异常也是对象,除了可以被抛出外,并没有什么特别的。因此如同所有的对象,异常也能够以多态的方式来引用。例如PantsException能够被赋值给Exception的引用。这样的好处是方法可以不必明确地声明每个可能抛出的异常,可以只声明父类就行。
为每个需要单独处理的异常编写不同的catch块。
有多个catch块时要从小排到大。
如果不想处理异常,你可以把它duck掉来避开。
处理或声明,做个堂堂正正的程序员
两种瞒住编译器的有风险方法调用方式
(1)处理。
把有风险的调用包在try/catch块中
try{
laundry.doLaundry();
}catch(ClothingException cex){
//恢复程序代码
}
(2)声明(duck掉)。
把method声明成跟有风险的调用一样会抛出相同的异常。
//有声明会抛出异常,但没有try/catch块,所以就会duck异常留给调用方
void foo() throws ClothingException{
laundry.doLaundry();
}
是非题:
(1)如果遇到编译器的检查异常,就必须把有风险的程序代码包在try/catch块中。
错,也可以声明异常。
(2)只有编译器的检查异常才会被捕获。
错,运行期间异常也会被捕获到。非检查异常:javac在编译时,不会提示和发现这样的异常
(3)如果方法声明可以抛出编译器检查的异常,则必须把抛出异常的程序代码包在try/catch块中。
错,只要声明就够了。
(4)main()方法必须处理所有未被处理的异常。
错,但如果连它也不管,那Java虚拟机只好中断。
(5)一个方法只能抛出一种异常。
错
(6)try块可以在没有catch或finally块的情形下单独出现。
错
(7)异常的处理有时被称为“ducking”。
错,duck在这里有声明的意思。
(8)带有try块和finally块的方法可以选择性地声明异常。
错,如果没有catch就要duck
。
(9)运行期的异常必须要处理或者duck掉。
错
Java 中的异常和处理详解
简介
程序运行时,发生的不被期望的事件,它阻止了程序按照程序员的预期正常执行,这就是异常。
Java提供了更加优秀的解决办法:异常处理机制。
异常处理机制能让程序在异常发生时,按照代码的预先设定的异常处理逻辑,针对性地处理异常,让程序尽最大可能恢复正常并继续执行,且保持代码的清晰。
Java中的异常可以是函数中的语句执行时引发的,也可以是程序员通过throw 语句手动抛出的,只要在Java程序中产生了异常,就会用一个对应类型的异常对象来封装异常,JRE就会试图寻找异常处理程序来处理异常。
Throwable类是Java异常类型的顶层父类,一个对象只有是 Throwable 类的(直接或者间接)实例,他才是一个异常对象,才能被异常处理机制识别。
Java异常的分类和类结构图
Java标准库内建了一些通用的异常,这些类以Throwable为顶层父类。
Throwable又派生出Error类和Exception类。
错误:Error类以及他的子类的实例,代表了JVM本身的错误。错误不能被程序员通过代码处理,Error很少出现。因此,程序员应该关注Exception为父类的分支下的各种异常类。
异常:Exception以及他的子类,代表程序运行时发送的各种不期望发生的事件。可以被Java异常处理机制使用,是异常处理的核心。
总体上我们根据Javac对异常的处理要求,将异常类分为2类。
非检查异常(unckecked exception):Error 和 RuntimeException 以及他们的子类。javac在编译时,不会提示和发现这样的异常,不要求在程序处理这些异常。所以如果愿意,我们可以编写代码处理(使用try…catch…finally)这样的异常,也可以不处理。对于这些异常,我们应该修正代码,而不是去通过异常处理器处理 。这样的异常发生的原因多半是代码写的有问题。如除0错误ArithmeticException,错误的强制类型转换错误ClassCastException,数组索引越界ArrayIndexOutOfBoundsException,使用了空对象NullPointerException等等。
检查异常(checked exception):除了Error 和 RuntimeException的其它异常。javac强制要求程序员为这样的异常做预备处理工作(使用try…catch…finally或者throws)。在方法中要么用try-catch语句捕获它并处理,要么用throws子句声明抛出它,否则编译不会通过。这样的异常一般是由程序的运行环境导致的。因为程序可能被运行在各种未知的环境下,而程序员无法干预用户如何使用他编写的程序,于是程序员就应该为这样的异常时刻准备着。如SQLException , IOException,ClassNotFoundException 等。
需要明确的是:检查和非检查是对于javac来说的,这样就很好理解和区分了。
异常处理的基本语法
在编写代码处理异常时,对于检查异常,有2种不同的处理方式:使用try…catch…finally
语句块处理它。或者,在函数签名中使用throws 声明
交给函数调用者caller去解决。
throws 函数声明
throws声明:如果一个方法内部的代码会抛出检查异常(checked exception),而方法自己又没有完全处理掉,则javac保证你必须在方法的签名上使用throws关键字声明这些可能抛出的异常,否则编译不通过。
throws是另一种处理异常的方式,它不同于try…catch…finally,throws仅仅是将函数中可能出现的异常向调用者声明,而自己则不具体处理。
采取这种异常处理的原因可能是:方法本身不知道如何处理这样的异常,或者说让调用者处理更好,调用者需要为可能发生的异常负责。
public void foo() throws ExceptionType1 , ExceptionType2 ,ExceptionTypeN
{
//foo内部可以抛出 ExceptionType1 , ExceptionType2 ,ExceptionTypeN 类的异常,或者他们的子类的异常对象。
}
throw 异常抛出语句
throw exceptionObject
程序员也可以通过throw语句手动显式的抛出一个异常。throw语句的后面必须是一个异常对象。
throw 语句必须写在函数中,执行throw 语句的地方就是一个异常抛出点,它和由JRE自动形成的异常抛出点没有任何差别。
public void save(User user)
{
if(user == null)
throw new IllegalArgumentException("User对象为空");
//......
}