Try with resources ошибка close

interface AutoClosable has following method declaration:

void close()  throws Exception

Thus we see that method close can throws Exception.

When I write code try-with resources it is looks like this:

private static void printFileJava7() throws IOException {

    try(FileInputStream input = new FileInputStream("file.txt")) {

        int data = input.read();
        while(data != -1){
            System.out.print((char) data);
            data = input.read();
        }
    }
}

At this code is absent Exception handling.

I don’t understand what happens if close method throws exception.

asked Jul 13, 2014 at 9:39

gstackoverflow's user avatar

gstackoverflowgstackoverflow

36.5k112 gold badges345 silver badges701 bronze badges

0

You are right that close() method defined in AutoClosable throws exception. However other interface named Closable that extends AutoClosable redefines this method as following:

void close() throws IOException

All IO related classes implement Closable, so their close() method throws IOException. But your method throws it too, so in your code no-one catches this exception. As always it will be caught by upper layer of your application or by JVM itself if no-one catches it before.

answered Jul 13, 2014 at 9:45

AlexR's user avatar

AlexRAlexR

114k16 gold badges130 silver badges207 bronze badges

1

From the jls about Compile-Time Checking of Exceptions

When interfaces are involved, more than one method declaration may be overridden by a single overriding declaration. In this case, the overriding declaration must have a throws clause that is compatible with all the overridden declarations (§9.4.1).

So when you have something like

static class AC implements AutoCloseable {
    @Override
    public void close() throws Exception {
        throw new Exception("btooom!");
    }
    public void ac() {
        System.out.println("no");
    }
}

public static void main(String[] args) throws Exception {
    try(AC ac = new AC()) {
        ac.ac();
    }
}

then you are forded to either add a catch clause to the surrounding try block or add a throws declaration.

In the case of FileInputStream and to apply what the jsl states, the AutoCloseable’s close method is overriden from Closeable’s, hence you only have to catch an IOException or add it to throws clause.

Further the javadocs of AutoCloseable#close states

While this interface method is declared to throw Exception, implementers are strongly encouraged to declare concrete implementations of the close method to throw more specific exceptions, or to throw no exception at all if the close operation cannot fail.

answered Jul 13, 2014 at 10:26

A4L's user avatar

A4LA4L

17.4k6 gold badges49 silver badges68 bronze badges

The key to this particular scenario is the «supressed exception».

«Whenever an exception is thrown within the body, and then followed
by an exception thrown by the try-with-resources statement, only the
exception thrown in the body of the try is eligible to be caught by
the exception handling code. All other exceptions are considered supressed exceptions»

A concept that is new with Java 7.SO you will get the same output as when only the printfileJava7 class throws an exception, since the CloseException thrown by the close() method would be suppressed.

Please refer to When two exceptions are thrown by the try-with-resources construct link.Which have taken exact scenario what You have asked

answered Jul 13, 2014 at 9:44

Abhijeet Panwar's user avatar

Abhijeet PanwarAbhijeet Panwar

1,8273 gold badges26 silver badges48 bronze badges

You need to have a catch block followed by try in the method printFileJava7(). If you don’t want to catch it here, you need handle it in the method which calls the method printFileJava7(). We have to catch in one of the layers otherwise it will be propagated back to the calling client.

answered Jan 27, 2016 at 22:53

Venkat's user avatar

VenkatVenkat

1371 gold badge1 silver badge10 bronze badges

From the docs:

The resource declared in the try-with-resources statement is a
BufferedReader. The declaration statement appears within parentheses
immediately after the try keyword. The class BufferedReader, in Java
SE 7 and later, implements the interface java.lang.AutoCloseable.
Because the BufferedReader instance is declared in a try-with-resource
statement, it will be closed regardless of whether the try statement
completes normally or abruptly (as a result of the method
BufferedReader.readLine throwing an IOException).

Basically, it’s equivalent to:

BufferedReader br = new BufferedReader(new FileReader(System.in));
try {
    int i = Integer.parseInt(br.readLine());
    System.out.println(i);
} finally {
    br.close();
}

Let’s try it out (from here a working example):

//class implementing java.lang.AutoCloseable
public class ClassThatAutoCloses implements java.lang.AutoCloseable {

    
    public ClassThatAutoCloses() {}
    
    public void doSomething() {
        
        System.out.println("Pippo!");
    }
    
    
    @Override
    public void close() throws Exception {
    
        throw new Exception("I wasn't supposed to fail"); 
        
    }

}
//the main class
public class Playground {

    /**
     * @param args
     */
    public static void main(String[] args) {
        
        //this catches exceptions eventually thrown by the close
        try {
            
            try(var v = new ClassThatAutoCloses() ){
                
                v.doSomething();
                
            }
        } catch (Exception e) {
            //if something isn't catched by try()
            //close failed will be printed
            System.err.println("close failed");
            e.printStackTrace();
        }
    }

}
//the output
Pippo!
close failed
java.lang.Exception: I wasn't supposed to fail
    at ClassThatAutoCloses.close(ClassThatAutoCloses.java:26)
    at Playground.main(Playground.java:24)

Sometime ago I started writing a JNI wrapper around libgit2.

I quickly gave up on it. I was not confident on my very little C knowledge nor my very little JNI knowledge. So instead I started writing a pure Java GIT (partial) implementation.

A main takeaway from my brief JNI experience was my newfound appreciation of:

  • Java’s error signaling via exceptions. Checked exceptions included (as opposed to return codes); and
  • Java’s exception handling and resource releasing via try and try-with-resources statements (as opposed to a combination of if and goto statements).

