专栏首页云霄雨霁Java--违例控制(异常处理)

Java--违例控制(异常处理)

违例发生时Java处理过程:

  1. 首先,创建违例对象:在内存“堆” 里,用new来创建。
  2. 随后,停止当前执行路径(记住不可沿这条路径继续下去),然后从当前的环境中释放出违例对象的句柄。
  3. 此时,违例控制机制会接管一切,并开始查找一个恰当的地方,用于继续程序的执行。这个恰当的地方便是“违例控制器”(Java的catch块),它的职责是从问题中恢复,使程序要么尝试另一条执行路径,要么简单地继续。

违例属于对象,用new在内存堆里创建,并需调用一个构建器。在所有标准违例中,存在着两个构建器:第一个是默认构建器,第二个则需使用一个字串自变量,使我们能在违例里置入相关信息:

if(t==null)
    throw  new NullPointerException();     //第一种方法
    //throw new NullPointerException("t = null");     //第二种方法

语法:

try{
    //可能产生违例的代码;
}catch(Type1 id1){
    //处理类型1违例
}catch(Type2 id2){
    //处理类型2违例
}catch(Type3 id3){
    //处理类型3违例
}finally{
    //每次都会发生的情况
}
  • try块:try块为警戒区,可能产生违例的代码放在这里面。
  • catch块:catch块为违例控制器,针对生成的违例进行捕获。可能捕获的每种违例类型都要有一个相应的违例控制器。
  • finally块:无论违例是否在try块中发生,都要执行的代码可以放在finally块中。

违例规范:

违例规范属于方法声明的一部分,位于自变量(参数)列表的后面。违例规范采用throws关键字,后面跟随全部潜在的违例类型。

