前言
Java 中的异样解决是个不简略的话题。初学者很难了解,即便是经验丰富的开发人员也能够破费数小时来探讨如何以及应该抛出或解决哪些异样。
这就是为什么大多数开发团队都有一套对于如何应用它们的规定的起因。而且,如果您是团队老手,那么您可能会感到诧异,这些规定与您以前应用的规定有何不同。
尽管如此,大多数团队还是采纳了几种最佳实际。以下是 9 个最重要的信息,它们能够帮忙您入门或改善异样解决。
一、在 finally 块中清理资源或应用 Try-With-resource 语句
常常产生的是,您在 try 块中应用了一个资源,例如 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);
}
}
问题在于,只有不引发异样,此办法仿佛就能够很好地工作。try 块中的所有语句将被执行,并且资源将被敞开。
然而您增加 try 块是有起因的。您调用一个或多个可能引发异样的办法,或者您可能本人引发异样。这意味着您可能未达到 try 块的开端。因而,您将不会敞开资源。
因而,您应该将所有清理代码放入 finally 块中,或应用 try-with-resource 语句。
应用 finally 模块
与 try 块的最初几行相同,finally 块始终执行。在胜利执行 try 块之后或在 catch 块中解决了异样之后,就会产生这种状况。因而,能够确保革除所有关上的资源。
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);
}
}
}
}
Java 7 的 Try-With-Resource 语句另一个抉择是 try-with-resource 语句,我在 Java 异样解决简介中对其进行了具体阐明。
如果您的资源实现了 AutoCloseable 接口,则能够应用它。那就是大多数 Java 规范资源所做的。当您在 try 子句中关上资源时,将在 try 块执行或解决异样后主动敞开资源。
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);
}
}
二、指定具体的异样
抛出的异样越具体越好。始终牢记,不晓得您的代码,或者可能几个月后不晓得您的代码的共事,须要调用您的办法并解决该异样。
因而,请确保为他们提供尽可能多的信息。这使您的 API 更易于了解。后果,您的办法的调用者将可能更好地解决该异样,或者通过额定的 check 防止该异样。
因而,请始终尝试查找最适宜您的异样事件的类,例如,抛出 NumberFormatException 而不是.
IllegalArgumentException。并防止引发不确定的 Exception。public void doNotDoThis() throws Exception {...}
public void doThis() throws NumberFormatException {...}
整顿了一下 2021 年的 Java 工程师经典面试真题,共 485 页大略 850 道含答案的面试题 PDF,蕴含了 Java、MyBatis、ZooKeeper、Dubbo、Elasticsearch、Memcached、Redis、MySQL、Spring、Spring Boot、Spring Cloud、RabbitMQ、Kafka、Linux 等简直所有技术栈,每个技术栈都有不少于 50 道经典面试真题,不敢说刷完包你进大厂,但有针对性的刷让你面对面试官的时候多几分底气还是没问题的。
三、对特定异样进行归档
每当在办法签名中指定异样时,也应在 Javadoc 中对其进行记录。这与以前的最佳实际具备雷同的指标:为呼叫者提供尽可能多的信息,以便他能够防止或解决异样。
因而,请确保在 Javadoc 中增加一个 @throws 申明,并形容可能导致异样的状况。
/**
* This method does something extremely useful ...
*
* @param input
* @throws MyBusinessException if ... happens
*/
public void doSomething(String input) throws MyBusinessException {...}
四、抛出异样的时候蕴含形容信息
最佳实际背地的想法与前两个相似。然而这一次,您没有将信息提供给您的办法的调用者。每个必须理解该日志文件或您的监督工具中报告该异样时产生的状况的人都能够浏览该异样的音讯。
因而,它应尽可能精确地形容问题,并提供最相干的信息以理解异样事件。
不要误会我的意思;您不应该写一段文字。然而您应该用 1 - 2 个简短的句子来阐明出现异常的起因。这能够帮忙您的经营团队理解问题的严重性,还能够使您更轻松地剖析任何服务事件。
如果抛出特定的异样,则其类名很可能曾经形容了谬误的品种。因而,您无需提供很多其余信息。一个很好的例子是 NumberFormatException。当您以谬误的格局提供 String 时,它将由类 java.lang.Long 的构造函数引发。
try {new Long("xyz");
} catch (NumberFormatException e) {log.error(e);
}
NumberFormatException 类的名称曾经告诉您问题的类型。它的音讯仅须要提供引起问题的输出字符串。如果异样类的名称不那么具备表现力,则须要在音讯中提供所需的信息。
17:17:26,386 ERROR TestExceptionHandling:52 - java.lang.NumberFormatException: For input string: "xyz"
五、首先捕捉最具体的异样
大多数 IDE 都能够帮忙您获得最佳实际。当您尝试首先捕捉不太具体的异样时,它们报告无法访问的代码块。
问题在于仅执行与异样匹配的第一个 catch 块。因而,如果您首先捕捉 IllegalArgumentException,那么您将永远不会达到应该解决更具体的 NumberFormatException 的 catch 块,因为它是 IllegalArgumentException 的子类。
始终首先捕捉最具体的异样类,并将不那么具体的捕捉块增加到列表的开端。
您能够在以下代码片段中看到这样的 try-catch 语句的示例。第一个 catch 块解决所有 NumberFormatException,第二个所有 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。JVM 抛出重大的谬误问题,这些问题不会由利用程序处理。
比如说:OutOfMemoryError 或 StackOverflowError。
两者都是由应用程序无法控制的状况引起的,无奈解决。
因而,最好不要捕捉 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)
在 com.stackify.example.TestExceptionHandling.main(TestExceptionHandling.java:58)
其余音讯也不会增加任何信息。如最佳做法 4 中所述,异样音讯应形容异样事件。堆栈跟踪会告诉您在哪个类,办法和行中引发了异样。
如果须要增加其余信息,则应捕捉异样并将其包装在自定义异样中。然而请确保遵循最佳实际 9。
public void wrapException(String input) throws MyBusinessException {
try {// do something} catch (NumberFormatException e) {throw new MyBusinessException("A message that describes the error.", e);
}
}
因而,仅在要解决它时才捕捉异样。否则,请在办法签名中指定它,而后让调用者来解决它。
九、在不耗费异样的状况下包装异样
有时最好捕捉一个规范异样并将其包装到自定义异样中。这种例外的典型示例是特定于应用程序或框架的业务例外。这使您能够增加其余信息,还能够对异样类施行非凡解决。
执行此操作时,请确保将原始异样设置为起因。该异样类提供了承受一个特定的构造方法的 Throwable 作为参数。否则,您将失落堆栈跟踪和原始异样的音讯,这将使剖析导致您的异样的异样事件变得艰难。
public void wrapException(String input) throws MyBusinessException {
try {// do something} catch (NumberFormatException e) {throw new MyBusinessException("A message that describes the error.", e);
}
}
总结
综上所述,抛出或捕捉异样时,您应该思考很多不同的事件。他们中大多数人的指标是进步代码的可读性或 API 的可用性。
异样通常是同时存在的错误处理机制和通信介质。因而,您应该确保与共事探讨要利用的最佳实际和规定,以便每个人都能了解个别概念并以雷同的形式应用它们。