第十二章 通过异常处理程序

概要

异常分为两种,一种是Error,一种是Exception。前者是系统编译时和系统错误。后者是程序代码错误。Exception的父类是Throwable。下面是重点内容:

1.throws new Exception();代表着异常抛出。其实异常抛出跟方法返回差不多,异常抛出会从该点返回一个异常类,然后结束这个方法。
2.我们会在try块后跟多个catch(Exception e)语句,值得注意的是,在前面的catch块捕获到异常后,后面的就不会再执行(这叫异常匹配),另外,对于这个异常的子类,catch也能匹配成功
3.如果一个方法在()后面写throws Exception这叫异常申明。那么在调用这个方法的时候,编译器会强制提醒你try catch。
4.我们可以通过e.printStackTrace(system.out)输出异常,值得注意的是,里面的参数代表重定向输出的流。在Throws类中有一个数组StackTrace[],输出的就是这个数组的遍历。实际上每一次方法调用的栈信息,比如A方法中调用了B方法,B方法出错了,那么这个StackTrace数组就存了方法A和方法B的信息,包括代码行等等。这就是我们为什么看到e.printStackTrace打印了方法调用层次信息的原因。
5.在异常catch(Exception e){}里面,如果直接再throws e,这个e被捕获后打印的是之前异常发生的信息。如果使用e.fillInStackTrace就会打印新的,当前这个位置的异常信息。如果我们再throws new Exception(),那么后面的catch会重新去捕获这个异常,并且打印的是当前位置抛出信息。
6.throw new RuntimeException是不需要我们捕获的(其他类型的异常都需要我们try catch),如果一直没有捕获它,它会直接传到main方法中,然后导致程序崩溃。
7.finally跟return是无关的,无论你从什么地方return,都会执行finally语句的。
8.有两种情况下会发生异常丢失:

  • try块嵌套导致的异常丢失
try{
//假设我们这里执行的代码可能会抛出异常1
   try{

}finally{
    //假设这里又抛出了新的异常2
}
}catch(Exception e){//这里的异常本来是异常1的,但是被异常2覆盖了

}
  • finally方法中return导致的异常丢失
   throw new RuntimeException();//这里抛出了异常
}finally{
return;//因为这里return了,所以我们将看不到这个异常了
}

6.在继承结构中,异常的抛出限制:

  • 子类构造函数中,要调用抛出基类构造的异常申明中的异常。
  • 方法重写的时候,只能抛出父类异常申明过的异常,或者这些异常的子类(考虑向上转型的时候,需要捕获的异常是基类的异常,如果这个方法子类申明的异常比基类多,那可能会没有正确捕获子类所有的异常)。
  • 如果一个类的基类存在一个方法,它和这个类实现的某个接口中申明的方法一致,那么是不允许实现接口的这个方法的,直接使用基类中的这个方法。
  • 继承某个接口申明的方法(要考虑类中是否已有这个方法,如果有的话参照上面一条)可以随意异常申明,但是在捕获的时候,要捕获接口的异常申明。
  • 异常申明不组成方法签名
  • 类在初始化的时候,如果向上转型,要强制捕获的是基类构造函数所申明的异常。

7.我们在构造器中,要特别注意异常捕获和清理,在构造器中,finally的执行要慎重,因为很有可能发生了异常,在这个类没有完成初始化的情况下去做清理工作(清理的某些东西可能没有被正确初始化)。
8.在构造器中,如果某些类在初始化到时候会产生异常,并且需要在finally中清理,那么切记不能只使用一个try finally块包裹两个对象的初始化工作。具体代码示例如下:

class NeedsCleanup {
    private static long counter = 1;
    private final long id = counter++;

    public void dispose() {
        System.out.println("i am dispose needcleanup ");
    }
}

class ConstructionException extends Exception {
}

class NeedsCleanUp1 extends NeedsCleanup {
    public NeedsCleanUp1() throws ConstructionException {
    }
}

class CleanUpIdiom {
    public static void main(String[] args) {
        NeedsCleanup needsCleanup = new NeedsCleanup();
        try {
            // do somethings
        } finally {
            needsCleanup.dispose();// 这里执行清理
        }
        NeedsCleanup needsCleanup2 = new NeedsCleanup();
        NeedsCleanup needsCleanup3 = new NeedsCleanup();
        try {
            // do somethings
        } finally {
            needsCleanup2.dispose();
            needsCleanup3.dispose();//注意这里,因为在清理的时候不会产生异常,我们可以同时在一个try finally中清理
        }
        try {
            NeedsCleanUp1 needsCleanUp1 = new NeedsCleanUp1();//再仔细看这里,由于我们这里的构造函数会产生异常,所以单独用了一个try块,如果我们跟上面一样,也写两个类的初始化,然后再同一个fianlly中执行清理工作,那么很有可能会因为第一个对象初始化产生了异常,而第二个对象没有初始化,然后跳到finally中直接执行没有初始化 的第二个对象的清理方法,这样会导致异常
            try {
                NeedsCleanUp1 needsCleanUp12 = new NeedsCleanUp1();
                try {
                    // do something
                } finally {
                    needsCleanUp12.dispose();
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                needsCleanUp1.dispose();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值