Java: Why does an unclosed stream not throw an exception?

348 views Asked by At

I wanted to write to a property file. But it silently never worked. Just from the code behavior I could not notice it. I always had to open the properties file and look if the value changed. But it never did. So actually I would expect to get an exception . The problem seemed to be that I did not close the InputStream before opening the OutputStream. But I never got to know that. It cost me 3 days because I would expect either OutputStream or store function to give me some feedback. Have a look at the code.

File file = ResourceUtils.getFile("classpath:First.properties");
FileInputStream in = new FileInputStream(file);
Properties props = new Properties();
props.load(in);
System.out.println(props.getProperty("country"));
in.close();  // This I always forgot

FileOutputStream out = new FileOutputStream(file);
props.setProperty("country", "germany");
props.store(out, null);
System.out.println(props.getProperty("country"));
out.close();
2

There are 2 answers

5
jschnasse On

A forgotten close() statement cannot cause an exception. From the perspective of your stream everything is okay. It just didn't wrote to its destination yet. Why should it? Even when the whole program terminates there is no guaranty that the stream closes and writes its internal buffers out.[1]

You always have to call flush() or close() actively. The underlying implementation will then perform the actual write operation.

This mistake is so common that there is an extra Java-Feature to handle it. It is called try-with-resources and prevents programmers from the evil consequences of missing close() statements.

Example:

//use try-with-resources on out
private void saveProperties(Properties properties, String path) {
    try(PrintStream out = new PrintStream(new FileOutputStream(path))) {
        printProperties(properties,out);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}
// you can pass System.out as well to print to console
private void printProperties(Properties properties, PrintStream out) {
    try {
        properties.store(out, null);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}
//properties.load leaves the stream open, so you have to use try-with-resources
private Properties readProperties(String path) {
    try (FileInputStream in = new FileInputStream(path)) {
        Properties properties = new Properties();
        properties.load(in);
        return properties;
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

Related posts on Java Properties:

Related posts on Java Streams:

[1] See: Josh Bloch, Effective Java,(2nd ed.), Page 27.

Avoid finalizers.[...] It is entirely possible, even likely, that a program terminates without executing finalizers on some objects that are no longer reachable.

1
daniu On

As for the actual question "why does it not throw an exception", it's because there are cases you want the Stream to remain open.

    class FileWriteSample implements Closeable {
        FileOutputStream writeTo;

        public FileWriteSample(String filename) throws IOException {
            writeTo = new FileOutputStream(filename);
            // should we expect an Exception here because we don't close the Stream?
            // we're planning to use it later on
        }
        public void write(String s) {
            // write to stream
        }
        public void close() throws IOException {
            writeTo.close();
        }
    }