What problems did the try-with-resources solve?

As mentioned we handle exceptional conditions in Java via the try and the try-with-resources Java statements. The former exists since the first release of the language. The latter was introduced in Java 7.

At time of writing Java 7 is almost 11 years old. So let’s remember why the try-with-resources statement was introduced.

I was not there with the language designers. Nor did I research the mailing lists. So this section is about my assumptions. I just want this to be clear.

Anyways, what problems did the try-with-resources solve? Wasn’t the plain try statement enough?

Let’s investigate.

Our running example

For our investigations we will use a class that simulates a resource that needs to be released after it has been used.

For all purposes, think of it being like a java.io.FileWriter.

Here is the code:

public class Resource implements Closeable {
  final String name;

  Resource(String name) {
    this.name = name;

    print0("created");
  }

  public static Resource create(String name) throws IOException {
    return new Resource(name);
  }

  @Override
  public void close() throws IOException {
    print0("closed");
  }

  public void write(String s) throws IOException {
    print0(s);
  }

  final void print0(String string) {
    System.out.println(name + ": " + string);
  }
}

Enter fullscreen mode

Exit fullscreen mode

To use it you first need to create a new instance by invoking the static create method. It requires a name. Think of it as a pathname.

The class can throw an IOException on creation, just like new FileWriter or Files.newBufferedWriter would. Examples of reasons why this might happen:

  • the specified pathname resolves to a directory. You can’t write to a directory as if it were a regular file; or
  • the specified pathname could be of a location for which you do not have write permission.

Once the resource object is created it prints created to System.out.

You can write to it by invoking the write method. It does actual I/O as it prints to System.out.

After you have done writing you must close the resource by invoking the close method. It is specified by the java.io.Closeable interface which Resource implements. It prints closed signaling that closing was successful.

Single resource

So let’s use our class. We will use a single resource to print a string to the console.

First using a plain try statement:

public class Try {
  public static void main(String[] args) throws IOException {
    var out = Resource.create("TRY");

    try {
      out.write("Try statement (iteration 1)");
    } finally {
      out.close();
    }
  }
}

Enter fullscreen mode

Exit fullscreen mode

I know, I am not actually doing any error handling here, but bear with me.

Executing this example results in the following output to the console:

TRY: created
TRY: Try statement (iteration 1)
TRY: closed

Enter fullscreen mode

Exit fullscreen mode

Now, the same example using the try-with-resources statement:

public class TryWithResources {
  public static void main(String[] args) throws IOException {
    try (var out = Resource.create("TWR")) {
      out.write("Try-with-resources statement (iteration 1)");
    }
  }
}

Enter fullscreen mode

Exit fullscreen mode

Prints:

TWR: created
TWR: Try-with-resources statement (iteration 1)
TWR: closed

Enter fullscreen mode

Exit fullscreen mode

The try-with-resources is more concise, the closing happens automatically and the resource variable is scoped to the statement itself.

Still, one can say that, in this example, the try statement looks manageable, does it not?

I tend to agree, but I do not think this example illustrates the main reason for using the try-with-resources statement.

Two resources

I think most problems arise when using more than one resource. This can be a common use-case, for instance:

  • transferring data from an InputStream to an OutputStream;
  • same thing but using Java NIO channels; or
  • using plain JDBC and dealing with connection, statement and result set.

So let’s write a version using two resources:

public class Try {
  // Please avoid code like this in production
  public static void main(String[] args) throws IOException {
    var a = Resource.create("A");
    var b = Resource.create("B");

    try {
      a.write("Try statement (iteration 2)");
      b.write("Try statement (iteration 2)");
    } finally {
      a.close();
      b.close();
    }
  }
}

Enter fullscreen mode

Exit fullscreen mode

This example has a number of problems which we will discuss in this section. For now, let’s run it as it is:

A: created
B: created
A: Try statement (iteration 2)
B: Try statement (iteration 2)
A: closed
B: closed

Enter fullscreen mode

Exit fullscreen mode

While using the try-with-resources statement:

public class TryWithResources {
  public static void main(String[] args) throws IOException {
    try (var a = Resource.create("A"); var b = Resource.create("B")) {
      a.write("Try-with-resources statement (iteration 2)");
      b.write("Try-with-resources statement (iteration 2)");
    }
  }
}

Enter fullscreen mode

Exit fullscreen mode

Gives:

A: created
B: created
A: Try-with-resources statement (iteration 2)
B: Try-with-resources statement (iteration 2)
B: closed
A: closed

Enter fullscreen mode

Exit fullscreen mode

Apart from the closing happening in a different order (we will look into it later) both examples produce an equivalent output.

So now what?

Two resources: error on creation

Let’s modify our Resource class to simulate an error on creation. As mentioned earlier, thinking the class as a FileWriter, this could be caused by specifying a location for which we do not have writing permission:

public static Resource throwOnCreate(String name) throws IOException {
  throw new IOException("Failed to create: " + name);
}

Enter fullscreen mode

Exit fullscreen mode

And we use it with the plain try statement:

public class Try {
  // Please avoid code like this in production
  public static void main(String[] args) throws IOException {
    var a = Resource.create("A");
    var b = Resource.throwOnCreate("B");

    try {
      a.write("Try statement (iteration 3)");
      b.write("Try statement (iteration 3)");
    } finally {
      b.close();
      a.close();
    }
  }
}

Enter fullscreen mode

