在 Java 中解决异样并不是一个简略的事件。不仅仅初学者很难了解,即便一些有教训的开发者也须要破费很多工夫来思考如何解决异样,包含须要解决哪些异样,怎么解决等等。
这也是绝大多数开发团队都会制订一些规定来标准对异样解决的起因。而团队之间的这些标准往往是截然不同的。
本文给出几个被很多团队应用的异样解决最佳实际。
实际
在 Finally 块中清理资源或者应用 try-with-resource 语句
当应用相似 InputStream 这种须要应用后敞开的资源时,一个常见的谬误就是在 try 块的最初敞开资源。
public void doNotCloseResourceInTry() {
FileInputStream inputStream = null;
try {File file = new File("./tmp.txt");
inputStream = new FileInputStream(file);
// use the inputStream to read a file
// do NOT do this
inputStream.close();} catch (FileNotFoundException e) {log.error(e);
} catch (IOException e) {log.error(e);
}
}
上述代码在没有任何 exception 的时候运行是没有问题的。然而当 try 块中的语句抛出异样或者本人实现的代码抛出异样,那么就不会执行最初的敞开语句,从而资源也无奈开释。
正当的做法则是将所有清理的代码都放到 finally 块中或者应用 try-with-resource 语句.
另外,关注公众号「Java 编程大本营」,在后盾回复:面试,能够获取我整顿的 2021 年最新 java 系列面试题和答案,十分齐全。
public void closeResourceInFinally() {
FileInputStream inputStream = null;
try {File file = new File("./tmp.txt");
inputStream = new FileInputStream(file);
// use the inputStream to read a file
} catch (FileNotFoundException e) {log.error(e);
} finally {if (inputStream != null) {
try {inputStream.close();
} catch (IOException e) {log.error(e);
}
}
}
}
public void automaticallyCloseResource() {File file = new File("./tmp.txt");
try (FileInputStream inputStream = new FileInputStream(file);) {// use the inputStream to read a file} catch (FileNotFoundException e) {log.error(e);
} catch (IOException e) {log.error(e);
}
}
指定具体的异样
尽可能的应用最具体的异样来申明办法,这样能力使得代码更容易了解。
public void doNotDoThis() throws Exception {...}
public void doThis() throws NumberFormatException {...}
如上,NumberFormatException 字面上即能够看出是数字格式化谬误。
对异样进行文档阐明
当在办法上申明抛出异样时,也须要进行文档阐明。和后面的一点一样,都是为了给调用者提供尽可能多的信息,从而能够更好地防止 / 解决异样。在 Javadoc 中退出 throws 申明,并且形容抛出异样的场景。
/**
* This method does something extremely useful ...
* @param input
* @throws MyBusinessException if ... happens
*/
public void doSomething(String input) throws MyBusinessException {...}
抛出异样的时候蕴含形容信息
在抛出异样时,须要尽可能准确地形容问题和相干信息,这样无论是打印到日志中还是监控工具中,都可能更容易被人浏览,从而能够更好地定位具体错误信息、谬误的重大水平等。
但这里并不是说要对错误信息简明扼要,因为原本 Exception 的类名就可能反映谬误的起因,因而只须要用一到两句话形容即可。
try {new Long("xyz");
} catch (NumberFormatException e) {log.error(e);
} catch(Exception e){log.error("describle exption's information... ",e)
}
NumberFormatException 即通知了这个异样是格式化谬误,异样的额定信息只须要提供这个谬误字符串即可。当异样的名称不够显著的时候,则须要提供尽可能具体的错误信息。
首先捕捉最具体的异样
当初很多 IDE 都能智能提醒这个最佳实际,当你试图首先捕捉最抽象的异样时,会提醒不能达到的代码。当有多个 catch 块中,依照捕捉程序只有第一个匹配到的 catch 块能力执行。因而,如果先捕捉 IllegalArgumentException,那么则无奈运行到对 NumberFormatException 的捕捉。
public void catchMostSpecificExceptionFirst() {
try {doSomething("A message");
} catch (NumberFormatException e) {log.error(e);
} catch (IllegalArgumentException e) {log.error(e)
}
}
不要捕捉 Throwable
Throwable 是所有异样和谬误的父类。你能够在 catch 语句中捕捉,然而永远不要这么做。如果 catch 了 throwable,那么不仅仅会捕捉所有 exception,还会捕捉 error。而 error 是表明无奈复原的 jvm 谬误。因而除非相对必定可能解决或者被要求解决 error,不要捕捉 throwable。
public void doNotCatchThrowable() {
try {// do something} catch (Throwable t) {// don't do this!}
}
不要疏忽异样
很多时候,开发者很有自信不会抛出异样,因而写了一个 catch 块,然而没有做任何解决或者记录日志。
public void doNotIgnoreExceptions() {
try {// do something} catch (NumberFormatException e) {// this will never happen}
}
但事实是常常会呈现无奈意料的异样或者无奈确定这里的代码将来是不是会改变 (删除了阻止异样抛出的代码),而此时因为异样被捕捉,使得无奈拿到足够的错误信息来定位问题。正当的做法是至多要记录异样的信息。
public void logAnException() {
try {// do something} catch (NumberFormatException e) {log.error("This should never happen:" + e);
}
}
不要记录并抛出异样
能够发现很多代码甚至类库中都会有捕捉异样、记录日志并再次抛出的逻辑。如下:
try {new Long("xyz");
} catch (NumberFormatException e) {log.error(e);
throw e;
}
这个解决逻辑看着是正当的。但这常常会给同一个异样输入多条日志。如下:
17:44:28,945 ERROR TestExceptionHandling:65 - java.lang.NumberFormatException: For input string: "xyz"
Exception in thread "main" java.lang.NumberFormatException: For input string: "xyz"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Long.parseLong(Long.java:589)
at java.lang.Long.(Long.java:965)
at com.stackify.example.TestExceptionHandling.logAndThrowException(TestExceptionHandling.java:63)
at com.stackify.example.TestExceptionHandling.main(TestExceptionHandling.java:58)
如上所示,前面的日志也没有附加更有用的信息。如果想要提供更加有用的信息,
那么能够将异样包装为自定义异样。因而,仅仅当想要解决异样时才去捕捉,否则只须要在办法签名中申明让调用者去解决。
包装异样时不要摈弃原始的异样
捕捉规范异样并包装为自定义异样是一个很常见的做法。这样能够增加更为具体的异样信息并可能做针对的异样解决。
须要留神的是,包装异样时,肯定要把原始的异样设置为 cause(Exception 有构造方法能够传入 cause)。否则,失落了原始的异样信息会让谬误的剖析变得艰难。
public void wrapException(String input) throws MyBusinessException {
try {// do something} catch (NumberFormatException e) {throw new MyBusinessException("A message that describes the error.", e);
}
}
总结
综上可知,当抛出或者捕捉异样时,有很多不一样的货色须要思考。其中的许多点都是为了晋升代码的可浏览性或者 api 的可用性。
异样不仅仅是一个谬误管制机制,也是一个沟通媒介,因而与你的协作者探讨这些最佳实际并制订一些标准可能让每个人都了解相干的通用概念并且可能依照同样的形式应用它们。