如何使用 try-with-resources代替try-catch-finally
1.适用范围(资源的定义): 任何实现 java.lang.AutoCloseable
或者 java.io.Closeable
的对象
2.关闭资源和 finally 块的执行顺序: 在 try-with-resources
语句中,任何 catch 或 finally 块在声明的资源关闭后运行
《Effective Java》中明确指出:
面对必须要关闭的资源,我们总是应该优先使用 try-with-resources
而不是try-finally
。随之产生的代码更简短,更清晰,产生的异常对我们也更有用。try-with-resources
语句让我们更容易编写必须要关闭的资源的代码,若采用try-finally
则几乎做不到这点。
Java 中类似于InputStream
、OutputStream
、Scanner
、PrintWriter
等的资源都需要我们调用close()
方法来手动关闭,一般情况下我们都是通过try-catch-finally
语句来实现这个需求,如下:
//读取文本文件的内容
Scanner scanner = null;
try {
scanner = new Scanner(new File("D://read.txt"));
while (scanner.hasNext()) {
System.out.println(scanner.nextLine());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (scanner != null) {
scanner.close();
}
}
使用 Java 7 之后的 try-with-resources
语句改造上面的代码:
try (Scanner scanner = new Scanner(new File("test.txt"))) {
while (scanner.hasNext()) {
System.out.println(scanner.nextLine());
}
} catch (FileNotFoundException fnfe) {
fnfe.printStackTrace();
}
当然多个资源需要关闭的时候,使用 try-with-resources
实现起来也非常简单,如果你还是用try-catch-finally
可能会带来很多问题。
通过使用分号分隔,可以在try-with-resources
块中声明多个资源。
try (BufferedInputStream bin = new BufferedInputStream(new FileInputStream(new File("test.txt")));
BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(new File("out.txt")))) {
int b;
while ((b = bin.read()) != -1) {
bout.write(b);
}
}
catch (IOException e) {
e.printStackTrace();
}
异常使用有哪些需要注意的地方
在异常使用中,有多个方面需要注意,以确保程序的健壮性、可读性和可维护性。以下是一些关键的注意事项:
一、异常处理的基本规范
-
提供精确、易读的错误原因信息:
- 在异常处理模块中,应确保提供的错误信息足够精确且易于理解,以便开发人员能够迅速定位并解决问题。
-
避免处理能够避免的异常:
- 对于可以通过改进代码逻辑或增加前置检查来避免的异常,应尽量避免在运行时处理。
-
限制异常抛出的类型:
- 一个方法不应抛出过多类型的异常,最好不超过三个,以减少调用者的处理负担。
-
避免在try及finally中使用return语句:
- 在try或finally块中使用return语句可能会导致异常信息丢失或程序流程混乱。
-
在finally中释放资源:
- 对于数据库连接、文件句柄等资源,应在finally块中释放,以防止资源泄露。
-
将try/catch置于循环之外:
- 尽量避免在循环内部使用try/catch块,因为这可能导致性能下降和代码可读性降低。
-
避免使用异常进行程序流程控制:
- 异常处理机制的效率低于条件分支,且跳转流程难以预测,因此应避免将异常用于程序流程控制。
二、异常捕获与处理的优化
-
细分异常处理:
- 应尽量捕获具体的异常类型,而不是直接捕获
Exception
或其父类。这有助于提供更准确的错误信息,并减少不必要的异常处理代码。
- 应尽量捕获具体的异常类型,而不是直接捕获
-
避免捕获unchecked Exception:
- 对于
RuntimeException
等unchecked异常,通常不应捕获,因为它们通常表示编程错误。应通过改进代码逻辑来避免这些异常的发生。
- 对于
-
合理处理checked Exception:
- 对于checked异常,应根据实际情况进行处理。如果异常可以被修复,则进行修复;如果无法处理,则重新抛出异常,让调用者处理。
-
使用try-with-resources简化资源管理:
- 对于实现了
AutoCloseable
接口的资源,可以使用try-with-resources语句来自动管理资源,从而简化代码并提高安全性。
- 对于实现了
三、日志记录与异常信息
-
记录完整的异常信息:
- 在日志中记录完整的异常信息,包括异常类型、错误消息和堆栈跟踪。这有助于开发人员快速定位问题原因。
-
避免在日志中使用异常消息替代完整信息:
- 异常消息可能不包含有用的信息,因此应尽量避免仅记录异常消息而忽略其他重要的上下文信息。
-
使用合适的日志级别:
- 根据异常的严重程度选择合适的日志级别(如error、warn等),以便更好地监控和排查问题。
-
避免重复记录日志:
- 对于同一个异常,应避免在多个地方重复记录日志,以减少日志的冗余和噪声。
四、其他注意事项
-
不要混合使用异常机制和返回值:
- 应避免同时使用异常机制和返回值来进行异常处理,因为这会使程序的异常处理部分变得复杂且难以理解。
-
保持异常处理的简洁性:
- 异常处理代码应尽可能简洁明了,避免引入不必要的复杂性。
-
遵循最佳实践:
- 学习和遵循业界关于异常处理的最佳实践,以提高代码的质量和可维护性。