Exit fullscreen mode

Notice we reversed the order of the close statements.

Once again, this example still contain warning signs (in particular in the finally block) which will discuss as we go.

Running this example prints:

A: created
Exception in thread "main" java.io.IOException: Failed to create: B
        at shared.Resource.throwOnCreate(Resource.java:29)
        at iter3.Try.main(Try.java:14)

Enter fullscreen mode

Exit fullscreen mode

Resource A was created but it was never closed.

On the other hand, the same example using the try-with-resources statement:

public class TryWithResources {
  public static void main(String[] args) throws IOException {
    try (var a = Resource.create("A"); var b = Resource.throwOnCreate("B")) {
      a.write("Try-with-resources statement (iteration 3)");
      b.write("Try-with-resources statement (iteration 3)");
    }
  }
}

Enter fullscreen mode

Exit fullscreen mode

Prints:

A: created
A: closed
Exception in thread "main" java.io.IOException: Failed to create: B
        at shared.Resource.throwOnCreate(Resource.java:29)
        at iter3.TryWithResources.main(TryWithResources.java:13)

Enter fullscreen mode

Exit fullscreen mode

Resource A was created and was properly closed.

Two resources: error on closing

Let’s modify our Resource class once again to simulate an error when executing the close method.

This can happen when using a buffered writer. During the close operation, flushing the buffer to the filesystem will fail if the disk runs out of space.

So we create a subclass that throws in the close method:

private static class ThrowOnClose extends Resource {
  ThrowOnClose(String name) {
    super(name);
  }

  @Override
  public void close() throws IOException {
    print0("close() called");

    throw new IOException("Failed to close: " + name);
  }
}

Enter fullscreen mode

Exit fullscreen mode

And our create method for this class is named throwOnClose:

public static Resource throwOnClose(String name) throws IOException {
  return new ThrowOnClose(name);
}

Enter fullscreen mode

Exit fullscreen mode

Let’s use it in the plain try case. We will have the B resource to throw on the close operation:

public class Try {
  // Please avoid code like this in production
  public static void main(String[] args) throws IOException {
    var a = Resource.create("A");
    var b = Resource.throwOnClose("B");

    try {
      a.write("Try statement (iteration 4)");
      b.write("Try statement (iteration 4)");
    } finally {
      b.close();
      a.close();
    }
  }
}

Enter fullscreen mode

Exit fullscreen mode

Running this example results in:

A: created
B: created
A: Try statement (iteration 4)
B: Try statement (iteration 4)
B: close() called
Exception in thread "main" java.io.IOException: Failed to close: B
        at shared.Resource$ThrowOnClose.close(Resource.java:54)
        at iter4.Try.main(Try.java:20)

Enter fullscreen mode

Exit fullscreen mode

And the resource A was not closed.

Let’s write the same example using the try-with-resources statement:

public class TryWithResources {
  public static void main(String[] args) throws IOException {
    try (var a = Resource.create("A"); var b = Resource.throwOnClose("B")) {
      a.write("Try-with-resources statement (iteration 4)");
      b.write("Try-with-resources statement (iteration 4)");
    }
  }
}

Enter fullscreen mode

Exit fullscreen mode

Which gives:

A: created
B: created
A: Try-with-resources statement (iteration 4)
B: Try-with-resources statement (iteration 4)
B: close() called
A: closed
Exception in thread "main" java.io.IOException: Failed to close: B
        at shared.Resource$ThrowOnClose.close(Resource.java:54)
        at iter4.TryWithResources.main(TryWithResources.java:16)

Enter fullscreen mode

Exit fullscreen mode

Resource A was properly closed.

Two resources: exception during the try block

We mentioned earlier that, so far, we were not actually doing any error handling. In other words, we were not declaring the catch part of our statement.

Granted, in our example, I am not sure we can do much. Except maybe:

  • give a proper message to the user (as opposed to a stack trace); and
  • stop the execution normally (as opposed to abruptly by an uncaught exception).

The try example was refactored to:

public class Try {
  // Please avoid code like this in production
  public static void main(String[] args) throws IOException {
    Resource a = null;
    Resource b = null;

    try {
      a = Resource.create("A");
      a.throwOnWrite("Try statement (iteration 5)");

      b = Resource.create("B");
      b.write("Try statement (iteration 5)");
    } catch (IOException e) {
      System.err.println("The program failed: " + e.getMessage());

      return; // explicitly stop execution
    } finally {
      b.close();
      a.close();
    }

    System.out.println("More instructions...");
  }
}

Enter fullscreen mode

Exit fullscreen mode

Notice that, on the A resource, we invoked a throwOnWrite method.

It is a method to simulate an IOException during a write operation:

public void throwOnWrite(String s) throws IOException {
  throw new IOException("Failed to write: " + name);
}

Enter fullscreen mode

Exit fullscreen mode

Running the example:

A: created
The program failed: Failed to write: A
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "shared.Resource.close()" because "b" is null
        at iter5.Try.main(Try.java:27)

Enter fullscreen mode

Exit fullscreen mode

The A resource does not get closed. To make matters worse, the program additionally exits abruptly with an uncaught exception: a NullPointerException occurred when trying to close the B resource.

The reason it happens is that the B resource is never created as the A resource throws before B can be created. And we deliberately did not include a null-check before closing the resource.

Let’s write the same example using the try-with-resources statement:

