java异常处理

参考博客:http://www.cnblogs.com/octobershiner/archive/2012/12/20/2827120.html

一、异常处理

异常处理通过五个关键字进行控制,他们是try、catch、throw、throws和finally。在程序中,在有可能出现不正常状况的地方,使用try关键字,用它吧这段代码包含起来。如果在try语句块中发生异常,这个异常就会被抛出。这个时候就可以使用catch语句来捕获异常,并在这块语句块中,对异常进行处理。还有一些不管发布发生异常,都需要执行的,就把它们放到finally语句块中。throw关键字用来手动引发一个异常。throws关键字用来定义任何被调用方法的异常。

  基本使用

    我们在使用java的一些文件或者数据库操作的时候已经接触过一些异常了,比如IOException、SQLException等,这些方法被声明可能会抛出某种异常,因此我们需要对其进行捕获处理。这就需要基本的try..catch语句了。下图就是我们经常写的一个基本结构。try语句块中写可能会抛出异常的代码,之后在catch语句块中进行捕获。我们看到catch的参数写的是一个Exception对象,这就意味着这个语句块可以捕获所有的检查类型的异常(虽然这并不是一种好的写法,稍后讨论),finally总是会保证在最后执行,一般我们在里面处理一些清理的工作,比如关闭文件流或者数据库,网络等操作。

语句块结构是灵活的,但是try是必须有的,catch和finally两者至少有一个,当然catche的数量可以有多个。有时候try语句块中可能抛出多种类型的异常,这个时候,我们可以写多个catch语句来捕获不同类型的异常,一个比较好的写法如下:

复制代码
        try{
            // ..invoke some methods that may throw exceptions
        }catch(ExceptionType1 e){
            //...handle exception
        }catch(ExceptionType2 e){
            //...handle exception
        }catch(Exception e){
            //...handle exception
        }finally{
            //..do some cleaning :close the file db etc.
        }
复制代码

   当异常不满足前两个type的时候,exception会将异常捕获。我们发现这个写法比较类似switch case的结构控制语句,但实际上,一旦某个catch得到匹配后,其他的就不会就匹配了,有点像加了break的case。有一点需要注意catch(Exception)一定要写在最后面,catch是顺序匹配的,后面匹配Exception的子类,编译器就会报错。  

     初次学习try..catch总会被其吸引,所以大量的使用这种结果,以达到某种“鲁棒性”。(这语句也是程序员表白的最爱)。但try语句实际上执行的时候会导致栈操作。即要保存整个方法的调用路径,这势必会使得程序变慢。fillInStackTrace()是Throwable的一个方法,用来执行栈的操作,他是线程同步的,本身也很耗时。这里问题在StackOverFlow上曾经有过一段非常经典的讨论

的确当我们在try中什么都不做,或者只执行一个类似加法的简单调用,那么其执行效率和goto这样的控制语句是几乎一样的。但是谁会写这样的代码呢?

    总之不要总是试图通过try catch来控制程序的结构,无论从效率还是代码的可读性上都不好。

    try catch好的一面

    try catch虽然不推荐用于程序结构的控制,但是也具有重要的意义,其设计的一个好处就是,开发人员可以把一件事情当做事务来处理,事务也是数据库中重要的概念,举个例子,比如完成订单的这个事务,其中包括了一个动作序列,包括用户提交订单,商品出库,关联等。当这个序列中某一个动作执行失败的时候,数据统一恢复到一个正常的点,这样就不会出现,你付完了帐,商品却没有给你的情况。我们在try语句块中就像执行一个事务一样,当出现了异常,就会在catch中得到统一的处理,保证数据的完整无损。其实很多不好的代码也是因为没有好好利用catch语句的语言,导致很多异常就被淹没了。

 

异常处理原则

Java中异常提供了一种识别及响应错误情况的一致性机制,有效地异常处理能使程序更加健壮、易于调试。异常之所以是一种强大的调试手段,在于其回答了以下三个问题:

  • 什么出了错?
  • 在哪出的错?
  • 为什么出错?

在有效使用异常的情况下,异常类型回答了“什么”被抛出,异常堆栈跟踪回答了“在哪“抛出,异常信息回答了“为什么“会抛出,如果你的异常没有回答以上全部问题,那么可能你没有很好地使用它们。有三个原则可以帮助你在调试过程中最大限度地使用好异常,这三个原则是:

  • 具体明确
  • 提早抛出
  • 延迟捕获

(1)异常处理只能用于非正常情况

(2)为异常提供说明文档。通过javaDoc的@throws标签来描述异常的条件。尽可能的避免异常

(3)保持异常的原子性。异常的原子性是指当异常发生时后,各个对象的状态能够恢复到异常前的初始抓天,而不是停留在某个不合理的中间状态。

(4)避免过于庞大的try代码块

(5)在catch自居中指定具体的异常类型

(6)不要在catch代码块中忽略被捕获的异常。

二、Throwable类

类Throwable继承自Object,java所有的异常类都是继承Throwable。该类有两个子类,一个是Error,一个是Exception。Error类表示系统错误或编译期错误,如语法错误、函数书写错误等,一般不用。不过我们经常遇到且可以操作的异常是Exception类,这类异常是java标准函数库中抛出的基本异常,或者是用户自定义的异常,也可以是运行期发生的异常事件,如对象引用为null等。

public class Throwable extends Object implements Serializable

该类是Object的子类,实现Serializable接口。

1、构造函数表

Throwable()
          构造一个将 null 作为其详细消息的新 throwable。
Throwable(String message)
          构造带指定详细消息的新 throwable。
Throwable(String message, Throwable cause)
          构造一个带指定详细消息和 cause 的新 throwable。
