Java异常

Java异常的分类

Java中的所有的异常都是Throwable的子类,在大的方面分为ErrorException两大类,Exception又可以分为非检查异常(UncheckedException,继承于RuntimeException)和检查异常(CheckedException继承于Exception,但不继承于RuntimeException),如下图所示:

java异常结构图

Error

Error通常用于描述系统级别的错误,当出现系统级别的错误的时候,系统环境已经不健康了,因此Error不需要强制捕获和声明,也不强制处理.常见的Error有OutOfMemoryError,StackOverflowError,InternalError,UnknownError等.

平时写代码的时候应该尽量避免一些可避免的Error,例如OutOfMemoryError,在大文件处理或使用缓存的时候容易出现.

非检测异常(UncheckedException)

直接或间接继承于RuntimeException以及其本身都属于非检测异常.

该类异常无需显示捕捉和处理,适用于调用的代码不能继续执行,需要立即终止的情况.例如数组越界,调用null对象的实例方法或属性,方法参数的检测等.

出现非检测异常的情况大部分情况是代码写挫了,此时记下日志即可.

常见的RuntimeException

  • NullPointerException: 空指针异常,当应用试图在要求使用对象的地方使用了null时,抛出该异常.
  • IllegalArgumentException: 方法被传入了非法或不适当的参数.
  • IndexOutOfBoundsException: 下标越界异常,数组、字符串或者集合的索引超出了范围。
  • IllegalStateException: 非法状态异常(表示在非法或不适当的时间调用了方法)
  • ArithmeticException: 算术条件异常.例如:整数除零.
  • SecurityException: 安全性异常.
  • ClassCastException: 类型强制转换异常.

当对方法的参数进行检查时,参数值为null,此时应该抛出IllegalArgumentException还是NullPointerException?
参见: https://stackoverflow.com/questions/3881/illegalargumentexception-or-nullpointerexception-for-a-null-parameter
个人理解:虽然《Effective Java》和jdk源码更倾向于抛出NullPointerException,但是不够直观,容易造成误解,例如以下一段代码;

//如果这行代码报NullPointerException异常,我们首先会怀疑loginModule为null,而不是userName或者password为null.
loginModule.login(userName,password);

其次,IllegalArgumentException就是设计用于参数检查的,不应该为了参数null的检查,而引入其它类型的异常.
所以,参数检查时,参数为null时,使用IllegalArgumentException会更加合理.

检测异常(CheckedException)

检测异常指的是继承于Exception,但不继承于RuntimeException的异常.

该类异常需要显示使用catch进行捕捉,当catch到检测异常时,可以选择进一步的恢复或处理其它一些事情.
例如IOException,当catch到IOException以后,可以对io流进行及时的关闭.

常见的检测异常

  • ClassNotFoundException:当程序尝试使用字符串的形式加载类时,但是未找到相应的类.
  • FileNotFoundException:尝试打开指定路径的文件失败.
  • InterruptedException:一个线程在等待、睡眠或其他方式占用时,并且该线程在活动之前或期间被中断.
  • IOException:程序进行IO操作失败或被中断.
  • SQLException:数据库访问错误.

常见误区

参见: https://www.ibm.com/developerworks/cn/java/j-lo-exception-misdirection/index.html

异常类型不够明确

抛出的异常越明确越好,越明确的异常越能提供更多的信息,抛出的异常最好需要携带message,用于对异常出现的原因进行描述,方便以后更准确的定位问题,例如下面参数的检查:

public String md5(String data) {
    if (data == null) {
        // 这里抛出的异常不够具体,而且抛出的异常不含message.
        throw new RuntimeException();
    }
    ...
}

上面的代码抛出的异常不够具体,而且没有message,应该为下面的形式:

public String md5(String data) {
    if (data == null) {
        throw new IllegalArgumentException("data is null.");
    }
    ...
}

直接将异常显示在页面或者客户端

实际上任何异常对用户而言没有实际意义,可以在异常中引入错误代码,一旦出现异常,只需要将异常的额代码呈现给用户,或者将错误代码换成更通俗易懂的提示,开发人员也可以根据错误代码去排查问题.

对代码层次结构的污染

例如下面一段代码:

  public boolean insert(String name,String pwd)throws SQLException{
	//将用户名和密码插入数据库
  }

上面的代码将会污染到上层调用的代码,我们可以改为下面一种形式:

public boolean insert(String name,String pwd){
    try{
        //将用户名和密码插入数据库
    }catch(SQLException e){
        //利用非检测异常封装检测异常,降低层次耦合
        throw new RuntimeException(SQLErrorCode, e);
    }finally{
        //关闭连接,清理资源
    }
}

忽略异常

例如下面的异常,仅仅是将异常输出到控制台,没有任何实际意义.而且代码将会继续执行,进而导致其它无关的异常.

public boolean insert(String name,String pwd){
    try{
        //将用户名和密码插入数据库
    }catch(SQLException e){
        //这里直接将异常打印到控制台并没有实际意义
        //这里可以将异常post到服务器,也可以转换为RuntimeException抛出.
        e.printStacktrace();
    }finally{
        //关闭连接,清理资源
    }
}

将异常包含在循环语句块中

下面的代码可能会导致catch中的代码块执行多次.

for(int i=0; i<100; i++){
    try{

    }catch(XXXException e){
         //...
    }
}

另一种情况与上面的情况类似:A类循环调用B类中的某个方法,该方法块中存在try-catch这样的语句块.

利用Exception捕捉所有潜在的异常

一段代码块中可能会抛出几种不同类型的异常,可能为了代码的简洁,会使用基类Exception捕捉所有潜在的异常.这样会丢失原有的异常详细信息;

public void retrieveObjectById(Long id){
    try{
        //…抛出 IOException 的代码调用
        //…抛出 SQLException 的代码调用
    }catch(Exception e){
        //这里利用基类 Exception 捕捉的所有潜在的异常,如果多个层次这样捕捉,会丢失原始异常的有效信息
        throw new RuntimeException(“Exception in retieveObjectById”, e);
    }
}

可以改为以下形式:

public void retrieveObjectById(Long id){
    try{
        //…抛出 IOException 的代码调用
        //…抛出 SQLException 的代码调用
    }catch(IOException e){
        //仅仅捕捉 IOException
        throw new RuntimeException(/*指定这里 IOException 对应的错误代码*/code,“Exception in retieveObjectById”, e);
    }catch(SQLException e){
        //仅仅捕捉 SQLException
        throw new RuntimeException(/*指定这里 SQLException 对应的错误代码*/code,“Exception in retieveObjectById”, e);
    }
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值