public class TryWithResources {
  public static void main(String[] args) throws IOException {
    try (var a = Resource.create("A"); var b = Resource.create("B")) {
      a.throwOnWrite("Try-with-resources statement (iteration 5)");
      b.write("Try-with-resources statement (iteration 5)");
    } catch (IOException e) {
      System.err.println("The program failed: " + e.getMessage());

      return; // explicitly stop execution
    }

    System.out.println("More instructions...");
  }
}

Enter fullscreen mode

Exit fullscreen mode

Results:

A: created
B: created
B: closed
A: closed
The program failed: Failed to write: A

Enter fullscreen mode

Exit fullscreen mode

Both resources are properly closed and the program exits normally.

I am sure that, if we kept looking, we would find more issues. But this should do it for now.

Let’s now see what’s in a try-with-resources statement.

What’s in a try-with-resources statement?

So what is in a try-with-resources statement? Meaning can we rewrite it using the plain try statement?

The answer is yes. In fact that is what the Java compiler does. Others have written about it, so I will not here. For example:

  • Heinz Kabutz writes about it here
  • there is also this StackOverflow answer

Here I will do something slightly different. Before we go into it, let’s first look what the specification says.

Java Language Specification (JLS) 18

At time of writing, Java 18 is the current release of the language. The try-with-resources statement is in section 14.20.3 of the Java language specification. In the first paragraph we read (emphasis mine):

A try-with-resources statement is parameterized with variables (known as resources) that are initialized before execution of the try block and closed automatically, in the reverse order from which they were initialized

So this explains why we had to reverse the order of the close operations in the previous section.

In section 14.20.3.1 we find the specification of the code the compiler must emit:

{
    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();
            }
        }
    }
}

Enter fullscreen mode

Exit fullscreen mode

The specification is recursive on ResourceSpecification_tail when more than one resource is declared. In other words, when two resources are declared, there will be a second try nested in this try. When three resources are declared, there will be a third try nested in the second one. And so on.

We are not trying to write a Java compiler. We just want to understand what happens when a try-with-resources statement is executed.

So, can we write a version of this code in such ways that:

  • produces the same effect, that is, «does the same job»; and
  • is more readable by a Java developer (as opposed to being readable by a JVM)?

Let’s give it a try.

A closing utility

A good initial refactoring candidate is the code in the finally block. This block:

  • closes the resource in a null safe way; and
  • handles the exception that can be thrown during the close operation.

Let’s extract it to a utility class and rewrite slightly:

public final class Close {
  public static Throwable ifPossible(Throwable t, AutoCloseable c) {
    var res = t;

    if (c != null) {
      try {
        c.close();
      } catch (Throwable e) {
        if (res == null) {
         res = e;
        } else {
         res.addSuppressed(e);
        }
      }
    }

    return res;
  }
}

Enter fullscreen mode

Exit fullscreen mode

First there is a null check on the AutoCloseable instance. It is there so the NullPointerException we faced in the «Two resources: exception during the try block» example does not happen.

Next the resource is closed in a try-catch statement. The try-catch is necessary as the close() operation might throw:

  • the Exception it declares it throws;
  • a RuntimeException. For example, a NullPointerException from a programming error;
  • an Error. For example, a StackOverflowError from a programming error.

We saw what happens if we don’t catch this throwable in the «Two resources: error on closing» example.

In the catch block we check if there was a previous exception. This would typically be the exception caught in the main try block. In our examples this would be the one thrown while trying to write to the resource.

Now, if there were such exception it would be a distinct exception than the one thrown during a close operation. By this I mean that, even though in this particular case they might have some relation (both were thrown by the same instance), they do not share a cause effect. In other words, one should not be declared as the cause of the other.

On the other hand, it would be unwise to simply ignore one exception or the other.

Suppressed exception

So that the closing exception is not ignored, Java 7 introduced the concept of suppressed exceptions.

The exception thrown during the close operation is added as a suppressed exception of the previous exception if the latter exists.

If there was no previous exception then the exception thrown during the close operation is simply returned by our utility and it becomes our primary exception.

Rewriting our two resources try examples

Let’s use our new utility class by rewriting the two resources examples.

A happy path examples becomes:

public class Try {
  public static void main(String[] args) {
    Throwable rethrow = null;

    Resource a = null;
    Resource b = null;

    try {
      a = Resource.create("A");
      b = Resource.create("B");

      a.write("Try statement (iteration 6)");
      b.write("Try statement (iteration 6)");
    } catch (Throwable e) {
      rethrow = e;
    } finally {
      rethrow = Close.ifPossible(rethrow, b);
      rethrow = Close.ifPossible(rethrow, a);
    }

    if (rethrow != null) {
      System.err.println("The program failed: " + rethrow.getMessage());

      return; // explicitly stop execution
    }

    System.out.println("More instructions...");
  }
}

Enter fullscreen mode

Exit fullscreen mode

The general recipe is:

  1. begin with a null throwable local variable named rethrow
  2. declare all your resources initialized will null
  3. at the start of the try block, create all your resources
  4. do your work
  5. declare a single catch-all catch block
  6. simply assign the caught throwable to the rethrow variable
  7. in the finally block, using our utility and in the reverse order of the resource creation close all of the resources
  8. if any error occurred, either in the try block or in the finally block, the rethrow variable will be rethrow != null. Handle the error appropriately
  9. if no errors occurred, continue the execution

Running this program results in:

A: created
B: created
A: Try statement (iteration 6)
B: Try statement (iteration 6)
B: closed
A: closed
More instructions...

Enter fullscreen mode

Exit fullscreen mode

