一提到try-catch-finally代碼塊,我們都被這三者中代碼執(zhí)行順序于置、異常拋出處理結(jié)果以及資源關(guān)閉所折磨劣光,今天主要來講講finally中資源關(guān)閉的使用。
教科書都告訴我們称开,finally中建議只寫和資源關(guān)閉回收相關(guān)的代碼亩钟,不要寫和業(yè)務(wù)相關(guān)的邏輯,因?yàn)樗鼈儓?zhí)行效率很低,還面臨上述執(zhí)行順序不明的風(fēng)險(xiǎn)径荔。即便如此督禽,很多人還是不能在finally中正確合理地關(guān)閉資源。
一总处、錯(cuò)誤地關(guān)閉方式
我們創(chuàng)建一個(gè)maven工程狈惫,然后在src/main/resources目錄下新建一個(gè)test.properties,其中內(nèi)容如下:
name=zhang
age=26
gender=male
然后我們寫一個(gè)程序是想讀取這個(gè)配置文件中的所有配置項(xiàng)并打印出來鹦马。
先來看看如下的錯(cuò)誤關(guān)閉資源方式一:
public class StreamTest1 {
private static Logger log = LoggerFactory.getLogger(StreamTest1.class);
public static void main(String[] args) {
String fileName = "src/main/resources/test.properties";
Properties props = new Properties();
InputStream inputStream = null;
try {
inputStream = new FileInputStream(fileName);
props.load(inputStream);
// 一旦上面兩行代碼拋出異常胧谈,inputStream就不會(huì)被關(guān)閉
inputStream.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
for (Map.Entry<Object,Object> entry : props.entrySet()) {
log.info("key:{},value:{}", entry.getKey().toString(), entry.getValue().toString());
}
}
}
如上代碼的資源關(guān)閉寫在了try代碼塊中,一旦close方法調(diào)用之前就拋出異常荸频,那么關(guān)閉資源的代碼就永遠(yuǎn)不會(huì)得到執(zhí)行菱肖。
那我們把關(guān)閉資源的代碼放在finally中就一定正確了嗎?
try {
inputStream = new FileInputStream(fileName);
props.load(inputStream);
} catch (IOException e) {
log.error(e.getMessage(), e);
} finally {
try {
inputStream.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
看起來似乎沒有問題旭从。但是稳强,倘若在try中inputStream讀取文件拋出異常,那么inputStream仍然為null和悦,此時(shí)進(jìn)入finally代碼塊中退疫,執(zhí)行close的意義是什么呢?因此鸽素,我們需要在close之前判斷一下inputStream是否為空褒繁,只有不為空的時(shí)候才需要close。
二馍忽、 舊風(fēng)格的關(guān)閉方式
看過了上述錯(cuò)誤使用場(chǎng)景初橘,那么正確的使用關(guān)閉方式大致如下:
public class StreamTest3 {
private static Logger log = LoggerFactory.getLogger(StreamTest3.class);
public static void main(String[] args) {
String fileName = "src/main/resources/test.properties";
Properties props = new Properties();
InputStream inputStream = null;
try {
inputStream = new FileInputStream(fileName);
props.load(inputStream);
} catch (IOException e) {
log.error(e.getMessage(), e);
} finally {
// 如果inputStream本身為空就不需要再進(jìn)行關(guān)閉操作
if (null != inputStream) {
try {
inputStream.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
}
for (Map.Entry<Object,Object> entry : props.entrySet()) {
log.info("key:{},value:{}", entry.getKey().toString(), entry.getValue().toString());
}
}
}
However愕掏,始終感覺這么做太繁瑣了,這還只是一個(gè)資源的關(guān)閉,倘若有三個(gè)資源需要在finally中關(guān)閉场躯,豈不是徒增很多不優(yōu)雅的代碼诅妹?反過來想想郎汪,資源的關(guān)閉其實(shí)是沒啥邏輯的代碼森瘪,就像垃圾回收和資源分配一樣,為啥要讓程序員顯示地來操作呢吵血?
三谎替、使用try-with-resources了
自從Java7以后,程序員就被從資源的手動(dòng)關(guān)閉中解放了蹋辅,使用try-with-resources來改寫以上的例子:
public class StreamTest4 {
private static Logger log = LoggerFactory.getLogger(StreamTest4.class);
public static void main(String[] args) {
String fileName = "src/main/resources/test.properties";
Properties props = new Properties();
// 使用try-with-resource钱贯,自動(dòng)關(guān)閉需要close的資源
try (
InputStream inputStream = new FileInputStream(fileName);
) {
props.load(inputStream);
} catch (IOException e) {
log.error(e.getMessage(), e);
}
for (Map.Entry<Object, Object> entry : props.entrySet()) {
log.info("key:{},value:{}", entry.getKey().toString(), entry.getValue().toString());
}
}
}
如此,丑陋繁瑣的finally就不是必要的了侦另。注意秩命,我們說的是finally中的關(guān)閉資源代碼可以不必要了尉共,但是finally代碼塊如果有的話,還是可以執(zhí)行的弃锐。
四袄友、try-with-resources使用場(chǎng)景
當(dāng)然了,并不是所有的資源都是可以使用try-with-resources來進(jìn)行管理的霹菊,只有實(shí)現(xiàn)了java.lang.AutoCloseable
接口的剧蚣,并且實(shí)現(xiàn)了close方法的資源類才能如此使用。
除此之外旋廷,我們可以自定義資源類來支持使用try-with-resources鸠按。
public class MyAutoClosable implements AutoCloseable {
private Logger log = LoggerFactory.getLogger(MyAutoClosable.class);
@Override
public void close() {
log.info("MyAutoClosable closed!");
}
public void print() {
log.info("MyAutoClosable printed!");
}
}
public class StreamTest5 {
private static Logger log = LoggerFactory.getLogger(StreamTest5.class);
public static void main(String[] args) {
// 使用try-with-resource,自動(dòng)關(guān)閉需要close的資源
try (
MyAutoClosable mac = new MyAutoClosable();
) {
mac.print();
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
log.info("finally!");
}
}
}
執(zhí)行結(jié)果如下:
MyAutoClosable printed!
MyAutoClosable closed!
finally!