Try-finally block prevents StackOverflowError
It doesn't run forever. Each stack overflow causes the code to move to the finally block. The problem is that it will take a really, really long time. The order of time is O(2^N) where N is the maximum stack depth.
Imagine the maximum depth is 5
foo() calls
foo() calls
foo() calls
foo() calls
foo() which fails to call foo()
finally calls
foo() which fails to call foo()
finally
foo() calls
foo() which fails to call foo()
finally calls
foo() which fails to call foo()
finally calls
foo() calls
foo() calls
foo() which fails to call foo()
finally calls
foo() which fails to call foo()
finally
foo() calls
foo() which fails to call foo()
finally calls
foo() which fails to call foo()
finally calls
foo() calls
foo() calls
foo() calls
foo() which fails to call foo()
finally calls
foo() which fails to call foo()
finally
foo() calls
foo() which fails to call foo()
finally calls
foo() which fails to call foo()
finally calls
foo() calls
foo() calls
foo() which fails to call foo()
finally calls
foo() which fails to call foo()
finally
foo() calls
foo() which fails to call foo()
finally calls
foo() which fails to call foo()
To work each level into the finally block take twice as long an the stack depth could be 10,000 or more. If you can make 10,000,000 calls per second, this will take 10^3003 seconds or longer than the age of the universe.
When you get an exception from the invocation of foo()
inside the try
, you call foo()
from finally
and start recursing again. When that causes another exception, you'll call foo()
from another inner finally()
, and so on almost ad infinitum.
Try running the following code:
try {
throw new Exception("TEST!");
} finally {
System.out.println("Finally");
}
You will find that the finally block executes before throwing an Exception up to the level above it. (Output:
Finally
Exception in thread "main" java.lang.Exception: TEST! at test.main(test.java:6)
This makes sense, as finally is called right before exiting the method. This means, however, that once you get that first StackOverflowError
, it will try to throw it, but the finally must execute first, so it runs foo()
again, which gets another stack overflow, and as such runs finally again. This keeps happening forever, so the exception is never actually printed.
In your bar method however, as soon as the exception occurs, it is just thrown straight up to the level above, and will be printed
In effort to provide reasonable evidence that this WILL eventually terminate, I offer the following rather meaningless code. Note: Java is NOT my language, by any stretch of the most vivid imagination. I proffer this up only to support Peter's answer, which is the correct answer to the question.
This attempts to simulate the conditions of what happens when an invoke can NOT happen because it would introduce a stack overflow. It seems to me the hardest thing people are failing to grasp in that the invoke does not happen when it cannot happen.
public class Main
{
public static void main(String[] args)
{
try
{ // invoke foo() with a simulated call depth
Main.foo(1,5);
}
catch(Exception ex)
{
System.out.println(ex.toString());
}
}
public static void foo(int n, int limit) throws Exception
{
try
{ // simulate a depth limited call stack
System.out.println(n + " - Try");
if (n < limit)
foo(n+1,limit);
else
throw new Exception("StackOverflow@try("+n+")");
}
finally
{
System.out.println(n + " - Finally");
if (n < limit)
foo(n+1,limit);
else
throw new Exception("StackOverflow@finally("+n+")");
}
}
}
The output of this little pointless pile of goo is the following, and the actual exception caught may come as a surprise; Oh, and 32 try-calls (2^5), which is entirely expected:
1 - Try
2 - Try
3 - Try
4 - Try
5 - Try
5 - Finally
4 - Finally
5 - Try
5 - Finally
3 - Finally
4 - Try
5 - Try
5 - Finally
4 - Finally
5 - Try
5 - Finally
2 - Finally
3 - Try
4 - Try
5 - Try
5 - Finally
4 - Finally
5 - Try
5 - Finally
3 - Finally
4 - Try
5 - Try
5 - Finally
4 - Finally
5 - Try
5 - Finally
1 - Finally
2 - Try
3 - Try
4 - Try
5 - Try
5 - Finally
4 - Finally
5 - Try
5 - Finally
3 - Finally
4 - Try
5 - Try
5 - Finally
4 - Finally
5 - Try
5 - Finally
2 - Finally
3 - Try
4 - Try
5 - Try
5 - Finally
4 - Finally
5 - Try
5 - Finally
3 - Finally
4 - Try
5 - Try
5 - Finally
4 - Finally
5 - Try
5 - Finally
java.lang.Exception: StackOverflow@finally(5)