Which is equivalent to the output of the following try-with-resources version:

public class TryWithResources {
  public static void main(String[] args) {
    try (var a = Resource.create("A"); var b = Resource.create("B")) {
      a.write("Try-with-resources statement (iteration 6)");
      b.write("Try-with-resources statement (iteration 6)");
    } catch (Throwable e) {
      System.err.println("The program failed: " + e.getMessage());

      return; // explicitly stop execution
    }

    System.out.println("More instructions...");
  }
}

Enter fullscreen mode

Exit fullscreen mode

Both versions are equivalent in the sense that both «do the same job». They are not «bytecode equivalent» however.

The try-with-resources version:

  • is more concise;
  • the closing happens automatically; and
  • the resource variable is scoped to the statement itself.

Additionally, the plain try statement version is error-prone:

  • a good indicator is the fact that the recipe we wrote has nine steps in it.

The remaining examples can be found in the GitHub repo.

Restricting the catch-all to catch IOException only

Did you notice that, in the previous try-with-resources example, we used a catch-all catch clause?

Meaning that we declared it to catch Throwable and not IOException which is the only checked exception declared to be thrown in the statement:

  • by the create operation;
  • by the write operation; and
  • by the close operation.

If using an IDE it would most likely suggest for the clause to catch an IOException only and not a Throwable:

} catch (IOException e) {
  System.err.println("The program failed: " + e.getMessage());

  return; // explicitly stop execution
}

Enter fullscreen mode

Exit fullscreen mode

To make our try example equivalent our single if statement after the try statement would have to be rewritten to:

if (rethrow instanceof RuntimeException re) {
  throw re;
}

if (rethrow instanceof Error err) {
  throw err;
}

if (rethrow instanceof IOException ioe) {
  System.err.println("The program failed: " + ioe.getMessage());

  return; // explicitly stop execution
}

Enter fullscreen mode

Exit fullscreen mode

Meaning that:

If either a RuntimeException or an Error is thrown in either the try block or during the closing of the resources then the program ends abruptly by an uncaught exception.

If an IOException is thrown in either the try block or during the closing of the resources then the program ends normally with the message «The program failed: {exception message}» printed to System.err.

Suppressed exception example

As a final example, let’s see the suppressed exceptions in action:

public class TrySuppressed {
  public static void main(String[] args) throws IOException {
    Throwable rethrow = null;

    Resource a = null;
    Resource b = null;

    try {
      a = Resource.create("A");
      b = Resource.throwOnClose("B");

      a.throwOnWrite("Try statement (suppressed in action)");
      b.write("Try statement (suppressed in action)");
    } catch (Throwable e) {
      rethrow = e;
    } finally {
      rethrow = Close.ifPossible(rethrow, b);
      rethrow = Close.ifPossible(rethrow, a);
    }

    if (rethrow != null) {
      System.err.println("The program failed: " + rethrow.getMessage());

      for (Throwable s : rethrow.getSuppressed()) {
        System.err.println("tsuppressed: " + s.getMessage());
      }

      return; // explicitly stop execution
    }

    System.out.println("More instructions...");
  }
}

Enter fullscreen mode

Exit fullscreen mode

Resource B is created such as it throws on closing. Resource A throws during writing.

Running this example gives:

A: created
B: created
B: close() called
A: closed
The program failed: Failed to write: A
        suppressed: Failed to close: B

Enter fullscreen mode

Exit fullscreen mode

The close operation was invoked in both resources.

And the close exception thrown by B was added as a suppressed exception to the exception thrown by A.

AutoCloseable

Before we go I would like to write a few words on the AutoCloseable type.

As you can see in its Javadocs it was introduced in Java 7.

It marks types that are allowed to be used as resources in try-with-resources statements. It is defined in the try-with-resources section of the Java language specification:

The type of a local variable declared in a resource specification, or the type of an existing variable referred to in a resource specification, must be a subtype of AutoCloseable, or a compile-time error occurs.

Before its introduction there was no standard interface to indicate that a type had a method that had to be invoked in order for resources to be released. Yes, there was the Closeable interface introduced in Java 5.

By the way, Closeable was retrofitted to extend AutoCloseable.

But not all «things that can be closed» implemented it. For instance:

  • java.util.zip.ZipFile. It had a close() method throwing IOException in Java 6. But it did not implement Closeable. Compare the javadocs: Java 6 vs. Java 7
  • java.sql.Connection, java.sql.Statement and java.sql.ResultSet. All had a close() method throwing SQLException in Java 6. But they did not implement a common interface. In Java 7 they all implemented AutoCloseable. Compare the javadocs for the Statement interface: Java 6 vs. Java 7

Conclusion

In this blog post we took a in-depth look in the try-with-resources Java statement.

We saw problems that can arise from the plain try statement when dealing with resources that must be closed programmatically. With this we wanted to understand what issues the try-with-resources was meant to address.

We then presented its formal definition in the Java language specification. We tried to understand it by rewriting it using the plain try statement and a small closing utility. In the process we saw two API changes introduced in Java 7 to support it:

  • suppressed exceptions
  • the AutoCloseable interface

Finally we saw some examples of JDK classes that were modified so they could be used with the try-with-resources statement.

The full source code of the examples listed in this post can be found in this GitHub repository.

From Effective Java 2e by Joshua Bloch,

An example of the sort of situation where it might be appropriate to ignore an
exception is when closing a FileInputStream. You haven’t changed the state of
the file, so there’s no need to perform any recovery action, and you’ve already
read the information that you need from the file, so there’s no reason to abort the
operation in progress. Even in this case, it is wise to log the exception, so that you
can investigate the matter if these exceptions happen often.

