1. 程式人生 > >Java基礎學習總結(136)——有關流資源關閉(try-with-resources與AutoCloseable)的使用總結

Java基礎學習總結(136)——有關流資源關閉(try-with-resources與AutoCloseable)的使用總結

前言

做Java開發的都知道,每個資源的開啟都需要對應的關閉操作,不然就會使資源一直佔用而造成資源浪費,從而降低系統性能。關於資源的關閉操作,從JDK7-JDK9有了不少的提升及簡化。我們來看看資源處理的變化。

一、JDK6

在JDK6及之前,每個資源都需要我們手動寫程式碼關閉,如:

FileInputStream fis = null;
byte[] buffer = new byte[1024];
try {
    fis = new FileInputStream(new File("E:\Java檔案.txt")); 
    while (fis.read(buffer) > 0) {
        System.out.println(new String(buffer)); 
    }
} catch(Exception e) {
    e.printStackTrace();
}finally{ 
    if(fis != null) { 
    try {
        fis.close(); 
    } catch (IOException e) {
        e.printStackTrace(); 
    } 
}

資源開啟使用完後,必須在finally塊中進行手動關閉!尤其是資料庫的連線物件Connection。我們有的同事,做一個查詢功能點,打開了連線查詢完後沒有手動關閉,最後造成連線池超出最大連線數而使系統功能堵塞。

二、JDK7

JDK7釋出後,添加了新特性:try-with-resources語句。所有需要關閉的資源只要實現了 java.lang.AutoCloseable(java.io.Closeable就實現了這個介面)介面就會在程式結束後自動關閉。AutoCloseable介面在JDK7中引入的,看原始碼,其中只有一個方法:

void close() throws Exception;

如上面的讀取檔案的流程式用JDK7來寫:

byte[] buffer = new[1024];
try (FileInputStream fis = new FileInputStream(new File("E:\Java檔案.txt"))) {
    while (fis.read(buffer) > 0) {
        System.out.println(new String(buffer));
    }
} catch (Exception e) {
    e.printStackTrace();
}

所有的資源在 try()裡面定義,並去掉了finally模組。最後程式會自動關閉資源(本例中的檔案流fis物件)。下面我們來寫一個自定義的流來看看是否自動關閉了。簡單示例。

定義一個自定義輸入輸出流

class MyInputStream implements AutoCloseable {
    
    void read(String content) {
        System.out.println("Read content " + content); 
    }
 
    @Override 
    public void close() throws Exception {
        System.out.println("Input stream is closed.");
    }
}

class MyOutputStream implements AutoCloseable {
    
    void write(String content) {
        System.out.println("write content " + content);
    }
 
    @Override 
    public void close() throws Exception {
        System.out.println("out stream is closed.");
    }
}

單個資源自動關閉

try (MyInputStream mis = new MyInputStream()) {
    mis.read("MyInputStream");
} catch (Exception e) {
    e.printStackTrace();
}

輸出:

read content MyInputStream
Input stream is closed.

多個資源自動關閉

try()裡面可以定義多個資源,它們的關閉順序是最後在 try()定義的資源先關閉。

try (MyInputStream mis = new MyInputStream(); MyOutputStream mos = new MyOutputStream()) {
    mis.read("ND");
    mos.write("ND");
} catch (Exception e) {
    e.printStackTrace();
}

輸出:

Read content NDwrite content NDout stream is closed.Input stream is closed.

三、JDK9

JDK9釋出後,又簡化了try-with-resources語句的用法。try()裡面可以是一個變數,但必須是final的或者等同final才行。如下面的mis,mos定義成區域性變數可以不用final,區域性變數可以等同於final,但定義成成員變數就必須是用final修飾的,不然會編譯錯誤。也就是說,資源不要在try後的括號內建立,而是提前建立,滿足上面的條件,在try後的括號內直接使用變數即可。

MyInputStream mis = new MyInputStream();
MyOutputStream mos = new MyOutputStream();
try (mis; mos) {
    mis.read("1.9");
    mos.write("1.9");
} catch (Exception e) {
    e.printStackTrace();
}

輸出:

Read content 1.9write content 1.9out stream is closed.Input stream is closed.

再來看個示意性例子:

Connection dbCon = DriverManager.getConnection("url", "user", "password");
try (dbCon; ResultSet rs = dbCon.createStatement().executeQuery("select * from emp")) { 
    while (rs.next()) {
        System.out.println("In loadDataFromDB() =====>>>>>>>>>>>> " + rs.getString(1)); 
    }
}catch (SQLException e) { 
    System.out.println("Exception occurs while reading the data from DB ->" + e.getMessage());
}

dbCon和rs都能被自動關閉——檢視原始碼可知,Connection和ResultSet等都實現AutoCloseable介面。

這樣可以在實際編碼時,寫出更簡潔的程式碼,省略很多莫辦事程式碼(finally中關閉各種資料庫相關的物件以及異常處理等)JDK9的這一點變化還是非常有用的,可使開發人員更關注資料的處理方面。