Search code examples
javafile-iofilereaderfilewriter

Do I need to keep variables for all writers/readers to close them all manually?


Consider this example:

public class FileAppender implements AutoCloseable {
    private final FileWriter fileWriter;
    private final BufferedWriter bufferedWriter;

    public FileAppender(String fileName) throws IOException {
        fileWriter = new FileWriter(fileName, true)
        bufferedWriter = new BufferedWriter(fileWriter);
    }

    public void appendLine(String line) throws IOException {
        bufferedWriter.write(line + "\n");
    }

    @Override
    public void close() throws Exception {
        bufferedWriter.close();
        fileWriter.close();
    }
}

Here we keep an unused FileWriter as a member of the class, just so we can manually close it later. There are many tutorials in the web which show examples like this where multiple streams are closed manually.

We could instead implement the same class more concisely:

public class FileAppender implements AutoCloseable {
    private final BufferedWriter writer;

    public FileAppender(String fileName) throws IOException {
        writer = new BufferedWriter(new FileWriter(fileName, true));
    }

    public void appendLine(String line) throws IOException {
        writer.write(line + "\n");
    }

    @Override
    public void close() throws Exception {
        writer.close();
    }
}

Same applies to the usage of FileReader and BufferedReader.

Would there be any difference between the above two implementations?


Solution

  • With BufferedWriter, specifically, you can just use close on the BufferedWriter and it will call close on the underlying FileWriter.

    But, as far as I'm aware, that's not documented, and not required of Writer implementations that wrap other Writers (and similarly, streams). I'm paranoid, and tend to close things explicitly (in reverse order of opening them).

    You can see the close operation in the BufferedWriter source (this example is from JDK 11.0.1), though you have to look fairly closely:

    public void close() throws IOException {
        synchronized (lock) {
            if (out == null) {
                return;
            }
            try (Writer w = out) {
                flushBuffer();
            } finally {
                out = null;
                cb = null;
            }
        }
    }
    

    Note the use of try-with-resources to auto-close out (via w).