博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
你现在就必须知道的Java异常体系
阅读量:4149 次
发布时间:2019-05-25

本文共 5781 字,大约阅读时间需要 19 分钟。

对于异常情况,例如,可能造成程序崩溃的错误输入,Java是通过捕获机制来处理异常错误。当程序出错时,我们不可能总是及时和用户沟通,所以希望记录出现的问题,以备日后进行分析。

如何处理错误

当一个用户在运行程序期间,由于程序的错误或一些外部环境的影响造成用户数据的丢失,用户就有可能不再使用这个程序了。为了避免这类事情发生,我们应该注意以下几点。

  1. 向用户通知错误的原因
  2. 保存所有的工作结果(日志)
  3. 允许用户以妥善的形式退出程序

异常分类

所有Java异常对象都是由Throwable继承而来。如果Java内置的异常类不满足我们的需求,我们可以创建自己的异常类。下面是我画的Java异常结构的一个简化示意图。

Error         是描述Java运行时系统内部错误和资源耗尽错误。应用程序不应该抛出这种类型的对象。如果出现了这样内部错误,除了告知客户,并尽力使用程序安全地终止之外,别无他法,不过这样的情况很少出现。
Exception                   是描述Java应用程序抛出和处理的非严重型错误,程序错误导致的异常就属于RuntimeException;而程序本身没有问题,但由于I/O错误这类问题导致的异常属于其他异常。

派生于RuntimeException的异常包含下面几种情况:

  • 错误的类型转换
  • 数组下标越界
  • 空指针异常

不是派生于RuntimeException的异常(其他异常):

  • 在文件尾部后面读取数据
  • 打开一个不存在的文件
  • 根据给定的字符串查找Class对象,而这个字符串表示的类不存在

如果存在RuntimeException异常,那么就一定是你的问题这是一条非常有道理的规则,不知道大家认不认可,我们应该在变量使用前检测是否为null可以防止出现NullPointerException(空指针异常);通过检测数组下标是否越界可以避免ArrayIndexOutOfBoundsException异常。

抛出异常

当我们需要在一个方法中抛出一个异常时,我们使用throw后加某异常类的实例,程序会在此向客户端程序(调用这段代码的程序)抛出对应异常并在此退出(相当于return)。另外需要注意的是,我们必须在定义该方法的时候指明异常类型,比如下面这段代码会抛出testException异常。

public void demo() throws testException,AException,BException{  throw new testException(); }

不同的异常类之间用逗号隔开即可,在这种情况下我们不必须throw每个异常类的实例(),但是客户端代码必须要catch到每个异常类:

public class MyException {  public static void main(String[] args){    MyException e = new MyException();    try {      e.demo();    } catch (testException e1) {      e1.printStackTrace();    } catch (BException e1) {      e1.printStackTrace();    } catch (AException e1) {      e1.printStackTrace();    }  }   public void a() throws testException,AException,BException{    throw new testException();  }} class testException extends Exception {};class AException extends Exception{};class BException extends Exception{};

捕获异常

1.捕获单个异常