Is it wise to always ignore that exception, as suggested. The code then would look like,

FileInputStream stream = null;
try {
    stream = new FileInputStream("foo.txt");
    // do stuff
} finally {
    if (null != stream) {
        try {
            stream.close();
        } catch (IOException e) {
            LOG.error("Error closing file. Ignoring this exception.", e);
        }
    }

which is much more verbose than

try (FileInputStream stream = new FileInputStream("foo.txt")) {
    // do stuff
}

But is the extra verbosity worth ignoring the exception from closing the file? Is it better to ignore the exception since there’s nothing to be done? Should the same approach be used with resources other than a file, such as a database connection?

asked Dec 30, 2017 at 11:52

Max's user avatar

4

The reason, which as I recall Bloch explains, is that the interesting Exception is the original one thrown from within the try block. Any Exception thrown within the finally block will hide the original Exception.

Imagine that the ur-Exception is because the File is on a network and the network is down. That’s the more informative Exception that you want to throw. But that problem also affects the close() , and you don’t want the 2nd exception to «get in the way».

Note this explanation was in V1 of his book…

Added The following code proves the point:

try {
  throw new Exception("in try, here is the ur-Exception");
}
finally {
  throw new Exception("in finally");
}

The second, «in finally» Exception, is the one you will see, hiding the original Exception.

answered Dec 31, 2017 at 17:15

user949300's user avatar

user949300user949300

8,6352 gold badges26 silver badges35 bronze badges

For argument’s sake, I can’t imagine how this operation could fail, and what could go wrong if it fails.

Therefore, I would do nothing except logging in production. And make sure that the app runs into a breakpoint when running on a developers machine. If this ever fails, I want to know about it, and why it happened, so I can figure out more correctly how to handle it.

answered Dec 31, 2017 at 15:42

gnasher729's user avatar

gnasher729gnasher729

40.9k4 gold badges56 silver badges115 bronze badges

3

In general you should expect your program to run without ever throwing an exception.

Therefore, when an exception is thrown, its unusual that you would want to ignore it.

However, it’s very usual that you don’t want your program to crash. So people put top level try catch do nothing blocks to stop stuff they haven’t thought of from crashing their applications completely.

In this case though really you do want to log the exception you hadn’t thought of, so that you can update your code to deal with it properly.

In the FileInputStream example, yes it’s not really clear why this would ever throw an exception (perhaps the file has been deleted?) or what you would do about it if it did.

Potentially I suppose if the close fails to complete successfully you might have a memory leak. So if its happening a lot you will want to know about it.

answered Dec 30, 2017 at 13:44

Ewan's user avatar

EwanEwan

69.6k5 gold badges76 silver badges159 bronze badges

12

then and now, very sometimes. Yes. But always? By God — never!

First note: you should be careful about over-generalizing a rule from a precise example.

Compare

void close(Closable target) {
    try {
        target.close();
    } catch (IOException e) {
        ???
    }
}

We don’t have nearly enough information to dismiss this exception as unimportant. There are many exceptional situations which prevent a resource from closing successfully.

void close(FileInputStream target) {
    try {
        target.close();
    } catch (IOException e) {
        ???
    }
}

Here, we have the additional context that we are reading data (no worries about being corrupted), and that the caller had already obtained the expected data (the caller is invoking close()). In other words, although we are in an exceptional situation, the circumstances don’t indicate that the caller will be unable to ensure its explicit postcondition.

We shouldn’t confuse a case where we accrue business value but leak a file handle with a case where there are indications of actual data loss.

I personally wouldn’t get too hung up on verbosity; you can use another Closable to do the work

try( ClosePolicy closer = new LoggingClosePolicy(new FileInputStream(...))) {
    FileInputStream stream = closer.get();
    // ...
}

which I think is a bit better on the intention revealing scale: the default strategy for exceptions during close isn’t satisfactory, so we are replacing it.

With that debris cleared, I think you can then start to address your original question: should we always…? It’s a checked exception, so we cannot passively ignore the possibility.

I see two possibilities; if the local method cannot establish its postcondition in the face of the exception, then I think you need to propagate it in some form.

But I think it’s much more common that the local method will be able to establish the observable portions of the post condition, in which case log-and-move-on seems like a reasonable way to accrue business value when the exceptional circumstances don’t require immediate remediation.

answered Dec 31, 2017 at 16:45

VoiceOfUnreason's user avatar

VoiceOfUnreasonVoiceOfUnreason

31.6k2 gold badges42 silver badges77 bronze badges

1

Note that this problem is not limited to the Java language, or for that matter languages that have exceptions. You can face exactly the same problem when programming in C. Do you check the return value from fclose()? Or from close()?

I have seen both approaches: check and not check. Personally, I try to code finalizers so that they don’t return an error code, just silently ignoring all errors. In some cases when I’m interested about catching bugs, I fail early in a big way, by calling abort() if I see that some important precondition is violated. The core dump left behind explains everything. Needless to say, in the final program the abort() function is never called. Don’t use abort() if e.g. a network connection or some external resource fails; it’s only for preconditions!

In the case of files, you probably won’t fail closing it. But however, in the case of some network streams, there may be perhaps an error condition.

Also, you could consider logging the exception and then ignoring it silently. If you see lots of such exceptions in the logs, you can turn off logging for the biggest log size offenders.

answered Dec 31, 2017 at 17:02

juhist's user avatar

juhistjuhist

2,56110 silver badges14 bronze badges

To me there’s one simple rule about exceptions:

If a method doesn’t fulfill its contract, it throws an exception.

Now there are explicit and implicit aspects of a method’s contract. The explicit contract typically (hopefully) correlates with its name. Implicit contract parts can be leaving the file system in the same state as before the method call, not creating resource leaks, and so on. It’s a design decision, which implicit duties a given method has.

What’s the contract of your method?

  • E.g. to read some data from a configuration file.

What’s the effect of a failure when closing the stream?

  • The file has been read successfully, so the explicit contract has been fulfilled.
  • Failing to close a FileInputStream results in some resource leak. For a single configuration file read once in the application’s run-time, that’s hardly a problem, so typically doesn’t violate the implicit contract.
  • Not closing a FileInputStream might (depending on the environment: OS, file system, …) leave a lock on the file, inhibiting later modification by some other process. As configuration files typically aren’t modified often (and in our case need an application restart when modified), that also is acceptable behaviour of our read method.

So yes, in this case of reading a single configuration file, I’d log the exception with a WARN level and return without an exception.

But if the method were the fileCopy() function of a file manager application, then not closing the source file with the risk to leave a lock on the file would definitely violate the implicit contract, so needing an exception.

answered Jan 1, 2018 at 13:25

Ralf Kleberhoff's user avatar

The 3rd edition of Effective Java contains a new section with the title «Prefer try-with-resources to try-finally». In it, Bloch writes, «Always use try-with-resources in preference to try-finally when working with resources that must be closed,» and provides a rationale which includes the 3 following points:

  • An exception in the finally block will hide the exception in the try block, exactly as user949300 describes in his answer.

  • try-finally is difficult to read when used with more than one resource.

  • try-finally is often used incorrectly. «In fact, two-thirds of the uses of the close method in the Java libraries were wrong in 2007.»

So to answer the question, Bloch seems to think that try-with-resources is worth it.

answered Mar 5, 2018 at 19:01

Max's user avatar

MaxMax

2672 silver badges8 bronze badges

2

Bloch proposes to swipe the garbage under the rug and hide it from the user by pretending it is not there, which I am certain an honest programmer (or program) will never do. I should make the program fail and crash real hard in on order to detect all possible errors at their first occurrences. Error recovery is another matter, though.

There also seems to be a redundancy in your code—If you create the stream outside the try block, you will not have to check whether it is null in the finally because it will always have been created.

answered Dec 30, 2017 at 19:10

Anton Shepelev's user avatar

Вступление

try-with-resources — это один из нескольких try в Java, призванный
освободить разработчиков от обязанности освобождать ресурсы,
используемые в блоке try

Первоначально он был представлен в Java 7, и вся идея заключалась в том,
что разработчику не нужно беспокоиться об управлении ресурсами для
ресурсов, которые они используют только в одном блоке
try-catch-finally. Это достигается за счет устранения необходимости в
finally , которые на практике разработчики использовали только для
закрытия ресурсов.

Кроме того, код, использующий try-with-resources , часто чище и
читабельнее, что упрощает управление кодом, особенно когда мы имеем дело
со многими блоками try

Синтаксис

Синтаксис try-with-resources почти идентичен обычному синтаксису
try-catch-finally. Единственное отличие — это скобки после try в
которых мы объявляем, какие ресурсы мы будем использовать:

 BufferedWriter writer = null; 
 try { 
 writer = new BufferedWriter(new FileWriter(fileName)); 
 writer.write(str); // do something with the file we've opened 
 } catch (IOException e) { 
 // handle the exception 
 } finally { 
 try { 
 if (writer != null) 
 writer.close(); 
 } catch (IOException e) { 
 // handle the exception 
 } 
 } 

Тот же код, написанный с использованием try-with-resources, будет
выглядеть так:

 try(BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))){ 
 writer.write(str); // do something with the file we've opened 
 } 
 catch(IOException e){ 
 // handle the exception 
 } 

