Tôi có đang sử dụng Java 7 try-with-resources đúng cách không


87

Tôi đang mong đợi trình đọc bộ đệm và trình đọc tệp sẽ đóng và tài nguyên được giải phóng nếu trường hợp ngoại lệ được ném ra.

public static Object[] fromFile(String filePath) throws FileNotFoundException, IOException
{
    try (BufferedReader br = new BufferedReader(new FileReader(filePath)))
    {
        return read(br);
    } 
}

Tuy nhiên, có bắt buộc phải có catchđiều khoản để đóng thành công không?

BIÊN TẬP:

Về cơ bản, đoạn mã trên trong Java 7 tương đương với đoạn mã dưới đây cho Java 6:

public static Object[] fromFile(String filePath) throws FileNotFoundException, IOException
{

    BufferedReader br = null;

    try
    {
        br = new BufferedReader(new FileReader(filePath));

        return read(br);
    }
    catch (Exception ex)
    {
        throw ex;
    }
    finally
    {
        try
        {
            if (br != null) br.close();
        }
        catch(Exception ex)
        {
        }
    }

    return null;
}

Sau khi đọc lại câu hỏi của bạn, tôi không chắc mình đã hiểu rõ về nó. Bạn có thể vui lòng giải thích nó?
Maroun

Chào. Cheetah, tôi đang cố gắng hiểu vai trò của catchví dụ đầu tiên của bạn đối với Java 6. Tức là catch (Exception ex) { throw ex; }- nó chỉ ném lại ngoại lệ, nó không làm gì cả, nó có thể dễ dàng loại bỏ mà không bị tổn hại gì. Hay tôi đang thiếu cái gì đó?
Sasha

Câu trả lời:


103

Nó đúng và không có yêu cầu cho catchđiều khoản. Tài liệu Oracle java 7 cho biết tài nguyên sẽ bị đóng bất kể trường hợp ngoại lệ có thực sự được ném ra hay không.

Bạn chỉ nên sử dụng một catchmệnh đề nếu bạn muốn phản ứng khi có ngoại lệ. Các catchkhoản sẽ được thực hiện sau khi các nguồn tài nguyên được đóng lại.

Đây là một đoạn trích từ hướng dẫn của Oracle :

Ví dụ sau đây đọc dòng đầu tiên từ một tệp. Nó sử dụng một thể hiện của BufferedReader để đọc dữ liệu từ tệp. BufferedReader là một tài nguyên phải được đóng lại sau khi chương trình kết thúc với nó:

static String readFirstLineFromFile(String path) throws IOException {
    try (BufferedReader br =
                   new BufferedReader(new FileReader(path))) {
        return br.readLine();
    }
} // In this example, the resource declared in the try-with-resources statement is a BufferedReader.

... Vì cá thể BufferedReader được khai báo trong câu lệnh try-with-resource, nó sẽ bị đóng bất kể câu lệnh try hoàn thành bình thường hay đột ngột (do phương thức BufferedReader.readLine ném một IOException).

BIÊN TẬP

Về câu hỏi được chỉnh sửa mới:

Mã trong Java 6 thực thi khối catchvà sau đó finally. Điều này khiến các tài nguyên vẫn có thể được mở trong catchkhối.

Trong 7 cú pháp Java, tài nguyên được đóng trước khi các catchkhối, vì vậy nguồn lực đã được đóng cửa trong catchthực hiện khối. Điều này được ghi lại trong liên kết trên:

Trong câu lệnh try-with-resources, bất kỳ khối bắt hoặc cuối cùng nào được chạy sau khi các tài nguyên được khai báo đã bị đóng.


69

Việc sử dụng try-with-resources của bạn sẽ hoạt động tốt trong trường hợp cụ thể này, nhưng nhìn chung thì không hoàn toàn đúng. Bạn không nên xâu chuỗi nguồn lực như vậy vì có thể dẫn đến những bất ngờ khó chịu. Giả sử bạn có kích thước bộ đệm thay đổi:

public static Object[] fromFile(String filePath) throws FileNotFoundException, IOException
{
    int sz = /* get buffer size somehow */
    try (BufferedReader br = new BufferedReader(new FileReader(filePath), sz))
    {
        return read(br);
    } 
}

Giả sử có điều gì đó không ổn và cuối cùng bạn đã trở nên sztiêu cực. Trong trường hợp này, tài nguyên tệp của bạn (được tạo qua new FileReader(filePath)) sẽ KHÔNG bị đóng.

Để tránh vấn đề này, bạn nên chỉ định từng tài nguyên riêng biệt như sau:

public static Object[] fromFile(String filePath) throws FileNotFoundException, IOException
{
    int sz = /* get buffer size somehow */
    try (FileReader file = new FileReader(filePath);
         BufferedReader br = new BufferedReader(file, sz))
    {
        return read(br);
    } 
}

Trong trường hợp này, ngay cả khi khởi tạo brkhông thành công filevẫn bị đóng. Bạn có thể tìm thêm thông tin chi tiết tại đâytại đây .


Tôi đang cố gắng hiểu tại sao tài nguyên được tạo qua new FileReader(filePath))sẽ không đóng trong trường hợp an IllegalArgumentExceptionđược ném khi sz âm. Không thử-với-tài nguyên đóng tất cả các AutoClosabletài nguyên bất kể trường hợp ngoại lệ nào được ném ra?
Prasoon Joshi

3
@PrasoonJoshi Không, nó chỉ gọi .close()các biến đã được khai báo trong trình khởi tạo try-with-resources. Đó là lý do tại sao tách nó thành hai khai báo trong ví dụ này thực hiện một mẹo.
Mario Carneiro

4
Andrii và @Mario Bạn vừa đúng vừa sai. Trong ví dụ đầu tiên, FileReader không bị đóng bởi logic try-with-resource. Nhưng khi BufferedReader được đóng, nó cũng sẽ đóng FileReader được bọc. Để có bằng chứng, hãy xem nguồn của java.io.BufferedReader.close (). Do đó, mã từ ví dụ đầu tiên nên được ưu tiên hơn, vì nó ngắn gọn hơn.
jschreiner

7
@jschreiner Đúng, mặc dù vấn đề của Andrii (có phần sz < 0nguyên nhân ) trong đó khiến hàm tạo ném một ngoại lệ trên thực tế sẽ khiến tài nguyên bị rò rỉ.
Mario Carneiro

5
@mario Tôi đồng ý. Phương thức khởi tạo bên ngoài có thể bị lỗi và tài nguyên bên trong sẽ bị rò rỉ. Tôi đã không thấy điều đó trước đây, cảm ơn bạn.
jschreiner
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.