maneira correta de fluxos aninhados perto e escritores em Java [duplicado]
Pergunta
Esta questão já tem uma resposta aqui:
Nota: Esta questão ea maioria de suas respostas data de antes do lançamento do Java 7 Java 7 fornece funcionalidade para fazer isso easilly Gerenciamento automático de Recursos . Se você estiver usando Java 7 ou mais tarde você deve avançar para a resposta de Ross Johnson .
O que é considerado o melhor caminho, mais abrangente aos fluxos aninhados perto em Java? Por exemplo, considere a configuração:
FileOutputStream fos = new FileOutputStream(...)
BufferedOS bos = new BufferedOS(fos);
ObjectOutputStream oos = new ObjectOutputStream(bos);
Eu entendo a operação de fechamento precisa ser segurado (provavelmente usando uma cláusula finalmente). O que eu pergunto sobre se, é necessário fazer explicitamente se que os fluxos aninhados estão fechados, ou é suficiente para apenas certifique-se de fechar o fluxo exterior (oos)?
Um aviso coisa que eu, pelo menos, lidar com este exemplo específico, é que as correntes internas só parecem jogar FileNotFoundExceptions. Que parece implicar que não é tecnicamente uma necessidade de se preocupar com fechá-los se eles falharem.
Aqui está o que um colega escreveu:
Tecnicamente, se fosse implementada direita, fechando a mais externa fluxo (oos) deve ser suficiente. Mas a implementação parece falho.
Exemplo: BufferedOutputStream herda close () de FilterOutputStream, que define como:
155 public void close() throws IOException {
156 try {
157 flush();
158 } catch (IOException ignored) {
159 }
160 out.close();
161 }
No entanto, se flush () lança uma exceção de tempo de execução, por algum motivo, então out.close () nunca será chamado. Assim, parece "mais segura" (mas feio) para principalmente preocupação sobre o fechamento FOS, que está mantendo o arquivo aberto.
O que é considerado o melhor mãos para baixo, quando-você-absolutamente-need-to-be-a certeza, se aproxime para fechar fluxos aninhados?
E há alguma documentação oficial do Java / Sun que lidam com isso em detalhes finos?
Solução
Eu costumo fazer o seguinte. Primeiro, definir um método-modelo de classe base para lidar com a bagunça try / catch
import java.io.Closeable;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
public abstract class AutoFileCloser {
// the core action code that the implementer wants to run
protected abstract void doWork() throws Throwable;
// track a list of closeable thingies to close when finished
private List<Closeable> closeables_ = new LinkedList<Closeable>();
// give the implementer a way to track things to close
// assumes this is called in order for nested closeables,
// inner-most to outer-most
protected final <T extends Closeable> T autoClose(T closeable) {
closeables_.add(0, closeable);
return closeable;
}
public AutoFileCloser() {
// a variable to track a "meaningful" exception, in case
// a close() throws an exception
Throwable pending = null;
try {
doWork(); // do the real work
} catch (Throwable throwable) {
pending = throwable;
} finally {
// close the watched streams
for (Closeable closeable : closeables_) {
if (closeable != null) {
try {
closeable.close();
} catch (Throwable throwable) {
if (pending == null) {
pending = throwable;
}
}
}
}
// if we had a pending exception, rethrow it
// this is necessary b/c the close can throw an
// exception, which would remove the pending
// status of any exception thrown in the try block
if (pending != null) {
if (pending instanceof RuntimeException) {
throw (RuntimeException) pending;
} else {
throw new RuntimeException(pending);
}
}
}
}
}
Observe o "pendente" exceção -. Este cuida do caso em que uma exceção lançada durante o próximo iria mascarar uma exceção que pode realmente se preocupam
O finalmente tenta fechar do lado de fora de qualquer fluxo decorado em primeiro lugar, por isso, se você tivesse um BufferedWriter envolvendo um FileWriter, tentamos fechar a BuffereredWriter primeiro lugar, e se isso falhar, ainda tentar fechar a si FileWriter. (Note-se que a definição de chamadas closeable para close () para ignorar a chamada se o fluxo está já fechado)
Você pode usar a classe acima da seguinte forma:
try {
// ...
new AutoFileCloser() {
@Override protected void doWork() throws Throwable {
// declare variables for the readers and "watch" them
FileReader fileReader =
autoClose(fileReader = new FileReader("somefile"));
BufferedReader bufferedReader =
autoClose(bufferedReader = new BufferedReader(fileReader));
// ... do something with bufferedReader
// if you need more than one reader or writer
FileWriter fileWriter =
autoClose(fileWriter = new FileWriter("someOtherFile"));
BufferedWriter bufferedWriter =
autoClose(bufferedWriter = new BufferedWriter(fileWriter));
// ... do something with bufferedWriter
}
};
// .. other logic, maybe more AutoFileClosers
} catch (RuntimeException e) {
// report or log the exception
}
Usando essa abordagem, você nunca tem que se preocupar com o try / catch / finally para lidar com fechamento de arquivos novamente.
Se este for demasiado pesado para o seu uso, pelo menos, pensar sobre seguindo o try / catch e o "pendente" abordagem variável que utiliza.
Outras dicas
Ao fechar fluxos acorrentados, você só precisa fechar o fluxo externo. Quaisquer erros serão propagadas até a cadeia e ser pego.
Consulte Java i o Streams / para mais detalhes.
Para resolver a questão
No entanto, se flush () lança uma exceção de tempo de execução, por algum motivo, então out.close () nunca será chamado.
Isso não está certo. Depois de pegar e ignorar essa exceção, a execução irá pegar de volta após o bloco catch ea declaração out.close()
será executada.
O seu colega faz um ponto sobre o Runtime Exceção bom. Se você absolutamente precisa do fluxo a ser fechado, você pode sempre tentar fechar cada um individualmente, de fora para dentro, parando na primeira exceção.
Na era Java 7, tentar-com-recursos é certamente o caminho ir. Como mencionado em várias respostas anteriores, o pedido de fecho se propaga a partir do fluxo mais exterior para a mais interior fluxo. Assim, um único perto é tudo o que é necessário.
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(f))) {
// do something with ois
}
Há, porém, um problema com esse padrão. O try-com-recursos não tem conhecimento do FileInputStream interior, por isso, se o construtor ObjectInputStream lança uma exceção, o FileInputStream nunca é fechada (até que o coletor de lixo chega a ele). A solução é ...
try (FileInputStream fis = new FileInputStream(f); ObjectInputStream ois = new ObjectInputStream(fis)) {
// do something with ois
}
Isto não é tão elegante, mas é mais robusta. Se este é realmente um problema vai depender do que exceções podem ser geradas durante a construção do objeto externo (s). ObjectInputStream pode jogar IOException que pode muito bem se tratado por uma aplicação sem terminar. Muitas classes de fluxo única lançar exceções não verificadas, o que pode resultar no cancelamento da aplicação.
É uma boa prática para usar o Apache Commons para lidar com IO objetos relacionados.
No finally
IOUtils
uso cláusula
IOUtils.closeQuietly (bWriter); IOUtils.closeQuietly (oWritter);
Código trecho abaixo.
BufferedWriter bWriter = null;
OutputStreamWriter oWritter = null;
try {
oWritter = new OutputStreamWriter( httpConnection.getOutputStream(), "utf-8" );
bWriter = new BufferedWriter( oWritter );
bWriter.write( xml );
}
finally {
IOUtils.closeQuietly(bWriter);
IOUtils.closeQuietly(oWritter);
}
O colega levanta um ponto interessante, e há motivos para discutir qualquer forma.
Pessoalmente, eu iria ignorar o RuntimeException
, porque uma exceção não verificada significa um erro no programa. Se o programa estiver incorreto, corrigi-lo. Você não pode "pega" um mau programa em tempo de execução.
Esta é uma pergunta surpreendentemente difícil. (Mesmo assumindo que o código acquire; try { use; } finally { release; }
está correto.)
Se a construção do decorador falhar, então você não vai ser fechar o fluxo subjacente. Portanto você precisa fazer para fechar o fluxo subjacente explicitamente, seja no uso, finalmente, depois ou, mais diifcult depois de ter conseguido entregar o recurso para o decorador).
Se uma exceção faz a execução a falhar, você realmente quer nivelado?
Alguns decoradores têm realmente próprios recursos. A implementação atual Sol da ZipInputStream
por exemplo, tem memória heap Java não-alocado.
Foi alegado que (IIRC) dois terços dos usos de recursos na biblioteca Java são implementadas de forma claramente errada.
Enquanto BufferedOutputStream
fecha mesmo em um IOException
de flush
, BufferedWriter
fecha corretamente.
Meu conselho: Fechar recursos tão diretamente quanto possível e não deixá-los manchar outro código. OTOH, você pode gastar muito tempo sobre esta questão - se OutOfMemoryError
é jogado É bom para se comportar bem, mas outros aspectos do seu programa são, provavelmente, uma maior prioridade e código da biblioteca é provavelmente quebrado nesta situação de qualquer maneira. Mas eu sempre escrever:
final FileOutputStream rawOut = new FileOutputStream(file);
try {
OutputStream out = new BufferedOutputStream(rawOut);
... write stuff out ...
out.flush();
} finally {
rawOut.close();
}
(Look: Não! Captura)
E, talvez, usar o idioma Executar Around.
O Java SE 7 tentar-com-recursos doesn 't parecem ser mencionado. Ele elimina a necessidade de fazer explicitamente a fechar completamente, e eu gosto bastante da idéia.
Infelizmente, para o desenvolvimento Android este doce só se torna disponível usando Estúdio Android (eu acho) e segmentação Kitkat e acima .
Além disso, você não tem que fechar todos os fluxos aninhados
verificar isso http://ckarthik17.blogspot.com/2011/02/closing-nested -streams.html
Eu uso a fluxos de perto como este, sem nidificação try-catch em finalmente blocos
public class StreamTest {
public static void main(String[] args) {
FileOutputStream fos = null;
BufferedOutputStream bos = null;
ObjectOutputStream oos = null;
try {
fos = new FileOutputStream(new File("..."));
bos = new BufferedOutputStream(fos);
oos = new ObjectOutputStream(bos);
}
catch (Exception e) {
}
finally {
Stream.close(oos,bos,fos);
}
}
}
class Stream {
public static void close(AutoCloseable... array) {
for (AutoCloseable c : array) {
try {c.close();}
catch (IOException e) {}
catch (Exception e) {}
}
}
}
JavaDocs da Sun incluem RuntimeException
s em sua documentação, como mostrado por lido (byte [], int int) método; documentados como jogando NullPointerException e IndexOutOfBoundsException.
flush de FilterOutputStream () só é documentado como jogando IOException, assim, que na verdade não lançar quaisquer RuntimeException
s. Qualquer que poderia ser jogado provavelmente ser envolto em um IIOException
.
Ainda pode lançar uma Error
, mas não há muito que você pode fazer sobre aqueles; Sun recomenda que você não tente pegá-los.