То, как Java понимает этот код:

Ресурсы, открытые в круглых скобках после оператора try, понадобятся
только здесь и сейчас. Я вызову их .close() как только закончу
работу с блоком try. Если в блоке try возникает исключение, я все
равно закрою эти ресурсы.

До того, как этот подход был введен, закрытие ресурсов выполнялось
вручную, как было показано в коде ранее. По сути, это был шаблонный код,
и кодовые базы были завалены им, снижая удобочитаемость и усложняя
поддержку.

catch и, finally часть try-with-resources работают должным
образом, при этом catch обрабатывают соответствующие исключения, а
finally выполняется независимо от того, было исключение или нет.
Единственное отличие — это подавленные исключения, которые описаны в
конце этой статьи.

Примечание . Начиная с Java 9, объявлять ресурсы в операторе
try-with-resources необязательно. Вместо этого мы можем сделать что-то
вроде этого:

 BufferedWriter writer = new BufferedWriter(new FileWriter(fileName)); 
 try (writer) { 
 writer.write(str); // do something with the file we've opened 
 } 
 catch(IOException e) { 
 // handle the exception 
 } 

Множественные ресурсы

Еще один хороший аспект try-with-resources — это простота добавления /
удаления ресурсов, которые мы используем, с уверенностью, что они будут
закрыты после того, как мы закончим.

