Java中异常处理

752 阅读4分钟

        从毕业到现在也经历了好几个项目组,以前项目中的代码异常处理都很混乱,现在项目中代码比以前质量有提高,所以想写一篇关于异常处理的文章来探讨一下异常处理

1、捕获异常的原则

        在处理异常之前我们要先问自己一个问题,这个异常我们能处理吗?如果自己处理不了就将它抛出去,交给调用者处理。捕获了异常是为了处理它,不要捕获了之后什么也不处理,类似下面的代码就毫无意义

public static void division() throws Exception {
        try {
            int i = 15/0;
        } catch(Exception e){
            throw e;
        }
 }

或者没有记录异常信息,或者记录信息不规范,导致异常被吞掉,不方便问题定位

public static void division() throws Exception {
        try {
            int i = 15/0;
        } catch(Exception e){
            e.getMesage();
        }
 }

最常见的就是记录异常堆栈信息Logger.debug("xxxxxxxxxxx",e);因为堆栈信息中记录了出错的详细信息包括出错的代码行号,对于定位问题太有帮助了。

2、对异常进行细化

不要笼统的捕获Exception,最好进行细分,异常处理是采用责任链模式,最后一层采用最大Excepion兜底即可

3、防止空指针异常

空指针是程序中最容易出现的异常

1、自动拆箱有可能会空指针

2、集合中的元素取出来操作时候有可能会有空指针

3、级联调动obj.get().get().get();可能会有空指针异常

4、远程调用返回的结果可能会有空指针异常

5、session中获取的数据有可能为null

6、数据库查询结果可能返回null

4:e.getMessage()、e.toString()、e.printStackTrace()、+e区别

      所以捕获到异常记录日志时候一定要记录异常的堆栈信息,不仅有异常类型、异常说明、还有异常代码的位置,e.getMessage()、e.toString()都是不规范的写法,不利于问题的定位,e.printStackTrace()是打印在控制台方便查看的,自然正式代码上不能有这个操作,+e这个也不是规范的写法,如果代码里有敏感信息,+e会打印敏感信息

5、Finally块

       对于涉及的文件io操作的,或者数据库连接操作的,或者其他连接操作的等等,一定要在Finally块中对其连接进行关闭,避免资源的浪费久而久之造成内存溢出

      不要在Finally块中有return操作,return操作导致方法返回出去,有可能导致异常块以外的代码没有执行到,Finally块中有return操作要谨慎

6、自定义异常

       项目中我们经常会自定义异常,这个正常,原始的异常也许并不能发表我们的业务意思,所以我们会自己定义异常,有错误码和错误码描述,更好的表达业务错误,这里我定义的异常类型是受检异常,当然你也可以定义RuntimeException。受检异常,调用者一定要去处理。RuntimeException调用者就不要显示的去处理了。

package com.my.test.Exception;

public class MyException extends Exception {

    private String code;

    private String message;

    public MyException(){
        super();
    }
    public MyException(Exception e){
        super(e);
    }

    public MyException(String code,String message){
        this.code = code;
        this.message = message;
    }

    public MyException(String code,String message,Exception e){
        super(e);
        this.code = code;
        this.message = message;
    }
}

package com.my.test.Exception;

public class Test {

    public static void main(String[] args) throws MyException {
        int n = division();
        System.out.println(n);
    }

    public static int division() throws MyException {
        try {
            int i = 15/0;
            return i;
        } catch(Exception e){
            throw new MyExcception("10001","参数不能为0");
        }
    }
}
这里最好能记录一下日志,打印原始的堆栈信息

7、异常信息查看

      正常的业务代码我们的调用链都很长从Controler到service,再到dao,中间还有各种各样的业务处理。然后抛出了一个自定义异常,看到错误的堆栈信息很长,怎么快速查看代码出错位置呢

一:有自定义异常

package com.my.test.Exception;

public class Test {

    public static void main(String[] args) throws MyExcception {
        int n = before();
        System.out.println(n);
    }

    public static int before () throws MyExcception {
        System.out.println("begin........");
        return division();
    }
    
    public static int division() throws MyExcception {
        try {
            int i = 15/0;
            return i;
        } catch(Exception e){
            throw new MyExcception("10001","参数不能为0",e);
        }
    }
}

begin........
Exception in thread "main" com.my.test.Exception.MyExcception: java.lang.ArithmeticException: / by zero
	at com.my.test.Exception.Test.division(Test.java:22)
	at com.my.test.Exception.Test.before(Test.java:13)
	at com.my.test.Exception.Test.main(Test.java:6)
Caused by: java.lang.ArithmeticException: / by zero
	at com.my.test.Exception.Test.division(Test.java:19)
	... 2 more
我们要看Caused by,Caused by后面才是代码出错的位置,上面记录的是自定义异常,调用的堆栈信息,
Caused by才是真正的代码出错位置

二:没有自定义异常
package com.my.test.Exception;

public class Test {

    public static void main(String[] args){
        int n = before();
        System.out.println(n);
    }

    public static int before (){
        System.out.println("begin........");
        return division();
    }

    public static int division()  {
            int i = 15/0;
            return i;
    }
}
begin........
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at com.my.test.Exception.Test.division(Test.java:17)
	at com.my.test.Exception.Test.before(Test.java:12)
	at com.my.test.Exception.Test.main(Test.java:6)

Process finished with exit code 1

我们知道栈是先进后出的,a()->b()->c()所以最上面栈信息我们应该最先关注的地方,然后依次向下

8、日志打印

一般在异常的地方会记录日志业务异常一般记录warn级别日志,比如参数不合法,等

Exception异常一般记录error级别日志,需要开发人员定位跟踪