• Java Exception最佳实践(转)


    https://www.dubby.cn/detail.html?id=9033

    本篇文章主要给大家介绍一些众所周知的异常处理原则,但是也有部分鲜为人知,但也很有用的原则,希望能引发各位对异常处理的思考,以及在开发过程中,写出更优美的代码。

    1.异常介绍

    大致可以把异常分成三种情况下的异常(不正常情况):

    1. 代码错误引发的异常:比如数组越界,空指针等。
    2. 客户端错误调用引发的异常:比如用户名最长只允许32,客户端传了100;方法参数不能为空,客户端传了空等。
    3. 资源错误引发的异常:比如网络错误,硬盘故障,文件被删等。

    2.Java中的异常介绍

    无图言屌,下面就给出异常的继承关系图:

    主要说说这几个概念:

    • Checked exceptions:这种异常在代码层面必须要捕获或者在签名处申明这个异常。这种异常是Java强制你必须捕获,因为这些一般一般是不可避免的,比如:网络,文件系统等不可控因素。
    • Unchecked exceptions:这种异常不会强制捕获或者在签名处申明。这类异常一般是由于代码问题产生的,比如:数组越界,空指针等。
    • Errors:这类错误,一般是在软件层面不可恢复的。比如:OutOfMemoryErrorLinkageError, 还有StackOverflowError。这种错误一般会是的程序(或者程序的一部分)不可用。针对这类错误,一定要有一个良好的日志习惯,不然很难定位。

    3.自定义异常

    一般我们想要自定义异常的目的都是为了让异常信息更丰富,比如:输入名称不合法,我们可能希望有一个UsernameInvalidException。本人在代码中曾经这么干过,一个类似的用法,好处很明显,在我们的内部监控系统中,对异常统计界面可以很清晰的反映出是什么问题,但是也带来一个问题,那就是异常数量很多。

    在这里我先摆几个大师的意见吧:

    • 不要使用自定义异常:
      Java已经给我们提供了很多很多异常,尽量复用这些异常,好处有:减少我们的代码量,也就减少了维护的成本和精力,不至于让代码中出现很多只用过一次或几次的异常,最后异常数量爆炸(这就是我遇到的);使用通用异常,也可以减少别人阅读我们的代码,使用我们的接口时,更轻松,毕竟多一个类我们就需要理解这个类存在的意义。这里给几个经常可以用到的异常:
      1. IllegalStateException
      2. UnsupportedOperationException
      3. IllegalArgumentException
      4. NoSuchElementException
      5. NullPointerException
    • 如果不得不自定义异常,那就写个通用异常:
      如果自己不得不写的话,那就写的详细一下,不要只有个String来传达信息,那完全可以用通用的异常来替代,给个包含详细信息的例子:
    1. public class OutOfRangeException
    2. extends IllegalArgumentException {
    3. private final long value, min, max;
    4. public OutOfRangeException(long value, long min, long max) {
    5. super("Value " + value + " out of range " +
    6. "[" + min + ".." + max + "]");
    7. this.value = value;
    8. this.min = min;
    9. this.max = max;
    10. }
    11. public long getValue() {
    12. return value;
    13. }
    14. public long getMin() {
    15. return min;
    16. }
    17. public long getMax() {
    18. return max;
    19. }
    20. }

    4.几个建议

    1)不要生吞异常

    1. catch (NoSuchMethodException e) {
    2. return null;
    3. }

    这样做会让这个异常信息永远的丢失,你将无法知道这个异常的原因,怎么去解决这个异常,甚至你不知道有这个异常的存在。

    2)申明具体的异常

    1. public void foo() throws Exception { //不正确的做法
    2. }

    这样除了告诉调用方我可能会有异常之外没有任何其他信息,而事实是我们本可以提供更具体的信息,建议这样做:

    1. public void foo() throws SpecificException1, SpecificException2 { //正确的做法
    2. }

    3)尽可能的捕获具体异常

    1. try {
    2. someMethod();
    3. } catch (Exception e) {
    4. LOGGER.error("method has failed", e);
    5. }

    这么做的问题是,如果你调用的方法中多了一个新的异常,他本来的目的是希望你处理这个新的异常,可是因为你在这里捕获了所有的异常,你可能会忽略这个提醒,而忘记捕获。

    4)永远不要捕获Throwable

    这样会捕获本来不该有我们来处理的错误,包括一些我们的代码无法处理的错误。

    5)不要丢失异常信息

    1. catch (NoSuchMethodException e) {
    2. throw new MyServiceException("Some information"); //不正确
    3. }

    这样做会完全丢失异常信息。

    1. catch (NoSuchMethodException e) {
    2. throw new MyServiceException("Some information: " + e.getMessage()); //不正确
    3. }

    这种做法会丢失堆栈信息,建议:

    1. catch (NoSuchMethodException e) {
    2. throw new MyServiceException("Some information: " , e); //正确
    3. }

    6)日志和上抛不可兼得

    1. catch (NoSuchMethodException e) {
    2. LOGGER.error("Some information", e);
    3. throw e;
    4. }

    这样会导致一个问题,就是一个异常会有多份日志,因为上层可能也会记一次日志。所以要么上抛,要么记日志,不要都做。

    7)不要在finally里抛异常

    1. try {
    2. someMethod(); //Throws exceptionOne
    3. } finally {
    4. cleanUp(); //If finally also threw any exception the exceptionOne will be lost forever
    5. }

    这样的问题是,如果finally里也抛异常,就会导致真正的异常信息丢失,你只会收到finally里抛的异常。

    8)不要为了捕获而捕获

    1. catch (NoSuchMethodException e) {
    2. throw e; //Avoid this as it doesn't help anything
    3. }

    这段代码没有任何有意义,你可以直接上抛。

    9)不要使用printStackTrace()或类似的语句

    这种输出没有任何意义,而且不确定输出路径,对定位问题没有帮助。

    10)不一定要catch

    1. try {
    2. someMethod(); //Method 2
    3. } finally {
    4. cleanUp(); //do cleanup here
    5. }

    如果你只是想要finally来做善后,那就只用它就可以了,不要用catch。

    11)“Throw early catch late”

    这句话我不想翻译,因为我希望你能看到这句话,以后你也会见到这句话的。Throw early catch late。在底层抛异常,在信息足够的时候来捕获并处理。

    12)记得用finall善后

    比如数据库连接,一定要用finally关闭连接。当然你也可以用try-with-resource的方式。

    13)上抛信息明确的异常

    如果这个方法是解析文件,那么FileNotFoundException就比NullPointException更明确。

    14)永远不要使用异常来做流程控制

    1. public void useExceptionsForFlowControl() {
    2. try {
    3. while (true) {
    4. increaseCount();
    5. }
    6. } catch (MaximumCountReachedException ex) {
    7. }
    8. //Continue execution
    9. }
    10. public void increaseCount()
    11. throws MaximumCountReachedException {
    12. if (count >= 5000)
    13. throw new MaximumCountReachedException();
    14. }

    算我求你了,不要这么干!

    15)尽早校验输入

    很多异常都是由不合法的输入引起的,所以尽可能早的校验输入。

    16)一条信息打印异常

    1. LOGGER.debug("Using cache sector A");
    2. LOGGER.debug("Using retry sector B");

    何必呢?而且这样也容易误导其他人,建议:

    1. LOGGER.debug("Using cache sector A, using retry sector B");

    17)让你的异常信息更充实

    包括堆栈和其他提示信息。

    18)如果线程被interrupted一定要结束线程

    1. while (true) {
    2. try {
    3. Thread.sleep(100000);
    4. } catch (InterruptedException e) {} //Don't do this
    5. doSomethingCool();
    6. }

    这段代码很cool,但是一般会interrupt线程,要么是超时了,要么是线程池被关闭了,所以你应该尽可能的去结束线程。

    19)使用模板来减少重复的try-catch代码

    1. class DBUtil{
    2. public static void closeConnection(Connection conn){
    3. try{
    4. conn.close();
    5. } catch(Exception ex){
    6. //Log Exception - Cannot close connection
    7. }
    8. }
    9. }

    使用这个来减少每次都try一遍。

    20)文档中加上异常说明

    1. /**
    2. * This method does something extremely useful ...
    3. *
    4. * @param input
    5. * @throws MyBusinessException if ... happens
    6. */
    7. public void doSomething(String input) throws MyBusinessException { ... }

    前人种树后人乘凉,你可能也可以乘凉。

  • 相关阅读:
    Android渐变GradientDrawable叠加组合环ring
    72.spring boot讨论群【从零开始学Spring Boot】
    71.mybatis 如何获取插入的id【从零开始学Spring Boot】
    Android GradientDrawable的XML实现
    服务器端架构及实战 — C#分享
    70.打印所有Spring boot载入的bean【从零开始学Spring Boot】
    69. JPA实体Bean的生命周期【从零开始学Spring Boot】
    Android版网易云音乐唱片机唱片磁盘旋转及唱片机机械臂动画关键代码实现思路
    服务器架构及实战(架构篇)- PHP建站
    创建MyOffice项目
  • 原文地址:https://www.cnblogs.com/wxdlut/p/9102933.html
Copyright © 2020-2023  润新知