maneira correta de fluxos aninhados perto e escritores em Java [duplicado]

StackOverflow https://stackoverflow.com/questions/884007

  •  22-08-2019
  •  | 
  •  

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?

Foi útil?

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 RuntimeExceptions 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 RuntimeExceptions. 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.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top