Do you really know? Which of catch, finally and return is executed first?
A friend of mine encountered a problem a while ago. The point of the problem is which of the catch and finally code blocks in try…catch…finally executes first. This question seems very simple, of course it is “catch executed first, finally executed later”? Is it really?
There is the following piece of C# code, what is the execution result of this code?
public static void Main(string[] args)
{
try
{
A();
}
catch
{
Console.WriteLine("catch!!!");
}
}
static void A()
{
try
{
throw new Exception();
}
finally
{
Console.WriteLine("finally!!!");
}
}
An exception is thrown in the try code block of the A() method, and the A method does not handle the exception, so the catch code block of the Main method will catch the exception, but there is a finally code block in the A() method, so what is the exception? After throwing, should the catch code block of the Main method be executed first or the finally code block in the A() method be executed first? Run the program and you can see that the finally code block is executed, and the result is as follows.
finally!!!
catch!!!
Why? This needs to start with how the exception object is passed to the called method. When a piece of code calls a method, the called method will put the return value and exception objec in a specific location, this location is called Stack Frame, and the caller code will get the return value and exception object of the called method from this specific location. Therefore, whether it is throwing an exception or returning a value, the called method just puts the exception object or return value in this specific position. After the return or throw is executed, if there are still code of finally block not been executed code, then these codes will continue to execute after return and throw, and then the method execution will end, and then the code that calls this method will read the return value from the Stack Frame or get the thrown by the called method exception object. Therefore, the above code will execute “finally” first and then “catch”.
Now, please tell me what is the result of the following code?
public static void Main(string[] args)
{
try
{
A();
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
static void A()
{
try
{
throw new Exception("aa");
}
finally
{
throw new Exception("bb");
}
}
The above is a very special piece of code. An exception is thrown in the try code block (message is aa), and an exception is also thrown in finally (message is bb). So what is the exception information actually printed by the program? The above program execution result is “bb”. It is not difficult to understand the principle through the above analysis: throw new Exception(“aa”) in the try code block sets the exception object of the method to Exception(“aa”), and throw new Exception(“bb” in the finall code block ) and modify the exception object of the method to Exception(“bb”), so the exception object thrown by the final method is Exception(“bb”).
Next, let’s play with the return value of the method. We try to modify the return value of the method in the finally code block. Unfortunately (or fortunately), C# forbids us to use the return statement in the finally code block, but we can make such an attempt in Java, as shown in the following Java code:
public static void main(String[] args)
{
System.out.println(A());
}
static int A()
{
try
{
return 1;
}
finally
{
return 2;
}
}
We set the return value of the method to 1 in the try code block, but set the return value of the method to 2 in the finally code block, so the final return value of the method is 2.
To sum up, when a method returns to set the return value or throws an exception, the method does not return immediately, but saves the return value or exception object on the Stack Frame, and then continues to execute the code in finally , if we modify the return value or throw a new exception in the finally code block, then the return value or the caught exception object is the modified one.