2

As a rule, the finally block is always executed whether or not an exception is thrown in the try block or a continue, break or return statement encounters in the try block itself.

Accordingly, the return value of the method in the code snippet given below should be changed but it doesn't.

final class Product
{
    private String productName=null;

    public Product(String productName)
    {
        this.productName=productName;
    }

    public String getProductName()
    {
        try
        {
            return this.productName;
        }
        finally
        {
            this.productName="The product name has been modified in the finally block.";
            System.out.println("Product name in the finally block : "+this.productName);
        }
    }
}

public final class Test
{
    public static void main(String...args)
    {
        System.out.println(new Product("Intel core i3").getProductName());
    }
}

The method getProductName() simply returns the product name which is assigned by the constructor to the field productName before this method is invoked.

Before this method returns, the value of productName is modified in the finally block. Therefore, the method should return this new modified value but it returns the initial value which is assigned to productName when an instance of this class has been constructed.


It produces the following output:

Product name in the finally block : The product name has been modified in the finally block.
Intel core i3

Why doesn't this method return the new value which is assigned to the productName field in the finally block? It should return the new value. Shouldn't it?

Lion
  • 18,729
  • 22
  • 80
  • 110
Tiny
  • 27,221
  • 105
  • 339
  • 599
  • 3
    I regularly visit your profile and see that you ask good questions. Understanding this much stuff at this stage in Java (including advanced Java frameworks and enterprise Java) is not so easy. Keep up good work. – Lion Oct 14 '13 at 13:28

2 Answers2

5

The key here is that you are returning a reference value to the respective object from your method. And in the caller side, what you get is the copy of the reference of the method. Remember, everything in Java are passed by value. Even the references are passed by value.

When you modify the object using the reference in the finally block, the changes will be visible on the caller side. But, if you change the value of the reference itself, then the changes would of course not be reflected on the caller side, as now both of them have a different references.

Before this method returns, the value of productName is modified in the finally block.

What you have done in the finally block is, you've assigned a new String object to the productName reference. That will change the the reference value of productName. And thus as per the above paragraph, the changes wouldn't be reflected on the caller end. Both of them are dealing with 2 different String objects.

Therefore, the method should return this new modified value.

No, it shouldn't. Because both the String objects are different alltogether. Try this code with a StringBuilder object:

public StringBuilder getProductName() {

    StringBuilder sb = new StringBuilder(productName);
    try {
        return sb;
    }
    finally {
        sb.append("Modified");
    }
}

Now the changes to the StringBuilder will be reflected in the return value. So what is happening here?

First with the following return statement:

return sb;

you returned a reference to the StringBuilder object you created. Then in the finally block:

sb.append("Modified");

.. you've modified the object pointed to by sb. Since you haven't changed the reference value of sb itself, the changes to the object will be visible for all the references pointing to that object, and hence the one at the caller end too.

Now modify the finally block as:

sb = new StringBuilder("Modified");

and see whether the change is reflected or not. You will not see any change in return value. Because, now you have changed the reference value of sb itself.

Further, if you return the modified reference from the finally block, the original returned value would be overwritten. Try modifying your finally block to:

finally {

    this.productName="The product name has been modified in the finally block.";
    System.out.println("Product name in the finally block : "+this.productName);

    return this.productName;  // Note: This is bad idea
}

Now you will see the new value.


See also:

Community
  • 1
  • 1
Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
  • This `StringBuilder` example is nice to understand. Thanks. – Tiny Oct 14 '13 at 13:17
  • The first code snippet demonstrates that changes to `StringBuilder` are reflected in the return value but assigning `null` to `sb` (`sb = null`) in the `finally` block, for example has no effect anymore (which is not reflected in the return value). Could you please comment something on it. (I understand that it is already covered in the answer and after about four months, it may not be reasonable to ask this but sometimes, when I think about it, I just skip/avoid this thing, when I don't understand it literally after thinking hard). – Tiny Mar 03 '14 at 19:49
  • @Tiny Assume that you've two references `stackSB` and `localSB` to the same `StringBuilder` object. Now, when you do `localSB.append("abc")`, you'll see the change for `stackSB` right? But when you do `localSB = null`, will that also make `stackSB` equals to `null`? Think about it. – Rohit Jain Mar 03 '14 at 19:55
  • The answer is *no* and this should be a conclusion. Am I right? Thank you very much :) – Tiny Mar 03 '14 at 20:01
  • @Tiny Yes, you're right :) – Rohit Jain Mar 03 '14 at 20:02
1

Because the finally block is not doing a return this.productName.

The return this.productName in the try block returns the address of the object. The assignment in the finally block creates a new object, leaving the return value unchanged.

Alcanzar
  • 16,985
  • 6
  • 42
  • 59