Pregunta

According to Java Language Specification, Section §14.20.2

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 normally, then the finally block is executed, and then there is a choice:
    • If the finally block completes normally, then the try statement completes normally.
    • If the finally block completes abruptly for reason S, then the try statement completes abruptly for reason S

If I interpret it correctly then after execution of try block finally is invoked, But how all this works and why I got the output,

public static int TestTryFinallyBlock()  
{
    int  i =0;
    try 
    {
        i= 10; //Perform some more operation
        return i;
    }       
    finally
    {
        i = 40; 
    }
}

public static void main( String[] args )
{
    int i1 = TestTryFinallyBlock(); //Here the output was 10 not 40
}   

I want to know how this thing produced output 10.

Is that when try block is executed and return statement is encountered the output value is already pushed to stack, and then the finally block is executed

I know that return is encountered first then finally blocks runs so output is 10, but How jvm interpret or how the try finally block is handled or converted by jvm?
Is that jvm uses GOTO section jump section to go to finally section or the stack is already maintained?

¿Fue útil?

Solución

After a little search and seeing what byte codes were generated, I found that actually there are no finally blocks as it seems and no jump or goto statements generated by JVM.
The above code is translated something as (if I interpret byte code correctly, If I am wrong please do correct me)

public static int TestTryFinallyBlock()  
{
  int returnValue; //A temporary return variable
  try
  {
     int  i = 0;     
     i = 10; 
     returnValue = i; 
     i = 40; 
     return returnValue;    
  }
  catch (RuntimeException e)
  {
       i = 40; //finally section code id copied here too
       throw e;
  }
}

Point to Note: If 'i' would have been a reference to a mutable class object and the contents of the object were changed in the finally block, then those changes would have been reflected in the returned value too.

Otros consejos

Compiling Finally

Compilation of a try-finally statement is similar to that of try-catch. Prior to transferring control outside the try statement, whether that transfer is normal or abrupt, because an exception has been thrown, the finally clause must first be executed. For this simple example:

void tryFinally() {
    try {
        tryItOut();
    } finally {
        wrapItUp();
    }
}

the compiled code is:

Method void tryFinally()
0   aload_0             // Beginning of try block
1   invokevirtual #6    // Method Example.tryItOut()V
4   jsr 14              // Call finally block
7   return              // End of try block
8   astore_1            // Beginning of handler for any throw
9   jsr 14              // Call finally block
12  aload_1             // Push thrown value
13  athrow              // ...and rethrow value to the invoker
14  astore_2            // Beginning of finally block
15  aload_0             // Push this
16  invokevirtual #5    // Method Example.wrapItUp()V
19  ret 2               // Return from finally block
Exception table:
From    To      Target      Type
0       4       8           any

There are four ways for control to pass outside of the try statement: by falling through the bottom of that block, by returning, by executing a break or continue statement, or by raising an exception.

To know more about how javac interprets the finally block .Please refer JLS - 3.13. Compiling finally

When you enter the return, the method is already prepared to return 10. 10 is on the stack as the return value. The finally block is executed and sets i to 40--but that i is not the same location as the return value. Now if there's a side effect like:

public static int TestTryFinallyBlock()  
{
    int  i =0;
    try 
    {
        i= 10; //Perform some more operation
        return i;
    }       
    finally
    {
        i = 40; 
        System.out.println("local: "+i);
    }
}

40 will be printed.

This is because of the way the return statement function and the way it interact with a try with finally statement.

The Section §14.17 of the JLS describe the return statement.

A return statement with an Expression attempts to transfer control to the invoker of the method that contains it; the value of the Expression becomes the value of the method invocation. More precisely, execution of such a return statement first evaluates the Expression. If the evaluation of the Expression completes abruptly for some reason, then the return statement completes abruptly for that reason. If evaluation of the Expression completes normally, producing a value V, then the return statement completes abruptly, the reason being a return with value V.

The last sentence indicates that if the expression of the return statement is evaluated normally, then the return statement completes abrutptly. In your example, the try block terminates abrutply because of the return statement and the reason being a return value 10 (i is evaluated to value 10).

Since in your example the return is in a try with finally block, Section §14.20.2 of the JLS tells us what happens next:

  • 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 bruptly for reason S (and reason R is discarded).

So, since the try block terminated abruptly because of the return statement being evaluated to value 10, and because the finally block completes normally, then the method returns 10.

Now the value of i is 40 but you are not getting 40, because you got(returned) the value before making it 40.

it's like

i=10
return i
i=40
if return here,you get 40

Though not a good practice, Just for demo.

public static int TestTryFinallyBlock()  
    {
        int  i =0;
        try 
        {
            i= 10; //Perform some more operation

        }       
        finally
        {
            i = 40;
            return i;
        }
    }

Now the value of 40 you'l get in i.

*As a side note:*never write any business logic other than resource clean up. That's kills readability and causes bugs.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top