6

I run this code :

public static void main(String[] args) {
    System.out.println(catcher());
}

private static int catcher() {
    try {
        System.out.println("TRY");
        thrower();
        return 1;
    } catch (Exception e) {
        System.out.println("CATCH");
        return 2;
    } finally {
        System.out.println("FINALLY");
        return 3;
    }
}

private static void thrower() {
    throw new RuntimeException();
}

and I expect to see this at output:

TRY
CATCH
FINALLY
2

but surprisingly the output is:

TRY
CATCH
FINALLY
3

I'm confused. where goes return 2 statement? Is return at finally a bad-practice?

Farvardin
  • 5,336
  • 5
  • 33
  • 54

9 Answers9

4

where goes return 2 statement?

It's gone.

Specifically what the JLS says is:

If the catch block completes abruptly for reason R, then the finally block is executed. 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).

(A return statement is a cause for abrupt completion.)

Is return at finally a bad-practice?

Basically yes. Because there is no good reason for doing it and it will eat exceptions.

For example observe the following:

static void eatAnException() {
    try {
        throw new RuntimeException("oops");
    } finally {
        return;
    }
}

The return statement in finally discards the exception. Instead, place a return statement after a finally block.

Community
  • 1
  • 1
Radiodef
  • 37,180
  • 14
  • 90
  • 125
2

That is not surprising.

It is actual behavior. Returning value decided at finally block.

If you are not returning anything in finally then the previous value of going to be return value is the returning value.

You can see that from byte code that the return value will going to be update in finally block if you are returning there.

As a side note:

the finally block is meant for a special purpose.

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.

Writing your logic inside it is not recommended.

Suresh Atta
  • 120,458
  • 37
  • 198
  • 307
1

Oracle documentation (http://docs.oracle.com/javase/tutorial/essential/exceptions/finally.html) states:

"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."

Dmitry Nikiforov
  • 2,988
  • 13
  • 12
1

Anything that is returned in the finally block will actually override any exception or returned value that is inside the try/catch block

The finally block will not be called after return in a couple of unique scenarios: if System.exit() is called first, or if the JVM crashes.

This is why it is considered to be a very bad idea to have a return statement inside the finally block.

Mostafa Jamareh
  • 1,389
  • 4
  • 22
  • 54
1

In the JSE7 Language Specification §14.1 a return statement is defined as an abrupt termination. If your finally block completes abruptly (your return) the try block ends for the same reason (as defined in §14.20.2):

§14.1 [...] An abrupt completion always has an associated reason, which is one of the following: [...] A return with no value [...] A return with a given value [...]

§14.20.2 [...] If the finally block completes abruptly for reason S, then the try statement completes abruptly for reason S (and reason R is discarded). [...] (reason R is the result of catch).

steffen
  • 16,138
  • 4
  • 42
  • 81
0

The reason for that behavior is that finally block is always executed, so you can't end method execution on the catch block, when there is a return in the finally block.

So what is actually happening step-by-step:

  1. TRY goes to output
  2. thrower() method is executed
  3. It throws new RuntimeException()
  4. catch block in the catcher() catches it
  5. CATCH goes to output
  6. finally block is executed
  7. FINALLY goes to the output
  8. And the method returns 3 - you can't go back to the catch block
Eel Lee
  • 3,513
  • 2
  • 31
  • 49
0

The compiler inserts "finally" part (as a call of subroutine, usually) into all branches before return ("when the try block exits"). This means your method is being transformed like this:

private static int catcher() {
    try {
        System.out.println("TRY");
        thrower();

        // -- finally section --
        System.out.println("FINALLY");
        return 3;
        // ---------------------

        return 1; // will be eliminated by the compiler
    } catch (Exception e) {
        System.out.println("CATCH");

        // -- finally section --
        System.out.println("FINALLY");
        return 3;
        // ---------------------

        return 2; // will be eliminated by the compiler
    }
}

That's why you see

TRY CATCH FINALLY 3

Is return at finally a bad-practice?

Yes, in theory. You can use "return" in "finally" if you understand how it works :) But it's better to use "finally" to release resources only.

And one more interesting case:

private static int catcher2() {
    int v = 1;
    try {
        System.out.println("TRY");
        thrower();
        return v;
    } catch (Exception e) {
        System.out.println("CATCH");
        v = 2;
        return v;
    } finally {
        System.out.println("FINALLY");
        v = 3;
    }
}

It returns

TRY CATCH FINALLY 2

because in the byte-code the last operation before return (ireturn/"when the try block exits") is iload (loading a value saved in stack, which has "2" by this moment)

AnatolyG
  • 1,557
  • 8
  • 14
0

The finally block gets called irrespective of whether an exception is thrown or not. Here after the catch block the flow returns 2 to finally block. Again the finally block returns 3 to main program.

And more over you expect "finally" to be printed. The return statement which is immediately next to finally is 3. Then how do you expect 2 to be returned.

user3256147
  • 378
  • 2
  • 9
0

If the finally block executes a control transfer statement such as, a return or a labeled break, then a value returned by a return statement in the finally block will supersede any value returned by a return statement in the try block or a catch block.

Pankaj Shinde
  • 3,361
  • 2
  • 35
  • 44