11 异常处理

学习的”道“:接收-思考-质疑-接受

目录

页码: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 中的异常和处理详解

preference

简介

程序运行时,发生的不被期望的事件,它阻止了程序按照程序员的预期正常执行,这就是异常。

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对象为空");
      //......

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值