What's the Purpose of Try-With-Resources Statements

What's the purpose of try-with-resources statements?

It was introduced because of some resources used in Java (like SQL connections or streams) being difficult to be handled properly; as an example, in java 6 to handle a InputStream properly you had to do something like:

InputStream stream = new MyInputStream(...);
try {
// ... use stream
} catch(IOException e) {
// handle exception
} finally {
try {
if(stream != null) {
stream.close();
}
} catch(IOException e) {
// handle yet another possible exception
}
}

Do you notice that ugly double try? now with try-with-resources you can do this:

try (InputStream stream = new MyInputStream(...)){
// ... use stream
} catch(IOException e) {
// handle exception
}

and close() is automatically called, if it throws an IOException, it will be supressed (as specified in the Java Language Specification 14.20.3) . Same happens for java.sql.Connection

What is the diff between declaring a Stream with try-with-resources statement or not?

In Java 8, Stream (which is AutoCloseable) cannot be reused, once it
is consumed or used, the stream will be closed.

Not exactly.

Stream terminal operations such as forEach() don't close the stream.

It makes the stream pipeline not consumable any longer.

Which is different.

Package java.util.stream description states :

After the terminal operation is performed, the stream pipeline is
considered consumed, and can no longer be used; if you need to
traverse the same data source again, you must return to the data
source to get a new stream.

It doesn't say that the stream is closed.

So in the following code, the AutoCloseable.close() method of the Stream instance is never invoked :

Stream<Path> entries = Files.walk(Paths.get("."), 4, FileVisitOption.FOLLOW_LINKS);
entries.forEach(x -> System.out.println(x.toAbsolutePath()));

So is it always required to close the stream ? (with explicit close() invocation or better try-with-resources)

java.util.stream.Stream<T> javadocs explains that nearly all Stream instances don't need to be closed with AutoCloseable.close() after using.

It is only true for IO stream.

Streams have a close() method and implement AutoCloseable, but nearly
all stream instances do not actually need to be closed after use.
Generally, only streams whose source is an IO channel (such as those
returned by Files.lines(Path, Charset)) will require closing. Most
streams are backed by collections, arrays, or generating functions,
which require no special resource management. (If a stream does
require closing, it can be declared as a resource in a
try-with-resources statement.)

In our case, you manipulate Files, so it makes sense to close the channel.

By using a try-with-resources, you avoid resource exhaustion exceptions and errors that may occur.

If any error or exception is risen during the processing, the resource may be all the same released.

What exactly gets caught in an extended try-with-resources statement?

Quoting from the JLS section 14.20.3.1:

In a basic try-with-resources statement that manages a single resource:

  • If the initialization of the resource completes abruptly because of a throw of a value V, then the try-with-resources statement completes abruptly because of a throw of the value V.
  • If the initialization of the resource completes normally, and the try block completes abruptly because of a throw of a value V, then:

    • If the automatic closing of the resource completes normally, then the try-with-resources statement completes abruptly because of a throw of the value V.

    • If the automatic closing of the resource completes abruptly because of a throw of a value V2, then the try-with-resources statement completes abruptly because of a throw of value V with V2 added to the suppressed exception list of V.

  • If the initialization of the resource completes normally, and the try block completes normally, and the automatic closing of the resource completes abruptly because of a throw of a value V, then the try-with-resources statement completes abruptly because of a throw of the value V.

This means that if both the code inside the try block and the automatic close() statement throw an exception, the catch part will handle the exception thrown by the try block, with the exception thrown by close() in the suppressed exceptions.

Also, this means that if the try block is successful but the automatic close() fails, the catch will sill be executed and the catched exception will be the exception thrown by close().


Here's a test to verify this behaviour:

