Estou usando o Java 7 try-with-resources corretamente

87

Estou esperando que o leitor em buffer e o leitor de arquivos fechem e os recursos sejam liberados se a exceção for lançada.

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

No entanto, é necessário ter uma catchcláusula para o fechamento bem-sucedido?

EDITAR:

Essencialmente, o código acima em Java 7 é equivalente ao código abaixo para 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;
}
guepardo
fonte
Depois de ler sua pergunta novamente, não tenho certeza se entendi bem. Você pode explicar isso?
Maroun
Oi. Cheetah, estou tentando entender o papel do primeiro catchde seu exemplo para Java 6. Ou seja catch (Exception ex) { throw ex; }- ele está apenas relançando a exceção, não está fazendo nada, pode ser facilmente removido sem nenhum dano. Ou eu estou esquecendo de alguma coisa?
Sasha

Respostas:

103

Está correto e não há exigência de catchcláusula. Oracle java 7 doc diz que o recurso será fechado independentemente de uma exceção ser realmente lançada ou não.

Você deve usar uma catchcláusula apenas se quiser reagir à exceção. A catchcláusula será executada após o encerramento do recurso.

Aqui está um snippet do tutorial da Oracle :

O exemplo a seguir lê a primeira linha de um arquivo. Ele usa uma instância de BufferedReader para ler os dados do arquivo. BufferedReader é um recurso que deve ser fechado após o programa terminar com ele:

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.

... Como a instância BufferedReader é declarada em uma instrução try-with-resource, ela será fechada independentemente de a instrução try ser concluída normalmente ou abruptamente (como resultado do método BufferedReader.readLine lançando uma IOException).

EDITAR

Em relação à nova questão editada:

O código em Java 6 executa catcho finallybloco e depois o bloco. Isso faz com que os recursos ainda sejam potencialmente abertos no catchbloco.

Na sintaxe Java 7, os recursos são fechados antes do catchbloco, portanto, os recursos já estão fechados durante a catchexecução do bloco. Isso está documentado no link acima:

Em uma instrução try-with-resources, qualquer catch ou finally block é executado depois que os recursos declarados são fechados.

yair
fonte
69

Seu uso de try-with-resources funcionará bem neste caso específico, mas não é totalmente correto em geral. Você não deve encadear recursos como esse, porque pode levar a surpresas desagradáveis. Suponha que você tenha um tamanho de buffer variável:

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);
    } 
}

Suponha que algo deu errado e você acabou szsendo negativo. Neste caso, seu recurso de arquivo (criado via new FileReader(filePath)) NÃO será fechado.

Para evitar esse problema, você deve especificar cada recurso separadamente, como este:

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);
    } 
}

Neste caso, mesmo se a inicialização de brfalha fileainda for fechada. Você pode encontrar mais detalhes aqui e aqui .

Andrii Polunin
fonte
Estou tentando entender porque o recurso criado via new FileReader(filePath))não fechava caso um IllegalArgumentExceptionfosse lançado quando sz fosse negativo. O try-with-resources não fecha todos os AutoClosablerecursos, independentemente de quaisquer exceções lançadas?
Prasoon Joshi
3
@PrasoonJoshi Não, ele apenas chama .close()as variáveis ​​que foram declaradas no inicializador try-with-resources. É por isso que separá-lo em duas declarações neste exemplo resolve o problema.
Mario Carneiro
4
Andrii e @Mario Você está certo e errado. No primeiro exemplo, o FileReader não é fechado pela lógica try-with-resource. Mas quando o BufferedReader for fechado, ele fechará também o FileReader empacotado. Para prova, dê uma olhada na fonte de java.io.BufferedReader.close (). Como consequência, o código do primeiro exemplo deve ser preferido, pois é mais conciso.
jschreiner
7
@jschreiner Verdadeiro, embora o problema de Andrii (um tanto artificial) em que sz < 0faz com que o construtor lance uma exceção irá de fato causar o vazamento do recurso.
Mario Carneiro
5
@mario eu concordo. O construtor externo pode falhar e o recurso interno pode vazar. Eu não vi isso antes, obrigado.
jschreiner