我的java异常最佳实践

这是我的最佳实践,为什么呢?先看代码

class ResourceHolder implements AutoCloseable {
  private String name;
  public ResourceHolder(String name) {
    this.name = name;
    System.out.println(name + " get resource");
  }
  public void useResource() throws Exception {
    throw new Exception(name + " exception in useResource");
  }
  public void close() {
    System.out.println(name + " release resource");
    throw new RuntimeException(name + " exception in close");
  }
}

public class TestException {
  public static void testTryBlock() throws Exception {
    ResourceHolder resourceHolder = null;
    try {
      resourceHolder = new ResourceHolder("try-block");
      resourceHolder.useResource();
    } catch (Exception e) {
      throw e;
    } finally {
      if (resourceHolder != null) resourceHolder.close();
    }
  }

  public static void testTryWithResource() throws Exception {
    try (
      ResourceHolder resourceHolder = new ResourceHolder("try-with-resource")
      ) {
      resourceHolder.useResource();
    } catch (Exception e) {
      throw e;
    }
  }

  public static void main(String[] args) {
    try {
      testTryBlock();
    } catch (Exception e) {
      System.out.println(e.toString());
    }
    try {
      testTryWithResource();
    } catch (Exception e) {
      System.out.println(e.toString());
    }
  }
}

这是输出

try-block get resource
try-block release resource
java.lang.RuntimeException: try-block exception in close
try-with-resource get resource
try-with-resource release resource
java.lang.Exception: try-with-resource exception in useResource

1 Checked 还是 Unchecked ?

java的异常分 CheckedUnchecked ,可以简单的认为所有继承自 RuntimeException 的异常都是 Unchecked 的,这类异常可以try-catch,也可以不try-catch,并且不需要出现在函数的签名中,例如 ResourceHolder::close ,剩下的都是 Checked ,必须try-catch或者出现在函数签名中,异常向上抛。除此外,两者没有任何不同。

这点和c++不同, c++的异常都是 Unchecked 的。 Checked 的好处是强制,无法忘记异常的存在,当然我可以选择try-catch之后,选择忽略,强制的作用就失效了。 Unchecked 的好处是,给你选择,给你自由,可以处理,也可以不处理。

问题在于,有多少时候,catch住异常后,异常是可以恢复的,如果异常不可恢复,那 Checked 异常就毫无意义。有语法错误的SQL,超时的连接,等等,都是错了就错了,除非人工干预,否则catch住异常没有任何用处,最后还是把错误报告给最终用户。这里有一个例外,那就是支线流程的异常。所谓支线流程指,仅用于内部流程的功能,并不包含在API对外承诺的功能中,也不影响对外承诺的功能。例如:用户申请权限的API,用户获得授权是API对外承诺的功能,调接口通知审计系统是支线流程。支线流程的异常需要捕获,通过记录日志,报警等方式后期完成支线流程,最大限度的保证API的可用性。

Spring框架干了一件事情,它把几乎所有 Checked 的异常都转成 Unchecked 的了。这大大减少了开发中的冗余代码。

我觉得如果是开发基础代码,所有的异常最好都是 Unchecked ,让用户自由选择,毕竟强制处理异常的作用是微弱的。

ResourceHolder::close 抛出的异常如果是 Checked 的,那么finally子句还需要增加try-catch。

2 异常时如何释放资源的问题

分配资源释放资源 是成对儿的操作,如果两个操作中间有异常发生(尤其是 Unchecked 异常),需要处理异常,并在异常处理子句 释放资源 ,这个过程需要些大量 相似 代码,如 testTryBlock 所示。在java7中新增的 try-with-resources 可以简化该问题,如 testTryWithResource 所示,出现在 try() 中的对象,必须实现 AutoCloseable 接口,当 try{} 结束时(无论有没有异常),自动调用其 close 方法,可以把 释放资源 的操作放到 close 函数中。

所有涉及到 释放资源 的类或操作都应该用这种方法。

3 被抑制的异常

因为finally子句总是执行的,finally子句如果抛出异常,外层看到的是finally子句的异常,而不是catch子句的,这点和 try() 不一样,这点从main函数中捕获的两个异常不一样可以看出。

Author: root

Created: 2017-03-30 四 10:43

Validate