• 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...)
    捕获异常,可以由程序员自行对出现的异常进行处理
    抛出异常,程序运行过程中产生的异常递交给虚拟机,由虚拟机进行处理,异常的处理程序员无法干预
  • 相关阅读:
    mybatis N+1问题解决
    Best Time to Buy and Sell Stock
    119. Pascal's Triangle II
    HBuilder打包ios
    关于JavaScript/TypeScript中的setTimeout和setInterval
    白色透明渐变css
    AntV 在小程序中的使用
    AntV 在h5页面中的使用
    js处理后端返回超过16位大数字方案(network中preview和response返回不一致)
    m站taro编译css的时候有些属性会被编译掉,如果遇到这个问题,用第一行代码,写到无法编译的代码上面
  • 原文地址:https://www.cnblogs.com/xiaotiaosi/p/6381883.html
Copyright © 2020-2023  润新知