public class Main {
public static void main(String[] args) throws Exception {
// try block fails and close() fails
try (T t = new T()) {
throw new Exception("thrown by try part");
} catch (Exception e) {
System.out.println(e.getMessage());
System.out.println(e.getSuppressed()[0].getMessage());
}

// try block is successful but close() fails
try (T t = new T()) {
//
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}

class T implements AutoCloseable {
@Override
public void close() throws Exception {
throw new Exception("thrown by close");
}
}

This code will print

thrown by try part
thrown by close
thrown by close

meaning that the catched exception was the exception thrown by the try part of the code for the first part. For the second part, the catched exception was indeed the exception thrown by close().

Try With Resources vs Try-Catch

The main point of try-with-resources is to make sure resources are closed reliably without possibly losing information.

When you don't use try-with-resources there's a potential pitfall called exception-masking. When code in a try block throws an exception, and the close method in the finally also throws an exception, the exception thrown by the try block gets lost and the exception thrown in the finally gets propagated. This is usually unfortunate, since the exception thrown on close is something unhelpful while the informative one is the one thrown from within the try block. (So instead of seeing the SQLException that tells you which referential integrity constraint was violated, you're shown something like BrokenPipeException where closing the resource failed.)

This exception-masking is an annoying problem that try-with-resources prevents from happening.

As part of making sure exception-masking wouldn't lose important exception information, when try-with-resources was developed they had to decide what to do with the exceptions thrown from the close method.

With try-with-resources, if the try block throws an exception and the close method also throws an exception, then the exception from the close block gets tacked on to the original exception:

... there are situations where two independent exceptions can be thrown in sibling code blocks, in particular in the try block of a try-with-resources statement and the compiler-generated finally block which closes the resource. In these situations, only one of the thrown exceptions can be propagated. In the try-with-resources statement, when there are two such exceptions, the exception originating from the try block is propagated and the exception from the finally block is added to the list of exceptions suppressed by the exception from the try block. As an exception unwinds the stack, it can accumulate multiple suppressed exceptions.

On the other hand if your code completes normally but the resource you're using throws an exception on close, that exception (which would get suppressed if the code in the try block threw anything) gets thrown. That means that if you have some JDBC code where a ResultSet or PreparedStatement is closed by try-with-resources, an exception resulting from some infrastructure glitch when a JDBC object gets closed can be thrown and can rollback an operation that otherwise would have completed successfully.

Without try-with-resources whether the close method exception gets thrown is up to the application code. If it gets thrown in a finally block when the try block throws an exception, the exception from the finally block will mask the other exception. But the developer has the option of catching the exception thrown on close and not propagating it.

Should I use a try-with-resources statement for each line of log?

No, you should not open and close a FileWriter every time you want to append a new line to your log file.

You should:

  • open the log file at the start of the application
  • append to it
  • call flush()
  • and finally call close().

or if you want to use the try-with-resources statement then the code that calls the logger method should be put inside the body of the try-with-resources statement. Thanks to this you will not have to call the close() method as it will be called automatically.

However the best solution would be to, instead of reivnteting the wheel, use one of the existing, battle-tested, open source loggers such as log4j or logback, that cover all these internal details and expose a simple API that allows you to simply log a provided message.

Use resource in try with resource statement that was created before

You don't have to create the object in the try-with-resources statement, you just have to declare some local variables of a type that implements AutoCloseable. The variables are effectively final, and scoped to the try block, which allows the compiler to use them to generate the close boilerplate needed for cleanup.

FileInputStream f1 = new FileInputStream("test1.xml");
FileInputStream f2 = new FileInputStream("test2.xml");
// Don't need to create the resources here, just need to declare some vars
try (InputStream in1 = f1; InputStream in2 = f2) {
// error; in1 is final
in1 = new FileInputStream("t");
}

Better Resource Management with Java SE 7: Beyond Syntactic Sugar.

Addendum: Since java 9 the requirements have been relaxed; you don't have to redeclare the variables in the try block if the originals are effectively final.

JEP 213

try-with-resources details

Whenever language related details are needed, the most complete reference is the Java Language Specification (just Google it). For the try-with-resources statement, you can read section 14.20.3 which states that the following:

try ({VariableModifier} R Identifier = Expression ...)
Block

is translated to

{
final {VariableModifierNoFinal} R Identifier = Expression;
Throwable #primaryExc = null;
try ResourceSpecification_tail
Block catch (Throwable #t) {
#primaryExc = #t;
throw #t;
} finally {
if (Identifier != null) {
if (#primaryExc != null) {
try {
Identifier.close();
} catch (Throwable #suppressedExc) {
#primaryExc.addSuppressed(#suppressedExc);
}
} else {
Identifier.close();
}
}
}
}

In your first example, the resource R is BufferedReader, the Identifier is br and the Expression is new BufferedReader(new FileReader(filePath)). It follows that only the BufferedReader is closed in the implicit finally block. The finally block will not call close on the FileReader because it is not part of the resource declaration itself. However, it happens that the implementation of BufferedReader.close() internally calls the close method of the wrapped FileReader. So the answer to the first question is yes simply because the wrapper object closed it (following the common wisdom that a resource should release any wrapped resource when being itself released), not because of the try-with-resources.

In the second example:

private static BufferedReader buf1;

public static void main(String[] args) throws IOException {
//some code
try (BufferedReader buf2 = buf1)
{

}
}

the answer depends on the some code. Here buf2 and buf1 both refer to the same object in memory. If this "some code" initializes buf1 to some object, then this object will be closed since buf2 also refers to it. If not and buf1 is null (and therefore buf2 is null), then nothing will be closed because of the null check in the implicit finally shown above.

Does the catch in try-with-resources cover the code in parentheses?

From the JLS, your example is an extended try-with-resources.

A try-with-resources statement with at least one catch clause and/or a
finally clause is called an extended try-with-resources statement.

In that case :

The effect of the translation is to put the resource specification
"inside" the try statement. This allows a catch clause of an extended
try-with-resources statement to catch an exception due to the
automatic initialization or closing of any resource.

So yes, the exception will be caught by your catch block.



Related Topics



Leave a reply



Submit