A sintaxe do Java 7 try-with-resources (também conhecida como bloco ARM ( Gerenciamento automático de recursos )) é agradável, curta e direta ao usar apenas um AutoCloseable
recurso. No entanto, não tenho certeza de qual é o idioma correto quando preciso declarar vários recursos que dependem um do outro, por exemplo, a FileWriter
e a BufferedWriter
que o envolvem. Obviamente, essa pergunta diz respeito a qualquer caso em que alguns AutoCloseable
recursos são agrupados, não apenas a essas duas classes específicas.
Eu vim com as três alternativas a seguir:
1)
O idioma ingênuo que eu vi é declarar apenas o wrapper de nível superior na variável gerenciada pelo ARM:
static void printToFile1(String text, File file) {
try (BufferedWriter bw = new BufferedWriter(new FileWriter(file))) {
bw.write(text);
} catch (IOException ex) {
// handle ex
}
}
Isso é bom e curto, mas está quebrado. Como o subjacente FileWriter
não é declarado em uma variável, ele nunca será fechado diretamente no finally
bloco gerado . Ele será fechado apenas pelo close
método de embalagem BufferedWriter
. O problema é que, se uma exceção for lançada do bw
construtor, ela close
não será chamada e, portanto, o subjacente FileWriter
não será fechado .
2)
static void printToFile2(String text, File file) {
try (FileWriter fw = new FileWriter(file);
BufferedWriter bw = new BufferedWriter(fw)) {
bw.write(text);
} catch (IOException ex) {
// handle ex
}
}
Aqui, o recurso subjacente e o empacotamento são declarados nas variáveis gerenciadas pelo ARM, de modo que ambos certamente serão fechados, mas o subjacente fw.close()
será chamado duas vezes : não apenas diretamente, mas também através do empacotamento bw.close()
.
Isso não deve ser um problema para essas duas classes específicas que ambos implementam Closeable
(que é um subtipo de AutoCloseable
), cujo contrato afirma que várias chamadas para close
são permitidas:
Fecha esse fluxo e libera todos os recursos do sistema associados a ele. Se o fluxo já estiver fechado, a invocação desse método não terá efeito.
No entanto, em um caso geral, posso ter recursos que implementam apenas AutoCloseable
(e não Closeable
), o que não garante que close
possa ser chamado várias vezes:
Observe que, diferentemente do método close de java.io.Closeable, esse método close não precisa ser idempotente. Em outras palavras, chamar esse método close mais de uma vez pode ter algum efeito colateral visível, ao contrário de Closeable.close, que é necessário para não ter efeito se chamado mais de uma vez. No entanto, os implementadores dessa interface são fortemente encorajados a tornar seus métodos próximos idempotentes.
3)
static void printToFile3(String text, File file) {
try (FileWriter fw = new FileWriter(file)) {
BufferedWriter bw = new BufferedWriter(fw);
bw.write(text);
} catch (IOException ex) {
// handle ex
}
}
Esta versão deve ser teoricamente correta, porque apenas o fw
representa um recurso real que precisa ser limpo. O bw
próprio não possui nenhum recurso, ele apenas delega para o fw
, portanto deve ser suficiente fechar apenas o subjacente fw
.
Por outro lado, a sintaxe é um pouco irregular e, também, o Eclipse emite um aviso, que acredito ser um alarme falso, mas ainda é um aviso com o qual devemos lidar:
Vazamento de recurso: 'bw' nunca é fechado
Então, qual abordagem usar? Ou eu perdi algum outro idioma que é o correto ?
public BufferedWriter(Writer out, int sz)
pode jogar um IllegalArgumentException
. Além disso, eu posso estender o BufferedWriter com uma classe que lançaria algo de seu construtor ou criaria qualquer invólucro personalizado que eu precisar.
BufferedWriter
construtor pode facilmente lançar uma exceção. OutOfMemoryError
é provavelmente o mais comum, pois aloca uma boa parte da memória para o buffer (embora possa indicar que você deseja reiniciar todo o processo). / Você precisa flush
seu BufferedWriter
se você não fechar e quer manter o conteúdo (geralmente única o caso não exceção). FileWriter
escolhe o que quer que seja a codificação de arquivo "padrão" - é melhor ser explícito.