21

Today in college we talked a little bit about try, catch and finally. I got confused about these two examples:

PrintWriter out = null;
try {
  out = new PrintWriter(...); // We open file here
} catch (Exception e) {
  e.printStackTrace();
} finally { // And we close it here
  out.close();
}

What is the difference between closing the file in finally and if we just did it this way:

PrintWriter out = null;
try {
  out = new PrintWriter(...); // We open file here
} catch (Exception e) {
  e.printStackTrace();
}
out.close();

This piece of code after catch will always execute.

Can you give me some good examples about the differences between when we use finally and when we put the code after catch? I know that finally will always execute, but the program will also keep running after catch block.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Miljan Rakita
  • 1,433
  • 4
  • 23
  • 44
  • 3
    What if your catch did something like throw another (possibly unchecked) exception? Would `out.close()` get run in that situation? In other words, just printing the stack trace and moving on is not always how an exception is handled. – rmlan Apr 26 '16 at 13:10
  • 3
    If you re-throw the exception, or do not catch it all, then the finally block is your friend. There are also Throwable conditions that are not Exceptions, and then the 2nd example would be problematic. – KevinO Apr 26 '16 at 13:10
  • I am about to start to learn about Throwable, so i guess i will need to learn it first to understand this, right ? – Miljan Rakita Apr 26 '16 at 13:11
  • @Hackerdarshi Couldnt find this post, when i was posting this. Thanks though ! – Miljan Rakita Apr 26 '16 at 13:13
  • 3
    Also: catch `Exception` is usually a bad idea. You should catch the most specific exception types you can. – Andy Turner Apr 26 '16 at 13:17
  • 1
    No one is talking about [AutoCloseable](https://docs.oracle.com/javase/8/docs/api/java/lang/AutoCloseable.html) !! – Not a bug Apr 26 '16 at 13:24
  • @AndyTurner Yeah, i know. Today we learned about Exceptions. This was only used to explain my real problem. – Miljan Rakita Apr 26 '16 at 20:16

8 Answers8

33

It would still make difference if the code throws Error. This is not caught within the code and therefore any part after try/catch/finally wouldn't be caught. If it's part of finally, it will be still executed even for Error.

Secondly, if for whatever reason e.printStackTrace() throws an exception (although it would be very rare), the same will happen - finally will be still executed.

In general finally is very safe way for releasing resources no matter what happens. Even more safe is try-with-resources supported since Java 7 as it could easily manage possibly multiple exceptions thrown during close operations. In this example it would look like:

try (PrintWriter out = new PrintWriter(...)) {
    // do whatever with out
}
catch (Exception e) {
    e.print... (whatever)
}
// no need to do anything else, close is invoked automatically by try block

EDIT: Also note that your code is not really correct (no matter which version). If the PrintWriter constructor throws an exception, the line out.close() will fail on NullPointerException.

Zbynek Vyskovsky - kvr000
  • 18,186
  • 3
  • 35
  • 43
  • Didn't know that errors could happen in catch block. Thanks, this makes sense now. – Miljan Rakita Apr 26 '16 at 13:16
  • 16
    @MiljanRakita Errors can happen anywhere. Its part of the fun! – Gusdor Apr 26 '16 at 15:02
  • 2
    Keep in mind that `finally` will *not* be executed in truly catastrophic situations (say, a power failure), so don't count on it for things like maintaining database transaction consistency. – Mark Apr 26 '16 at 22:03
3

If an uncaught error occurs within the try block, or even if an error occurs within your catch block, the 'piece of code' after the catch won't be executed, but the finally block will.

finally will always be executed.

From the Java documentation :

The finally block always executes when the try block exits. This ensures that the finally block is executed even if an unexpected exception occurs. But finally is useful for more than just exception handling — it allows the programmer to avoid having cleanup code accidentally bypassed by a return, continue, or break. Putting cleanup code in a finally block is always a good practice, even when no exceptions are anticipated.

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
Guillaume Barré
  • 4,168
  • 2
  • 27
  • 50
2

What if something in catch block will throw Exception ? out.close will not execute. You can also use "try with resources" to make sure that all resources are closed after it's used. Try this example:

public static void withFinnaly() {
        try {
            throwException();
            System.out.println("This won't execute");
        } catch (Exception e) {
            System.out.println("Exception is caught");
            throwException();
        } finally {
            System.out.println("Finally is always executed," +
                    " even if method in catch block throwed Exception");
        }
    }

    public static void withOutFinnaly() {
        try {
            throwException();
            System.out.println("This won't execute");
        } catch (Exception e) {
            System.out.println("Exception is caught");
            throwException();
        }
        System.out.println("Looks like we've lost this... " +
                "This wont execute");
    }

    public static void throwException() throws RuntimeException {
        throw new RuntimeException();
    }
RichardK
  • 3,228
  • 5
  • 32
  • 52
2

The normal usecase for finally is when you don't want to catch the exception in the same method.

in that case you use a try with finally block without having a catch. That way you can ensure that your resources are closed without having to catch the exception in the method itself.

MTilsted
  • 5,425
  • 9
  • 44
  • 76
2

In Java, the source code:

void foo()
{
  try {
    if (W())
      return;
  }
  catch (FooException ex) {
    if (X())
      throw;
  }
  finally {
    Y();
  }
  Z();
}

will be converted by the compiler into:

void foo()
{
  try {
    if (W()) {
      Y();
      return;
    }
  }
  catch (FooException ex) {
    if (X()) {
      Y();
      throw;
    }
  }
  catch {
    Y();
    throw;
  }
  Y();
  Z();
}

The effect is to cause the code within the Finally block to be duplicated at all of the places where control might leave the method. Any try block which has a finally but not catch-all handler is equivalent to one with a catch-all handler that immediately throws (at which the processor could then insert a copy of the finally code before the catch-all handler.

supercat
  • 77,689
  • 9
  • 166
  • 211
1

Your second example can throw an unwanted NullPointerException if an exception occurs in the contructor of PrintWriter.

Another possibility is that out.close(); will throw an error, which is not caught.

If you move your code into a finally block, it will always be executed - no matter wether the try block succeeds or not. This is especially useful if your try-block raises an exception that is not caught. In your second example, this will lead to out.close() not being executed, while with a finally block, it would be executed even if the try block throws an uncaught error.

Polygnome
  • 7,639
  • 2
  • 37
  • 57
1

Though not complete answers themselves, these two examples of try-finally (mis)use may be enlightening:

public class JavaApplication3 
{
    static int foo()
    {
        try
        {
            return 6;
        }
        finally
        {
            return 4;
        }

    }

    public static void main(String[] args)
    {
        System.out.println("foo: " + foo());
    }
}


public class JavaApplication3 
{
    static int foo()
    {
        try
        {
            throw new Exception();
        }
        finally
        {
            return 4;
        }

    }
    public static void main(String[] args)
    {
        System.out.println("foo: " + foo());
    }
}

Both programs output 4.

The reason for this can be found on Chapter 14.20.2 of JLS

A try statement with a finally block is executed by first executing the try block. Then there is a choice:

• If execution of the try block completes abruptly because of a throw of a value V, then there is a choice:
[...]
– If the run-time type of V is not assignment compatible with a catchable exception class of any catch clause of the try statement, then the finally block is executed. Then there is a choice:
[...]
If the finally block completes abruptly for reason S, then the try statement completes abruptly for reason S (and the throw of value V is discarded and forgotten).

If execution of the try block completes abruptly for any other reason R, then the finally block is executed, and then there is a choice:
– If the finally block completes normally, then the try statement completes abruptly for reason R.
If the finally block completes abruptly for reason S, then the try statement completes abruptly for reason S (and reason R is discarded).

Editing mine

Consider that a return is an abrupt way of completing a try or finally block.

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
Margaret Bloom
  • 41,768
  • 5
  • 78
  • 124
0

A smaller example:

PrintWriter out = null;
try {
    out = new PrintWriter();
    out.print(data);
} finally {
    out.close();
}

Here, we don't catch any exceptions (that's handled by the caller), but we do want to close the writer whether we

  • operate normally through the try block, or
  • leave through an exception.

In both cases, the code in finally is executed as part of leaving the block.


Here, we catch a subset of exceptions:

PrintWriter out = null;
try {
    out = new PrintWriter();
    out.print(data);
} catch (IOException e) {
    log(e);
} finally {
    out.close();
}
do_something_else();

Here, there are three possible paths:

  • normal execution of the try part, followed by the finally, and then do_something_else(),
  • the try part throws IOException, which is caught and logged, then the finally block runs, and then do_something_else(), or
  • the try part throws a different throwable, which is not caught, but the finally block runs, then the code jumps to the next enclosing try, possibly in the caller.

Do take care when writing your finally block - it's not inside the try, so any exceptions there will pre-empt any exception that's in progress. The short advice is to try to avoid things that might throw from inside finally or catch.

Toby Speight
  • 27,591
  • 48
  • 66
  • 103