通过异常处理错误-2

Java标准异常

      Throwable这个Java类被用来表示任何可以作为异常被抛出的类。Throwable对象 可分为两种类型(指从Throwable继承而得到的类型): Error用来表示编译时和系统错误(除特殊情况外,一般不用你关心) ; Exception是 可以被抛出的基本类型,在Java类库、用户方法以及运行时故障中都可能拋出Exception型异常。所以Java程序员关心的基类型通常是Exception。

      要想对异常有全面的了解,最好去浏览一下HTML格式的Java文档( 可以从通过异常处理错误-2java.sun.com下载)。为了对不同的异常有个感性的认识,这么做是值得的。但很快你就会发现,这些异常除了名称外其实都差不多。同时,Java中异常的数目在持续增加,所以在书中简单罗列它们毫无意义。所使用的第三方类库也可能会有自己的异常。对异常来说,关键是理解概念以及如何使用。

      异常的基本的概念是用名称代表发生的问题,并且异常的名称应该可以望文知意。异常并非全是在java.lang包里定义的;有些异常是用来支持其他像util、net和io这样的程序包,这些异常可以通过它们的完整名称或者从它们的父类中看出端倪。比如,所有的输入/输出异常都是从通过异常处理错误-2java.io.IOException继承而来的。

一个特殊的例子:RuntimeException   if(t==null){     throw new NullPointerException();   }
      如果必须对传递给方法的每个引用都检查其是否为null (因为无法确定调用者是否传入了非法引用),这听起来着实吓人。幸运的是,这不必由你亲自来做,它属于Java的标准运行时检测的一部分。如果对null引用进行调用,Java会 自动抛出NullPointerException异常,所以上述代码是多余的,尽管你也许想要执行其他的检查以确保NullPointerException不会出现。

      属于运行时异常的类型有很多,它们会自动被Java虚拟机抛出,所以不必在异常说明中把它们列出来。这些异常都是从RuntimeException类继承而来,所以既体现了继承的优点,使用起来也很方便。这构成了一组具有相同特征和行为的异常类型。并且,也不再需要在异常说明中声明方法将拋出RuntimeException类型的异常(或者任何从RuntimeException继承的异常),它们也被称为“不受检查异常”。这种异常属于错误,将被自动捕获,就不用你亲自动手了。要是自己去检查RuntimeException的话,代码就显得太混乱了。不过尽管通常不用捕获RuntimeException异常,但还是可以在代码中抛出RuntimeException类型的异常。

      如果不捕获这种类型的异常会发生什么事呢?因为编译器没有在这个问题上对异常说明进行强制检查,RuntimeException类 型的异常也许会穿越所有的执行路径直达main方法,而不会被捕获。要明白到底发生了什么,可以试试下面的例子: 

class NeverCaught{
    static void f() {
        throw new RuntimeException();
    }
    static void g() {
        f();
    }
    public static void main() {
        g();
    }
}


      可能读者已经发现,RuntimeException (或任何从它继承的异常)是-一个特例。对于这种异常类型,编译器不需要异常说明,其输出被报告给了System.err:

        Exception in thread "main" java.lang.RuntimeException
   at throwException.NeverCaught.f(Text.java:16)
   at throwException.NeverCaught.g(Text.java:19)
   at throwException.NeverCaught.main(Text.java:22)

      所以答案是:如果RuntimeException没 有被捕获而直达main(),那么在程序退出前将调用异常的printStackTrace0方法。

      请务必记住:只能在代码中忽略RuntimeException (及其子类)类型的异常,其他类型异常的处理都是由编译器强制实施的。究其原因,RuntimeException代表的是编程错误:

      1)无法预料的错误。比如从你控制范围之外传递进来的null引用。

      2)作为程序员,应该在代码中进行检查的错误。( 比如对于ArrayIndexOutOf-BoundsException,就得注意一下数组的大小了。) 在一一个地方发生的异常,常常会在另一个地方导致错误。

      你会发现在这些情况下使用异常很有好处,它们能给调试带来便利。

      值得注意的是:不应把Java的异常处理机制当成是单一用途的工具。 是的,它被设计用来处理一些烦人的运行时错误,这些错误往往是由代码控制能力之外的因素导致的;然而,它对于发现某些编译器无法检测到的编程错误,也是非常重要的。

使用finally进行清理

对于一些代码,可能会希望无论try块中的异常是否抛出,它们都能得到执行。这通常适用于内存回收之外的情况(因为回收由垃圾回收器完成)。为了达到这个效果,可以在异常处理程序后面加上finally子句。为了证明finally总能运行,可以试试下面这个程序
public class FinallyWorks {
    static int count=0;
    public static void main(String[] args) {
        while(true) {
            try {
                if(count++==0) {
                    throw new Exception();
                }
                System.out.println("No Exception");
            }catch(Exception e) {
                System.out.println("Exception");
            }finally {
                System.out.println("In finally");
                if(count==3) break;
            }
        }
    }
}


      可以从输出中发现,无论异常是否被抛出,finally子句总能被执行。

      这个程序也给了我们一些思路,当Java中的异 常不允许我们回到异常抛出的地点时,那么该如何应对呢?如果把try块放在循环里,就建立了一个“程序继续执行之前必须要达到”的条件。还可以加入一个static类型的计数器或者别的装置,使循环在放弃以前能尝试-定的次数。这将使程序的健壮性更上一个台阶。   finally用 来做什么
      对于没有垃圾回收和析构函数自动调用机制的语言来说,finally非常 重要。它能使程序员保证:无论try块里发生了什么,内存总能得到释放。但Java有 垃圾回收机制,所以内存释放不再是问题。而且,Java也没有析构函数可供调用。那么,Java在什么情况下才能用到finally呢?

      当要把除内存之外的资源恢复到它们的初始状态时,就要用到finally子句。这种需要清理的资源包括:已经打开的文件或网络连接,在屏幕上画的图形,甚至可以是外部世界的某个开关,而且在异常没有被当前处理程序捕获的情况下,异常处理机制也会也会跳到更高一层的异常处理程序之前,执行finally子句。最后当涉及break和continue语句的时候,finally 子句也会得到执行。请注意,如果把finally子句和带标签的break及continue配合使用,在Java里就没必要使用goto语句了。
return中使用finally语句
因为finally语句总会执行,所以在一个方法当中,可以多个点返回,并且保证重要的清理工作仍然可以执行
public class Text {
    public static void f(int i) {
        try {
            if(i==1) return;
            if(i==2) return;
            if(i==3) return;
            if(i==4) return;
            
            return;
        }finally {
            System.out.println("Clean");
        }
    }
    public static void main(String[] args) {
        
        for(int i=1;i<5;i++) {
            f(i);
        }
    }
}
输出:
Clean
Clean
Clean
Clean

从输出可以看出,在finally类内部,从何处返回无关紧要

缺憾:异常丢失

 

遗憾的是,异常处理程序也有瑕疵。异常作为程序出错的标志,绝不应该被忽略,但是它还是可能被轻易忽略。用某些特殊的方式使用finally子句,就会发生这种情况:  
上一篇:面试官问我,使用Dubbo有没有遇到一些坑?我笑了。


下一篇:spring 事务回滚和常见异常