java异常 分类和处理_Java

一、java异常分类

Java语言按照错误严重性,从throwale根类衍生出Error和Exception两大派系:

  • Error(错误):程序在执行过程中所遇到的硬件或操作系统的错误。错误对程序而言是致命的,将导致程序无法运行。常见的错误有内存溢出,jvm虚拟机自身的非正常运行,calss文件没有主方法。程序本生是不能处理错误的,只能依靠外界干预。Error是系统内部的错误,由jvm抛出,交给系统来处理
  • EXCEPTION(异常):是程序正常运行中,可以预料的意外情况。比如数据库连接中断,空指针,数组下标越界。异常出现可以导致程序非正常终止,也可以预先检测,被捕获处理掉,使程序继续运行

EXCEPTION(异常)按照性质,又分为编译异常(可检测)和运行时异常(不可检测):

  • 编译时异常:又叫可检查异常,通常时由语法错和环境因素(外部资源)造成的异常。比如输入输出异常IOException,数据库操作SQLException。其特点是,Java语言强制要求捕获和处理所有非运行时异常。通过行为规范,强化程序的健壮性和安全性。
  • 运行时异常:又叫不检查异常RuntimeException,这些异常一般是由程序逻辑错误引起的,即语义错。比如算术异常,空指针异常NullPointerException,下标越界IndexOutOfBoundsException。运行时异常应该在程序测试期间被暴露出来,由程序员去调试,而避免捕获。

说到异常处理,这里就不得不提try/catch/finally。try不可以单独存在,要么搭配catch,要么搭配finally,或者三者并存:

  1. try代码块:监视代码块的执行,发现对应的的异常则跳转至catch,若无catch则直接到finally块。
  2. catch代码块:发生对应的异常会执行里面的代码,要么处理,要么向上抛出。
  3. finally代码块:不管是否有异常,都必执行,一般用来清理资源,释放连接等。然而有以下几种情况不会执行到这里的代码。

             ① 代码执行流程未进入try代码块。

             ② 代码在try代码块中发生死循环、死锁等状态。

             ③ 在try代码块中执行了System.exit()操作。

二、异常的处理方式

1、不处理

遇到异常问题不进行具体处理,而是继续抛给调用者 (throw,throws)
抛出异常有三种形式:

  • throw
  • throws
  • 系统自动抛异常
public static void main(String[] args) {
    String s = "abc";
    if(s.equals("abc")) {
        // 手动抛出一个数字格式化异常
        throw new NumberFormatException();
    } else {
        System.out.println(s);
    }
}

int div(int a,int b) throws Exception{
    return a/b;
}

throws和throw区别:

  • throws 表示出现异常的一种可能性,并不一定会发生这些异常;
  • throw 则是抛出了异常,执行 throw 则一定抛出了某种异常对象。

2、处理

void doA(int a) throws IOException,{
   try{
      ......
   }catch(Exception1 e){
      throw e;
   }catch(Exception2 e){
      System.out.println("出错了!");
   }
   if(a!=b)
      throw new  Exception3("自定义异常");
}

三、最佳实践

好了,前面简单介绍了异常的分类以及try/catch/finally的注意事项,现在可以总结一下我们在异常处理的时候有哪些”最佳实践“了。

  1. 当需要向上抛出异常的时候,需根据当前业务场景定义具有业务含义的异常,优先使用行业内定义的异常或者团队内部定义好的。例如在使用dubbo进行远程服务调用超时的时候会抛出DubboTimeoutException,而不是直接把RuntimeException抛出。

  2. 请勿在finally代码块中使用return语句,避免返回值的判断变得复杂。

  3. 捕获异常具体的子类,而不是Exception,更不是throwable。这样会捕获所有的错误,包括JVM抛出的无法处理的严重错误。

  4. 切记更别忽视任何一个异常(catch住了不做任何处理),即使现在能确保不影响逻辑的正常运行,但是对于将来谁都无法保证代码会如何改动,别给自己挖坑。

  5. 不要使用异常当作控制流程来使用,这是一个很奇葩也很影响性能的做法。

  6. 清理资源,释放连接等操作一定要放在finally代码块中,防止内存泄漏,如果finally块处理的逻辑比较多且模块化,我们可以封装成工具方法调用,代码会比较简洁。