处理异常的9个方法
原文 https://www.cnblogs.com/kcher90/p/7468512.html
异常处理是一件很头疼的事情。
到底是需要【处理掉】还是需要【抛出去】。
1. 在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);
}
}
finally块中的语句一定会被执行。因此所有开启的资源都能够确保被关闭。
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);
}
}
}
}
JDK7的Try-With-Resource
资源实现了AutoCloseable接口的话,可以使用try-with-resource语句。
如果你在try-with-resource语句中声明了一个资源,
它将会在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);
}
}
2. 优先使用更明确的异常
你的代码会被别人使用,所以异常要明确,
public void doNotDoThis() throws Exception { ... }
public void doThis() throws NumberFormatException { ... }
3. 在文档里记录你的异常
1. 在方法签名处指定一个异常,应该同时将它记录到Javadoc里。
2. 给方法调用者提供尽可能多的信息,让他可以避免触发异常或者方便的他处理异常。
3. 确保在Javadoc里边添加@throws声明并且描述什么样的情况会导致异常。
/** * This method does something extremely useful ... * * @param input * @throws MyBusinessException if ... happens */
public void doSomething(String input) throws MyBusinessException { ... }
4. 将异常与它的描述信息一起抛出
1. 并不是给方法调用者提供信息,当异常信息被打印到日志文件时,需要查看的人能够获得足够多的信息
2. 提供足够多的信息来描述出现了什么问题,发生了什么异常。
//给java.lang.Long的构造函数提供一个错误格式的String类型参数时
//便会抛出NumberFormatException
try {
new Long("xyz");
} catch (NumberFormatException e) {
log.error(e);
}
17:17:26,386 ERROR TestExceptionHandling:52 - java.lang.NumberFormatException: For input string: “xyz”
5. 优先捕获更明确的异常
NumberFormatException是IllegalArgumentException的子类
先捕获NumberFormatException,再捕获IllegalArgumentException。
IDE的提示就是这样的
public void catchMostSpecificExceptionFirst() {
try {
doSomething("A message");
} catch (NumberFormatException e) {
log.error(e);
} catch (IllegalArgumentException e) {
log.error(e)
}
}
6. 不要捕获Throwable
* Throwable是所有异常(Exception)和错误(Error)的父类。
* 在catch从句中使用了Throwable,它将不仅捕获所有异常,它还将捕获所有错误,
错误是由JVM抛出的,用来表明不打算让应用来处理的严重错误。
* OutOfMemoryError和StackOverflowError便是典型的例子,
它们都是由于一些超出应用处理范围的情况导致的。
public void doNotCatchThrowable() {
try {
// do something
} catch (Throwable t) {
// don't do this!
}
}
7. 别忽略异常
* 忽略异常导致不完整的bug报告,非常难以分析。
* 你认为这里永远不会抛出异常,加了一个不处理且不打印日志的catch块,还包含一句著名
的注释---“this will never happen"
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);
}
}
8. 不要打印异常日志的同时将其抛出
导致一个异常被打印多次
它的调用者也打印
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)
* 异常消息应该描述异常事件,堆栈信息告诉你异常抛出的所在类、方法、行数。
* 如果你需要添加额外的信息,你应该将异常捕获并将其包在你的自定义异常中,
* 但要确保遵循第9条准则。
public void wrapException(String input) throws MyBusinessException {
try {
// do something
} catch (NumberFormatException e) {
throw new MyBusinessException("A message that describes the error.", e);
}
}
9. 包裹某个一场的同时不要丢弃它原本的信息
有时候我们需要捕获一个标准异常并用自定义异常包裹住它,
一个典型的例子便是比如某个应用或者框架的指明业务异常,
它允许你添加额外的信息,你也可以实现特别的异常处理方法。
当你这么做的时候,要确保将原本的异常作为原因设置到自定义异常里面,
Exception类提供指定的构造方法可以接收Throwable类的对象作为参数,
否则你将会丢失堆栈信息和原异常的消息,这将会令异常分析变得什么的困难。
public void wrapException(String input) throws MyBusinessException {
try {
// do something
} catch (NumberFormatException e) {
throw new MyBusinessException("A message that describes the error.", e);
}
}