Throwable(Throwable cause)
          构造一个带指定 cause 和 (cause==null ? null :cause.toString())(它通常包含类和 cause 的详细消息)的详细消息的新 throwable。

2、使用throw主动抛出异常

个人理解可以作为代替goto语句使用(部分代码放在try语句块中,部分代码放在catch代码块中和finally代码块中~如何操作可以多想想。重要的是编程思想嘛)

 1 public class javaTest2 implements javaTest1 {
 2 
 3 public static void main(String[] args) {
 4 
 5     int a=1,b=2;
 6     int max = 0;
 7     try{
 8     if(a>b){
 9         max=a;
10         System.out.println("a>b!");
11       }else{
12         throw new Exception();
13        }
14     }catch(Exception e){
15         max=b;
16         System.out.println("a<b!");
17      }finally{
18         System.out.println(max);
19     }
20   }
21 }

运行结果:

a<b!
2

3、运行期异常(RuntimeException)

java程序中编写时RuntimeException是唯一可以省略的异常。所有运行期异常都继承于RuntimeException异常类,在编写程序时,不必考虑此类异常,所有函数默认自己可能抛出RuntimeException异常,系统会自动探测、捕获并处理运行期异常。由于编译器不强制捕获并处理运行期异常,所以此类异常会顺利的通过编译,可以想象程序运行时刻出现RuntimeException时,该异常会穿过层层方法,最后由系统捕获该异常,并输出相关信息。

4、Throwable类的方法

方法摘要
 Throwable fillInStackTrace()
          在异常堆栈跟踪中填充。
 Throwable getCause()
          返回此 throwable 的 cause;如果 cause 不存在或未知,则返回 null
 String getLocalizedMessage()
          创建此 throwable 的本地化描述。
 String getMessage()
          返回此 throwable 的详细消息字符串。
 StackTraceElement[] getStackTrace()
          提供编程访问由 printStackTrace() 输出的堆栈跟踪信息。
 Throwable initCause(Throwable cause)
          将此 throwable 的 cause 初始化为指定值。
 void printStackTrace()
          将此 throwable 及其追踪输出至标准错误流。
 void printStackTrace(PrintStream s)
          将此 throwable 及其追踪输出到指定的输出流。
 void printStackTrace(PrintWriter s)
          将此 throwable 及其追踪输出到指定的 PrintWriter。
 void setStackTrace(StackTraceElement[] stackTrace)
          设置将由 getStackTrace() 返回,并由 printStackTrace() 和相关方法输出的堆栈跟踪元素。
 String toString()
          返回此 throwable 的简短描述。

三、自定义异常

  我们可以自己定义异常,以捕获处理某个具体的例子。创建自己的异常类,可以直接继承Exception或者RuntimeException。区别是前者是简称类型的,而后者为检查类型异常。Sun官方力挺传统的观点,他建议开发者都是用检查类型的异常,即你一定要去处理的异常。下面是定义的一个简单的异常类.

复制代码
public class SimpleException extends Exception{

    SimpleException(){}
    SimpleException(String info){
        super(info);
    }
}
复制代码

 

    我们覆写了两个构造方法,这是有意义的。通过传递字符串参数,我们创建一个异常对象的时候,可以记录下详细的信息,这样这个异常被捕获的时候就会显示我们之前定义的详细信息。比如用下面的代码测试一下我们定义的异常类:

复制代码
public class Test {

    public void fun() throws SimpleException{
        throw new SimpleException("throwing from fun");
    }
    public static void main(String[] args) {
        Test t = new Test();
        try{
            t.fun();
        }catch(SimpleException e){
            e.printStackTrace();
        }
    }
}
复制代码

  运行就会得到下面的结果 printStackTrace是打印调用栈的方法,他有三个重载方法,默认的是将信息输出到System.err。这样我们就可以清晰的看到方法调用的过程,有点像操作系统中的中断,保护现场。

SimpleException: throwing from fun
at Test.fun(Test.java:4)
at Test.main(Test.java:9)

 四、异常的几个问题

1、异常丢失

异常丢失是指函数抛出的异常没有被捕获,异常丢失是很重要的问题。而java的异常处理机制难以弥补这个缺陷。

个人觉得无非两个原因:捕获了异常没进行异常处理,finally子句提供了一种不管有无异常都需执行的一种机制。如果finally块中使用return、continue或break,则会把抛出的异常吃掉。

 

2、异常匹配

异常匹配讨论的是异常被抛出后,该如何选择处理函数的问题,其实,java提供的匹配机制很简单,异常被抛出后,系统会根据处理函数的顺序依次匹配,知道找到第一个可以处理该类异常的处理函数。系统会比较catch子句参数的异常类型,如果该类型与抛出的异常类型相符则处理异常,否则继续寻找。java异常处理机制对“异常类型相符”没有严格要求,认为子类的异常对象和父类的异常对象相符。如:

 

 1 import java.io.IOException;
 2 
 3 public class javaTest2 implements javaTest1 {
 4 
 5 public static void main(String[] args) {
 6 
 7     try{
 8         throw new IOException();
 9     }catch(IOException ex){
10         System.out.println("IOException ");
11     }catch(Exception e){
12         System.out.println("Exception");
13     }
14   }
15 }

 

运行结果:

 1 IOException  

若把第一个

catch(IOException ex){
10         System.out.println("IOException ");注释了

运行结果为:
Exception 


五、捕获异常(try...catch...)与抛出异常的区别(throws...)
捕获异常,可以由程序员自行对出现的异常进行处理
抛出异常,程序运行过程中产生的异常递交给虚拟机,由虚拟机进行处理,异常的处理程序员无法干预

 

posted on 2017-02-09 14:09  小调~  阅读(480)  评论(0编辑  收藏  举报

导航