Если бы мы хотели работать с несколькими файлами, мы открывали файлы в
try() и разделяли их точкой с запятой:

 try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName)); 
 Scanner scanner = new Scanner(System.in)) { 
 if (scanner.hasNextLine()) 
 writer.write(scanner.nextLine()); 
 } 
 catch(IOException e) { 
 // handle the exception 
 } 

Затем Java заботится о том, чтобы вызвать .close() для всех ресурсов,
которые мы открыли в try() .

Примечание : они закрываются в обратном порядке объявления, что
означает, что в нашем примере scanner будет закрыт раньше writer .

Поддерживаемые классы

Все ресурсы, объявленные в try() должны реализовывать интерфейс
AutoCloseable Обычно это различные типы писателей, читателей, сокетов,
выходных или входных потоков и т. Д. Все, что вам нужно для написания
resource.close() после того, как вы закончите с ним работать.

Это, конечно, включает определяемые пользователем объекты, реализующие
интерфейс AutoClosable Тем не менее, вы редко встретите ситуацию,
когда вы захотите написать свои собственные ресурсы.

В случае, если это произойдет, вам необходимо реализовать
AutoCloseable или Closeable (только там, чтобы сохранить обратную
совместимость, предпочитайте AutoCloseable ) и переопределите метод
.close() :

 public class MyResource implements AutoCloseable { 
 @Override 
 public void close() throws Exception { 
 // close your resource in the appropriate way 
 } 
 } 

Обработка исключений

Если исключение выбрасывается из блока Java try-with-resources , любой
ресурс, открытый в круглых скобках try все равно будет автоматически
закрыт.

Как упоминалось ранее, try-with-resources работает так же, как
try-catch-finally , за исключением одного небольшого дополнения.
Добавление называется подавленными исключениями . Не надо понимать
подавленные исключения для того , чтобы использовать примерочных
с-ресурсами,
но читать о них может быть полезно для отладки , когда
ничего не похоже на работу.

Представьте себе ситуацию:

  • По какой-то причине в блоке try-with-resources возникает
    исключение
  • Java останавливает выполнение в блоке try-with-resources и
    вызывает .close() для всех ресурсов, объявленных в try()
  • Один из .close() вызывает исключение
  • Какое исключение « catch блок catch?

Эта ситуация знакомит нас с вышеупомянутыми подавленными исключениями.
Подавленное исключение — это исключение, которое в некотором смысле
игнорируется, когда оно создается в неявном блоке finally блока
try-with-resources , в случае, когда исключение также генерируется из
блока try

Эти исключения являются исключениями, которые возникают в .close() и
доступ к ним отличается от «обычных» исключений.

Важно понимать, что порядок исполнения такой:

  1. блок try-with-resources
  2. неявный, наконец
  3. блок catch (если исключение было сгенерировано в [1] и / или
    [2])
  4. (явный) наконец

Например, вот ресурс, который ничего не делает, кроме исключения:

 public static class MyResource implements AutoCloseable { 
 // method throws RuntimeException 
 public void doSomething() { 
 throw new RuntimeException("From the doSomething method"); 
 } 
 
 // we'll override close so that it throws an exception in the implicit finally block of try-with-resources (when it attempts to close our resource) 
 @Override 
 public void close() throws Exception { 
 throw new ArithmeticException("I can throw whatever I want, you can't stop me."); 
 } 
 } 
 
 public static void main(String[] arguments) throws Exception { 
 // declare our resource in try 
 try (MyResource resource = new MyResource()) { 
 resource.doSomething(); 
 } 
 catch (Exception e) { 
 System.out.println("Regular exception: " + e.toString()); 
 
 // getting the array of suppressed exceptions, and its length 
 Throwable[] suppressedExceptions = e.getSuppressed(); 
 int n = suppressedExceptions.length; 
 
 if (n > 0) { 
 System.out.println("We've found " + n + " suppressed exceptions:"); 
 for (Throwable exception : suppressedExceptions) { 
 System.out.println(exception.toString()); 
 } 
 } 
 } 
 } 

Этот код работоспособен. Вы можете использовать его, чтобы
поэкспериментировать с использованием нескольких MyResource или
посмотреть, что происходит, когда try-with-resources не генерирует
исключение, а .close() делает.

Подсказка : внезапно стали важны исключения, возникающие при
закрытии ресурсов.

Важно отметить, что если ресурс выдает исключение при попытке закрыть
его, любые другие ресурсы, открытые в том же блоке try-with-resources,
все равно будут закрыты.

Еще один факт, который следует отметить, заключается в том, что в
ситуации, когда try не генерирует исключение и когда возникает
несколько исключений при попытке использовать .close() используемые
ресурсы, первое исключение будет распространяться вверх по стеку
вызовов, а остальные будут подавлены.

Как вы можете видеть в коде, вы можете получить список всех подавленных
исключений, Throwable массиву Throwable.getSuppressed() .

Помните, что внутри блока try может быть создано только одно исключение.
Как только возникает исключение, код блока try завершается, и Java
пытается закрыть ресурсы.

Заключение

По возможности следует использовать try-with-resources вместо
обычного try-catch-finally
. Легко забыть закрыть один из ваших
ресурсов после нескольких часов написания кода или забыть закрыть
ресурс, который вы только что добавили в свою программу, после
случайного всплеска вдохновения.

Код более читабелен, его легче изменять и поддерживать, и, как правило,
он короче.

Понравилась статья? Поделить с друзьями:
  • Try except python код ошибки
  • Try except python как вывести строку ошибки
  • Try except python вывод ошибки
  • Try except python все ошибки
  • Try catch php обработка ошибок