void f() throws tooBig, tooSmall, divZero { //... 

如果一个方法声明了违例,但没有throw违例,这是可以的。这样可以强迫使用这个方法的程序员当作真的产生了那个违例去对待。在实际应用中,它是一个“占位符”的作用,方便该方法以后添加违例无需修改现成的代码。

捕获所有违例:

所有违例的基础类是Exception类,所以catch(Exception e)可以捕获任何违例。也是这个原因,我们无法从Exception类获得太多信息,但Exception类继承自Throwable类,一些Throwable类的方法会有帮助:

  • String getMessage()    //获取详细的信息
  • String toString()          //返回对 Throwable 的一段简要说明,其中包括详细的消息。 
  • void printStackTrace() 和 void printStackTrace(PrintStream)        //打印出 Throwable 和 Throwable 的调用堆栈路径。调用堆栈显示出将我们带到违例发生地点的方法调用的顺序。 第一个版本会打印出标准错误,第二个则打印出我们的选择流程。

重新“throw”出违例:

catch块可以不对捕获的违例进行处理,可以简单的再次抛出。这里会出现路径问题--就是上面的printStackTrace()方法。

如果只是简单地再次抛出, 我们打印出来的、与printStackTrace()内的那个违例有关的信息会与违例的起源地对应,而不是与重新掷出它的地点对应。若想重置新的堆栈跟踪信息,可调用 fillInStackTrace(),它会返回一个特殊的违例对象。这个违例的创建过程如下:将当前堆栈的信息填充到原来的违例对象里。

public static void g() throws Throwable {
    try {
        f();    //throw e;的起点
    } catch(Exception e) {
          throw e; 
          // throw e.fillInStackTrace(); // 重新定位,throw e.fillInStackTrace();的起点
    }
}

几乎永远不必专门捕获一个“运行期违例”—— RuntimeException ——它在默认情况下会自动得到处理。

编写自己的违例:

为创建自己的违例类,必须从一个现有的违例类型继承——最好在含义上与新违例近似。其实创建一个自己的违例非常简单:

class MyException extends Exception {
    public MyException() {}
    public MyException(String msg) {
        super(msg);
    }
}

接下来就像标准违例一样使用就行。创建自己的继承类和普通类一样,继承自标准违例之后,可以增添自己的构建器、方法、字段等。

违例的限制:

覆盖一个方法时,只能产生已在方法的基础类版本中定义的违例。

违例的限制情况比较多,下面用一个例子来说明:

class Exception1 extends Exception {}
class Exception2 extends Exception1 {}
class Exception3 extends Exception1 {}
class Exception4 extends Exception {}
class Exception5 extends Exception4{}
class Exception6 extends Exception2 {}

//基础类
abstract class Inning {    
    Inning() throws Exception1 {}
    void event () throws Exception1 {
        //实际不抛出任何异常
    }
    abstract void atBat() throws Exception2, Exception3;
    void walk() {} 
}

//接口
interface Storm {    
    void event() throws Exception5;
    void rainHard() throws Exception5;
}

//继承基础类并实现接口
public class StormyInning extends Inning implements Storm { 
    //可以为构造器添加新违例,但必须处理基础构造器的违例(这里Exception1是必须要处理的)
    StormyInning() throws Exception1, Exception5 {}
    StormyInning(String s) throws Exception1, Exception2 {}
    //常规方法必须符合基类
    !void walk() throws Exception6 {} //编译错误,因为基类没有违例
    //接口不能向基类中的现有方法添加异常:
    !public void event() throws Exception5 {}//编译错误,基类中event()方法没有抛出Exception5违例(即使接口中有)
    //如果该方法基类中没有,那么可以添加新违例:
    public void rainHard() throws Exception5 {}
    //即使基类中抛出违例,现有类中也可以不抛出:
    public void event() {}
    //重写的方法会抛出继承的异常:
    void atBat() throws Exception6 {}
    public static void main(String[] args) {
        try {
            //派生版本中不会触发Exception3
            StormyInning si = new StormyInning();
            si.atBat();
        } catch(Exception6 e) {
        } catch(Exception5 e) {
        } catch(Exception1 e) {}
        try {
            //上溯造型必须捕获方法的基类版本中的异常:
            Inning i = new StormyInning();
            i.atBat();
        } catch(Exception3 e) {
        } catch(Exception2 e) {
        } catch(Exception5 e) {
        } catch(Exception1 e) {}
    }
} 

我们必须认识到这一点:尽管违例规范是由编译器在继承期间强行遵守的,但违例规范并不属于方法类型的 一部分,后者仅包括了方法名以及自变量类型。因此,我们不可在违例规范的基础上覆盖方法。除此以外, 尽管违例规范存在于一个方法的基础类版本中,但并不表示它必须在方法的衍生类版本中存在。这与方法的 “继承”颇有不同(进行继承时,基础类中的方法也必须在衍生类中存在)。换言之,用于一个特定方法的 “违例规范接口”可能在继承和覆盖时变得更“窄”,但它不会变得更“宽”——这与继承时的类接口规则是正好相反的。 

违例匹配:

“掷”出一个违例后,违例控制系统会按当初编写的顺序搜索“最接近”的控制器。一旦找到相符的控制器,就认为违例已得到控制,不再进行更多的搜索工作。在违例和它的控制器之间,并不需要非常精确的匹配。一个衍生类对象可与基础类的一个控制器相配。

因为上面的原因,越基础的违例类应该放在违例列表的下面,而越具体(特殊)的违例类应该放在违例列表的上面。

违例准则:

用违例做下面这些事情:

  1. 解决问题并再次调用造成违例的方法。
  2. 平息事态的发展,并在不重新尝试方法的前提下继续。
  3. 计算另一些结果,而不是希望方法产生的结果。
  4. 在当前环境中尽可能解决问题,以及将相同的违例重新“掷”出一个更高级的环境。
  5. 在当前环境中尽可能解决问题,以及将不同的违例重新“掷”出一个更高级的环境。
  6. 中止程序执行。
  7. 简化编码。若违例方案使事情变得更加复杂,那就会令人非常烦恼,不如不用。
  8. 使自己的库和程序变得更加安全。这既是一种“短期投资”(便于调试),也是一种“长期投资”(改 善应用程序的健壮性)

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Mybatis映射文件笔记----参数处理

    SuperHeroes
  • Mybatis--动态SQL

    SuperHeroes
  • Java--自动装箱、拆箱与遍历循环

    SuperHeroes
  • 深度解析ug1292(7)

    布线延迟过大除了拥塞导致之外,还可能是其他因素。下图显示了降低布线延迟的另一流程(因其他因素导致布线延迟过大的处理流程)。

    Lauren的FPGA
  • 独家爆料!微信电商平台「微选」开启公测,移动电商大战升级

    知晓程序(微信号 zxcx0101)独家获悉,从 3 月 1 日晚开始,微信内的电商购物平台「微选」正式开始上线公测!

    知晓君
  • 每日一题 | 不确定参与人数的抽奖问题

    题目同样源于codeforces,链接:https://codeforces.com/gym/102646/problem/B

    TechFlow-承志
  • 中国5G新进展:运营商启动招标,临时牌照有望上半年发放

    2019年春节以来,5G(第五代移动通信网络)概念再度引发市场聚焦。那么,5G目前在中国究竟推进到了哪一步?

    用户4637678
  • Leetcode 82. Remove Duplicates from Sorted List II

    版权声明:博客文章都是作者辛苦整理的,转载请注明出处,谢谢! https://blog.cs...

    Tyan
  • 深度解析ug1292(3)

    通常,我们优先解决建立时间违例。Setup slack与逻辑延迟、布线延迟、时钟歪斜和时钟不确定性有关。因此,首先要明确这几个因素中哪个因素对建立时间违例起关键...

    Lauren的FPGA
  • hadoop-2.6.0常用命令记录 原

    尚浩宇

扫码关注云+社区

领取腾讯云代金券