First of all, yes, try-with-resource fixes any of these questions... but I can't see how this exactly works without it.
Let's look at this code from the java documentation as an example, which can be found here:
static String readFirstLineFromFileWithFinallyBlock(String path)
throws IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
try {
return br.readLine();
} finally {
if (br != null) br.close();
}
}
Now, the resource is released on br.close()
if it was acquired. However,
new FileReader(path)
succeeds and then new BufferedReader(...)
throws an exception? FileReader
is closed? BufferedReader
on an already opened FileReader
will always succeed? If so, why is that method declared as throwing an IOException
?Or should we instead be writing the following to make sure that situation doesn't happen?
static String readFirstLineFromFileWithFinallyBlock(String path)
throws IOException {
FileReader fr;
try {
FileReader fr = new FileReader(path);
BufferedReader br;
try {
BufferedReader br = new BufferedReader(fr);
return br.readLine();
} finally {
if (br != null) br.close();
}
finally {
// Implements closeable, so it is ok if we call it twice.
if (fr != null) fr.close();
}
}
Of course, using try-with-resources, this nested mess disappears nonetheless, since we can declare multiple resources in the same statement. But I always see myself writing try-with-resources as a way to avoid thinking at all about exactly this situation, and trying to find a solution online I really couldn't.
Any help would be appreciated, thanks!
The tutorials are often out of date or don't describe good practice.
If we look at the first piece of code you quoted, br
can never be null
. The if ( )
can be removed. This was probably caused by previous code by previous code mixing to finally
with a catch
. If a try
statement has both finally
and a catch
it is probably wrong and almost certainly doing something ill advised. Typically you'd see a null
dance going on and at least one obvious bug.
The received wisdom on this issue is that BufferedReader
will only fail if something has gone horribly wrong with your entire process. Perhaps out of memory or the stack have overflowed. If you get those sorts of exceptions you probably want to bail out of altogether.
The pedantic way of writing the code without try-with-resource is:
FileReader fr = new FileReader(path);
try {
BufferedReader br = new BufferedReader(fr);
return br.readLine();
} finally {
fr.close();
}
However, in some case you may get this wrong. Considered BufferWriter
. You've forgotten to flush
that, haven't you? I mean, I would. If you closed that in a finally
, it wouldn't have been a problem. Also some decorators are resources themselves. For instance, they may have a native implementation that uses non-garbage collected memory. That isn't necessarily documented.
Closing both resource and decorator isn't difficult, but does kind of head towards the right without try with resource.
// (Using same example even though it doesn't matter here - imaging a Writer)
FileReader fr = new FileReader(path);
try {
BufferedReader br = new BufferedReader(fr);
try {
return br.readLine();
} finally {
br.close();
}
} finally {
fr.close();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With