Java中请优先使用try-with-resources而非try-finally
Java中请优先使用try-with-resources而非try-finally
Java库包含了很多需要手工调用close方法来关闭的资源。比如说InputStream、OutputStream及java.sql.Connection。关闭资源常常会被客户端所忽视,这会导致可怕的性能问题。虽然很多资源使用了终结器来作为安全网,不过终结器却并不那么尽如人意。
从历史上来看,try-finally语句是确保资源会被恰当关闭的最佳方式,即便在遇到异常或是返回语句时亦如此:
// try-finally - No longer the best way to close resources!
static String firstLineOfFile(String path) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
try {
return br.readLine();
} finally {
br.close();
}
}
看起来还不错,不过当添加了第二个资源时情况就变得有些糟糕了:
// try-finally is ugly when used with more than one resource!
static void copy(String src, String dst) throws IOException {
InputStream in = new FileInputStream(src);
try {
OutputStream out = new FileOutputStream(dst);
try {
byte[] buf = new byte[BUFFER_SIZE];
int n;
while ((n = in.read(buf)) >= 0)
out.write(buf, 0, n);
} finally {
out.close();
}
} finally {
in.close();
}
}
虽然难以相信,但即便是优秀的程序员很多时候也会编写这样的代码。对于初学者来说,我在Java Puzzlers [Bloch05]的第88页指出了问题,但多年来没人注意到。事实上,2007年,在Java库中对close方法的使用有2/3是错误的。
甚至使用try-finally语句关闭资源的正确代码(如前面两个代码示例所示)也存在一些小问题。try块与finally块中的代码都可以抛出异常。比如说,在firstLineOfFile方法中,由于底层物理设备的失败,调用readLine可能会抛出异常,调用close也会因同样的原因而失败。在这些情况下,第二个异常完全会掩盖掉第一个。在异常堆栈中没有对第一个异常的记录信息,这会极大增加真实系统的调试复杂度——通常来说,第一个异常才是问题诊断的关键所在。虽然可以通过编写代码来压制第二个异常,从而保留第一个异常信息,但没人会这么做,因为太麻烦了。
所有这些问题都随着Java 7引入try-with-resources语句得到了解决[JLS, 14.20.3]。要想使用该结构,资源必须要实现AutoCloseable接口,该接口包含了唯一一个返回void类型的close方法。Java库与第三方库中的很多类和接口现在都实现或是继承了AutoCloseable。如果编写一个用于表示资源的类,它必须要关闭,那么这个类就应该实现AutoCloseable。
如下代码使用try-with-resources改写了上面第一个示例:
// try-with-resources - the the best way to close resources!
static String firstLineOfFile(String path) throws IOException {
try (BufferedReader br = new BufferedReader(
new FileReader(path))) {
return br.readLine();
}
}
如下代码使用try-with-resources改写了上面第二个示例:
// try-with-resources on multiple resources - short and sweet
static void copy(String src, String dst) throws IOException {
try (
InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dst))
{
byte[] buf = new byte[BUFFER_SIZE];
int n;
while ((n = in.read(buf)) >= 0)
out.write(buf, 0, n);
}
}
try-with-resources版本不仅代码更简洁,可读性比原来的要好,而且提供了更好的问题诊断能力。考虑firstLineOfFile方法。如果readLine调用与(不可见的)close方法都抛出了异常,那么后者的异常就会被前者压制住。事实上,多个异常都会被压制住,从而保留你实际想看的那个异常。这些被压制的异常并不是被丢弃掉;他们会在堆栈中打印出来,并且有相应的符号表示他们被压制了。你还可以通过编程的方式通过getSuppressed方法来访问这些异常,该方法是在Java 7中被添加到Throwable中的。
你可以将catch从句放到try-with-resources语句上,就像常规的try-finally语句一样。这样就可以在处理异常的同时又不会在另一个嵌套层次上搞乱代码了。举个例子,下面是不抛出异常的firstLineOfFile方法版本,不过如果无法打开文件或是无法读取文件,那么它会接收一个默认值来返回:
// try-with-resources with a catch clause
static String firstLineOfFile(String path, String defaultVal) {
try (BufferedReader br = new BufferedReader(
new FileReader(path))) {
return br.readLine();
} catch (IOException e) {
return defaultVal;
}
}
很显然,在处理需要关闭的资源时,请优先使用try-with-resources而非try-finally。结果代码更短、也更清晰,它所生成的异常也更加有用。try-with-resources语句使得编写使用了必须要关闭的资源的代码更加轻松,而这是try-finally所做不到的。
Java中请优先使用try-with-resources而非try-finally的更多相关文章
- java中请给一个Abstract类实现接口的实例!
2.Abstract类实现接口 马克-to-win:如果实现某接口的类是abstract类,则它可以不实现该接口所有的方法.但其非abstract的子类中必须拥有所有抽象方法的实在的方法体:(当然它a ...
- java中请给出一个return this的例子。
[新手可忽略不影响继续学习]下面例子中setYear中的return this;返回了一个指向对象的指针,this.setMonth(8).setDay(20);是合法的,如果像原来的例子一样什么都不 ...
- java中请给出一个抽象类,可以继承实体类的例子
例1.7.2(抽象类可以继承实体类)- class VehMark_to_win { void steer() { System.out.println("Turn st ...
- java中请给出例子程序:找出两个数的最大公约数和最小公倍数
9.2 找出12和8的最大公约数和最小公倍数. public class Test { public static void main(String[] args) { ...
- java中请给出例子程序:找出n到m之间的质数。
9.1 找出100到200之间的质数. public class Test { public static void main(String[] args){ for (in ...
- 关于java中为什么尽量把受检异常转化为非受检异常
首先理解一下受检异常与非受检异常: 异常表示程序运行过程中可能出现的非正常状态,运行时异常表示虚拟机操作中可能遇到的异常,是一种常见的运行错误,只要程序设计的没有问题通常就不会发生.受检异常与程序的上 ...
- Java中静态变量、静态代码块、非静态代码块以及静态方法的加载顺序
在研究单例设计模式的时候,用到了静态变量和静态方法的内容,出于兴趣,这里简单了解一下这四个模块在类初始化的时候的加载顺序. 经过研究发现,它们的加载顺序为: 1.非静态代码块 2.静态变量或者静态代码 ...
- Java中的资源文件加载方式
文件加载方式有两种: 使用文件系统自带的路径机制,一个应用程序只能有一个当前目录,但可以有Path变量来访问多个目录 使用ClassPath路径机制,类路径跟Path全局变量一样也是有多个值 在Jav ...
- c++中继承和java中继承的对比
java中: class Parent{ public void test(int a){ System.out.println("Parent:" + a); System.ou ...
随机推荐
- php--判断是否是手机端
function is_mobile_request(){ $_SERVER['ALL_HTTP'] = isset($_SERVER['ALL_HTTP']) ? $_SERVER['ALL_HTT ...
- 仿射密码Python实现
算法分析 仿射密码结合了移位密码和乘数密码的特点,是移位密码和乘数密码的组合. 仿射密码的加密算法就是一个线性变化,即对明文字符x,对应的密文字符为y=ax+b(mod26)其中,a, b属于Z26且 ...
- IDEA如何添加库lib(java)
1.点击file 2.点击 3.点击 4.点击右面+号 5.找到你的类库添加即可
- Numpy入门(一):Numpy的安装和创建
在数据分析和机器学习中,大量的使用科学计算,Numpy提供了大型矩阵计算的方式,而这些是python标准库中所缺少的.Numpy也是许多优秀的第三方库的基础,依赖于Numpy的库非常多,后续会慢慢的进 ...
- 如何进行Web服务的性能测试
涉及到web服务的功能在不断的增加,对于我们测试来说,我们不仅要保证服务端功能的正确性,也要验证服务端程序的性能是否符合要求.那么性能测试都要做些什么呢?我们该怎样进行性能测试呢? 性能测试 ...
- CPU网卡亲和绑定
#!/bin/bash # # Copyright (c) , Intel Corporation # # Redistribution and use in source and binary fo ...
- yum配置与使用
yum的配置一般有两种方式,一种是直接配置/etc目录下的yum.conf文件,另外一种是在/etc/yum.repos.d目录下增加.repo文件. 一.yum的配置文件 [main] cached ...
- 通过pl/sql连接远程Oracle数据库
通过PL/SQL连接远程数据库,简单的方式就是安装Oracle客户端,还有一种方式就是不安装客户端,但是需要自己创建必要的配置文件,下面主要对安装客户端的过程简单做一下记录. 网上一个不安装客户端的教 ...
- 【快速上手】Git的使用教程
创建Git仓库 git init 查看当前仓库情况 git status 添加修改 git add (file) or git add . 查看未提交的修改 git diff 撤销提交操作 git r ...
- 深入理解JavaScript的函数作用域
什么是作用域 ? 作用域:一个变量可以生效的范围. 变量不是在所有地方都可以使用的,而这个变量的使用范围就是我们要说的作用域. 注意:在JavaScript中,划分作用域也是用大括号划分的, 但是在 ...