Welcome everyone

探究 Java异常机制

java 汪明鑫 678浏览 0评论

异常体系

 

【分类】

  • Error:程序无法处理的系统错误,编译器不做检查
  • Exception:程序可以处理的异常,捕获后可能恢复
  • RuntimeException:不可预知的,程序应当自行避免(比如加入if(name!=null))
  • 非RuntimeException:可预知的,从编译器校验的异常(受检异常)

 

【从责任角度看】

  1. Error属于JVM需要负担的责任
  2. RuntimeException是程序应该负担的责任
  3. CheckedException可检查异常是java编译器应该负担的责任

 

【RuntimeException常见的异常】

  1. NullPointerException – 空指针引用异常
  2. ClassCastException – 类型强制转换异常
  3. IllegalArgumentException – 传递非法参数异常
  4. IndexOutOfBoundsException – 下标越界异常
  5. NumberFormatException – 数字格式异常
  6. ArithmeticException – 算术运算异常
  7. ArrayStoreException – 向数组中存放与声明类型不兼容对象异常
  8. NegativeArraySizeException – 创建一个大小为负数的数组错误异常
  9. SecurityException – 安全异常
  10. UnsupportedOperationException – 不支持的操作异常

【非RuntimeException常见异常】

  1. ClassNotFoundException 找不到指定class的异常
  2. IOexception IO操作异常
  3. FileNotFoundException 找不到指定文件的异常

【Error】

  1. NoClassDefFoundError 找不到class定义的异常(class或jar不存在、类文件存在但是在不同的域中、大小写问题,javac无视大小写)
  2. StackOverflowError 深递归导致栈被消耗尽而抛出的异常
  3. OutOfMemoryError 内存溢出异常

 

ps:一般我们最需要关注 RuntimeException

 

 

受检异常  VS 运行时异常

受检异常意思是说必须try-catch捕捉,否则编译器报错,如 IOException、ClassNotFoundException

运行时异常是程序逻辑错误引起的,比如数组下标越界

 

先给个例子简单感受下:

/**
 * @author wmx
 * @date 2019-08-19
 */
public class TestException {
    public static void main(String[] args) {
        int a = 6;
        int b = 0;

        try {
            if (b == 0) {
                throw new ArithmeticException();
                //"除数为0"等ArithmeticException,是RuntimException的子类。而运行时异常将由运行时系统自动抛出,不需要使用throw语句,这里把throw new ArithmeticException()去掉也是不影响运行结果的。
            }
            System.out.println("a/b的值是:" + a / b);
        } catch (ArithmeticException e) {
            System.out.println("程序出现异常,变量b不能为0。");
            //throw new ArithmeticException();
            //System.out.println("end");
        }

        System.out.println("程序正常结束。");
    }

}

运行时异常,并且把原始异常捕捉了,并且吞掉了

 

程序输出:

程序出现异常,变量b不能为0。
程序正常结束。

 

说明:

捕捉异常,会使try语句块后面的语句执行不到,

但是程序并不会中断

 

如果代码改成这样呢?

public class TestException {
    public static void main(String[] args) {
        int a = 6;
        int b = 0;

        try {
            if (b == 0) {
                throw new ArithmeticException();
                //"除数为0"等ArithmeticException,是RuntimException的子类。而运行时异常将由运行时系统自动抛出,不需要使用throw语句,这里把throw new ArithmeticException()去掉也是不影响运行结果的。
            }
            System.out.println("a/b的值是:" + a / b);
        } catch (ArithmeticException e) {
            System.out.println("程序出现异常,变量b不能为0。");
            throw new ArithmeticException();
            //System.out.println("end");
        }

        System.out.println("程序正常结束。");
    }

}

程序输出:

程序出现异常,变量b不能为0。
Exception in thread “main” java.lang.ArithmeticException
at pers.wmx.exception.TestException.main(TestException.java:20)

 

把捕捉的异常向外抛,程序中断

但是在方法处不用显示的throws  ArithmeticException

 

再往下看,受检异常方法名必须得throws

public class TestException1 {
    public static void main(String[] args) throws IOException {
        int a = 6;
        int b = 0;

        try {
            if (b == 0) {
                throw new IOException();
            }
            System.out.println("a/b的值是:" + a / b);
        } catch (IOException e) {
            System.out.println("程序出现异常,变量b不能为0。");
            throw new IOException();

        }

        System.out.println("程序正常结束。");
    }

}

 

 

 

异常使用的正确姿势

不知道你是否和我一样使用异常时常常瞎球用,乱try-catch,

丢到原始异常,用异常控制业务逻辑等等

至少到现在为止我不能保证我完全掌握Java异常的正确使用

 

  • 不要吞掉原异常,可以打log或者再抛出去;
  • 不要在for循环中try-catch;
  • 不要使用异常来控制业务逻辑;
  • 不要直接捕获泛泛的异常 Exception;
  • try-catch的代码快不要太大,不好定位,会降低jvm优化效率;
  • try-catch产生性能问题,会落快照;
  • 不知道怎么处理的异常直接抛出;

 

(持续更新…)

 

多个catch块哪个生效

如果多个catch块,异常的父类写在前面,后面的子类就catch不到,直接报错

异常的父类写后面父类异常也catch不到,比如这样

public static void main(String[] args){
        int a = 6;
        int b = 0;

        try {
            if (b == 0) {
                throw new ArithmeticException();
                //"除数为0"等ArithmeticException,是RuntimException的子类。而运行时异常将由运行时系统自动抛出,不需要使用throw语句,这里把throw new ArithmeticException()去掉也是不影响运行结果的。
            }
            System.out.println("a/b的值是:" + a / b);
        }
        catch (ArithmeticException e2) {
            System.out.println("程序出现异常,变量b不能为0。");
            //throw new ArithmeticException();
            //System.out.println("end");
        }catch (Exception e){
            System.out.println("Exception caught");
        }

        System.out.println("程序正常结束。");
    }

 

是不是有点感觉了?

 

try-catch-finally 中的return

显然在开发当中我们并不会在catch和finally中return

但是为了更好的理解异常的捕捉我们简单的了解些,而且有面试官也爱问这一块

/**
 * @author wmx
 * @date 2019-11-19
 */
public class TestException2 {

    public static void main(String[] args) {
        System.out.println(output());
    }

    private static int output() {
        try{
            System.out.println("try-start...");
            int a = 1/0;
            return 1;
        }catch (Exception e){
            System.out.println("catch-start...");
            return 2;
        }finally {
            System.out.println("finally-start...");
            return 3;
        }
    }
}

有finally的情况下return3

没有finally的情况下捕捉到异常return2

 

这个还是没什么难度的

 

dao调mapper接口异常如何处理

方案一:

捕捉数据库异常,吞掉原有异常,打log

 

方案二:

捕捉数据库异常,吞掉原有异常,抛出父类异常或自定义异常

 

方案三:

不管异常,让系统自己向上抛

 

你觉得哪种方案更好呢?

 

转载请注明:汪明鑫的个人博客 » 探究 Java异常机制

喜欢 (0)

说点什么

您将是第一位评论人!

提醒
avatar
wpDiscuz