try {  //code  more code}catch (Exception e){  //handler for this type}

2.捕获多个异常

注:捕获多个异常不仅让你代码看起来更简单,还会更加高效。

try {  ...}catch (FileNotFoundException e){  ...}catch (UnknownHostException e){  ...}catch (IOException e){  ...}

异常对象可能包含与异常本身有关的信息。要想获取对象更多的信息,可以尝试使用e.getMessage(),得到详细的错误信息(如果有的话),或者使用e.getClass().getName()得到异常对象的实际类型。

3.再次抛出异常与异常链

在catch子句中可以抛出一个异常,这样做是为了改变异常的类型。如果我们开发了一个供其他程序使用的子系统,那么表示子系统故障的异常类型可能会产生很多种解释。ServletException就是如此,执行servlet代码可能不想知道发生错误的细节,但希望明确知道servlet是否有问题。

try {  //连接database}catch (SQLException e){  throw new ServeletException("database error:"+e.getMessage());//throw抛异常}

这里,ServeletException用带有异常信息文本的构造器来构造。不过可以有一种更好的处理方法,并且将原始异常设置为新异常的原因:

try {      //连接database}catch (SQLException e){    Throwable se = new ServeletException("database error");    se.initCause(e);    throw se;}

当捕获到异常时,就可以用下面这条语句重新得到原始异常:

Throwable e = se.getCause();

强烈建议使用这种包装技术。可以让用户抛出子系统的高级异常,而不会丢失原始异常的细节。

小提示:如果在一个方法中发生异常,而又不允许抛出它,那么包装技术就十分重要。我们可以捕获这个异常,并将它包装成一个运行时异常。比如:有时候你可能只想记录一个异常,再将它重新抛出,而不做任何的改变,代码示例如下:

try {     //连接database}catch (Exception e){    logger.log(level,message,e);//日志    throw e;}

4.finally子句

不管是否有异常被捕获,finally子句中的代码总是会被执行。当finally包含return时,将会出现意想不到的结果,假设从try中退出return。在方法返回前,finally中的内容将会被执行。如果finally中也有return,这个返回值会覆盖原始的返回值,例子如下:

public static int(int n){	try{	    int r = n*n;	    return r;	}	finally{	    if(n==2) return 0;	}}

如果在finally中关闭资源,最好加上处理,防止异常,这是最传统的做法。下面第五点将给大家介绍jdk7之后资源关闭的方式。

finally{    try{	in.close();    }catch(Exception e){	throw e;    }}

5.try-with-resource(带资源的try)

如果打开了外部资源(文件、数据库连接、网络连接等),我们必须在这些外部资源使用完毕后,手动关闭它们。因为外部资源不由JVM管理,无法享用JVM的垃圾回收机制,如果我们不在编程时确保在正确的时机关闭外部资源,就会导致外部资源泄露,紧接着就会出现文件被异常占用,数据库连接过多导致连接池溢出等诸多很严重的问题。

当try退出时,会自动调用close方法,去读取单词中的所有单词,示例如下:

try(Scanner in = new Scanner(New FileInputStream("/user/share/dict/words")),"UTF-8"){    while(in.hasNext()){		System.out.println(in.next());    }}

如果try抛出一个异常,而且close方法也抛出一个异常,这样就会带来一个难题。到资源的try可以很好地处理。原来的close方法抛出的异常被抑制。这些异常将会自动捕获,并由addSuperssed方法增加到原来的异常。如果对这些异常感兴趣,可以调用getSupperssed方法,它会得到从close抛出并抑制的异常列表。

自定义异常

有时候Java内置异常不满足于我们的需求,我们就需要用到自定义异常。比如:系统中有些错误是符合Java语法的,但不符合我们项目的业务逻辑;企业项目是分模块或者分功能开发的 ,使用自定义异常类就统一了对外异常展示的方式等。

所有异常都必须是 Throwable 的子类。
如果希望写一个检查性异常类,则需要继承 Exception 类。
如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。

创建一个检查性异常类

public class DatamartAssertException extends Exception {	    private static final long serialVersionUID = 7358625131054183395L;	    public DatamartAssertException() {		    }		    public DatamartAssertException(String message) {      	super(message);    }  	}

抛出和使用自定义异常

public static void main(String[] args) throws DatamartAssertException{        CloseableHttpClient httpClient = null;        try{            //创建httpClient实例            httpClient  = HttpClients.createDefault();            //地址url            String url = "www.baidu.coms";            //创建HttpPost实例            HttpPost method = new HttpPost(url);            //设置请求读取超时时间 60秒            RequestConfig requestConfig = RequestConfig.custom()                    .setConnectTimeout(60000).setConnectionRequestTimeout(60000)                    .setSocketTimeout(60000).build();            method.setConfig(requestConfig);            //执行请求            try{                HttpResponse result = httpClient.execute(method);                /**请求发送成功,并得到响应**/                if (result.getStatusLine().getStatusCode() == 200) {                    String str = "";                    /**读取服务器返回过来的json字符串数据**/                    str = EntityUtils.toString(result.getEntity());                }            }catch (Exception e){                e.printStackTrace();            }        }catch(ConnectTimeoutException e){            // 捕获超时异常 并反馈给调用者            throw new DatamartAssertException("请求超时!");        }catch(ConnectException e) {            throw new DatamartAssertException("请求超时!");        }catch (Exception e){            e.printStackTrace();        }        finally{            /**关闭连接,释放资源**/            try {                httpClient.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }

总结:不要过分的细化异常,不要压制异常;早抛出,晚捕获。

 

--------------如果大家喜欢我的博客,可以点击左上角的关注哦。

你可能感兴趣的文章
Jackson Tree Model Example
查看>>
常用js收集
查看>>
如何防止sql注入
查看>>
springmvc传值
查看>>
在Eclipse中查看Android源码
查看>>
Android使用webservice客户端实例
查看>>
[转]C语言printf
查看>>
C 语言学习 --设置文本框内容及进制转换
查看>>
C 语言 学习---判断文本框取得的数是否是整数
查看>>
C 语言 学习---ComboBox相关、简单计算器
查看>>
C 语言 学习---ComboBox相关、简易“假”管理系统
查看>>
C 语言 学习---回调、时间定时更新程序
查看>>
C 语言 学习---复选框及列表框的使用
查看>>
第十一章 - 直接内存
查看>>
JDBC核心技术 - 上篇
查看>>
一篇搞懂Java反射机制
查看>>
Single Number II --出现一次的数(重)
查看>>
Palindrome Partitioning --回文切割 深搜(重重)
查看>>
对话周鸿袆:从程序员创业谈起
查看>>
Mysql中下划线问题
查看>>