来自大厂的11条异常最佳实践

引言

此文的灵感来自于

来自大厂的11条异常最佳实践

异常,这个名词对于开发同学来说应该非常不陌生,是在我们程序出现错误或者业务逻辑不通的时候,给予程序一个安全退出的通道

如何优雅的进行异常处理是一门艺术,本文就给大家提一提处理异常的正确姿势(默认阅读本文的小伙伴已经掌握了异常相关的基础知识)

来自大厂的11条异常最佳实践

优雅异常处理

「线上代码禁止使用 printStackTrace()」

printStackTrace()只会在控制台上输出错误的堆栈信息,他只适合于用来代码调试。

真正需要记录异常,请使用日志记录

Java 类库中定义的可以通过预检查方式规避的 RuntimeException 异常不应该通过 catch 的方式来处理」

比如:NullPointerException,IndexOutOfBoundsException 等等。

说明:无法通过预检查的异常除外,比如,在解析字符串形式的数字时,可能存在数字格式错误,不得不 通过 catch NumberFormatException 来实现。

// 正例
if (obj != null) {...}

//反例
try { obj.method(); } catch (NullPointerException e) {...}

「异常捕获后不要用来做流程控制,条件控制」

异常设计的初衷是解决程序运行中的各种意外情况,而不是做 iflese 这样的条件控制,在 catch 里去做业务逻辑,catch 中一般处理异常的抛出,日志的记录,并且异常的处理效率比条件判断方式要低很多

「catch时请分清稳定代码和非稳定代码,稳定代码指的是无论如何不会出错的代码。对于非稳定代码的 catch 尽可能进行区分异常类型,再做对应的异常处理」

对大段代码进行 try-catch,使程序无法根据不同的异常做出正确的应激反应,也不利于定位问题, 这是一种不负责任的表现。

就比如我们一个用户提现的逻辑,我们需要抛出异常给程序提示 余额不足不允许穷鬼提现、这是个坏人他输入的密码错误 等等,而不是一股脑的提示 提现失败

「捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的内容」

// 反例
catch (NoSuchMethodException e) {
   return null;
}

// 正例
catch (NoSuchMethodException e) {
   throw new BizException("错误码","错误原因")
}

事务场景中,抛出异常被catch后,如果需要回滚,一定要注意手动回滚事务」

// 正例
public void test() {
      TransactionDefinition tra = new DefaultTransactionDefinition();
      TransactionStatus status = transactionManager.getTransaction(tra);

       try {
         // 事务操作
         // 事务提交
         transactionManager.commit(status);
      } catch (DataAccessException e) {
         // 事务提交
         transactionManager.rollback(status);
         throw e;
      }
}

「finally块必须对资源对象、流对象进行关闭,有异常也要做try-catch」

// 通常做法
catch (Exception e) {
  e.printStackTrace(); // 第一处异常处理
 }
 finally {
        try {
            if (c != null) {
                c.close();
            }
        } catch (IOException e) {
            e.printStackTrace(); // 第二处异常处理
        }
    }

// 说明:如果 JDK7 及以上,可以使用 try-with-resources 方式。
public class Main {
    public static void startTest() {
        try (MyAutoCloseA a = new MyAutoCloseA();
             MyAutoCloseB b = new MyAutoCloseB()) {
            a.test();
            b.test();
        } catch (Exception e) {
            System.out.println("Main: exception");
            System.out.println(e.getMessage());
            Throwable[] suppressed = e.getSuppressed();
            for (int i = 0; i < suppressed.length; i++)
                System.out.println(suppressed[i].getMessage());
        }
    }

    public static void main(String[] args) throws Exception {
        startTest();
    }
}

「不要在 finally 块中使用 return」

try 块中的 return 语句执行成功后,并不马上返回,而是继续执行 finally 块中的语句,如果此处存 在 return 语句,则在此直接返回,无情丢弃掉 try 块中的返回点。

    // 反例:
    private int x = 0;
    public int checkReturn() {
        try {
            // x 等于 1,此处不返回
            return ++x;
        } finally {
            // 返回的结果是 2
            return ++x;
        }
    }

另外,不要在 finally 块中抛出异常

「捕获异常与抛异常,必须是完全匹配,或者捕获异常是抛异常的父类」

如果预期对方抛的是绣球,实际接到的是铅球,就会产生意外情况。

「在调用 RPC、二方包、或动态生成类的相关方法时,捕捉异常必须使用 Throwable 类来进行拦截」

通过反射机制来调用方法,如果找不到方法,抛出 NoSuchMethodException。什么情况会抛出 NoSuchMethodError 呢?二方包在类冲突时,仲裁机制可能导致引入非预期的版本使类的方法签名不匹配, 或者在字节码修改框架(比如:ASM)动态创建或修改类时,修改了相应的方法签名。这些情况,即使代 码编译期是正确的,但在代码运行期时,会抛出 NoSuchMethodError

「不打算处理异常的话,使用finally块进行处理,而不是catch块」

try {
  someMethod(); 

finally
{
  cleanUp();    
}

如果在方法中出现了异常,而不想处理,可以在 finally 中直接处理,无需 catch


参考:

阿里巴巴:《Java开发手册》

谨思慎言,我是 Skow

点个赞再走吧,我们下期再见


原文始发于微信公众号(Issues):来自大厂的11条异常最佳实践

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/107338.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!