Java 8 SE: Finally block of a method is running before entering the method

Asked

Viewed 57 times

2

On page 359 of the Book: Java - How to Program - 10th Edition. Deitel explains the treatment of Java exceptions. It exemplifies the question with the following code:

public class UsingExceptions {
   public static void main(String[] args) {
      try {
         throwException(); 
      } 
      catch (Exception exception) { // exception thrown by throwException
         System.err.println("4 - Exception handled in main");
      } 

      doesNotThrowException();
   }

   // demonstrate try...catch...finally
   public static void throwException() throws Exception {
      try { // throw an exception and immediately catch it
         System.out.println("1 - Method throwException");
         throw new Exception(); // generate exception
      } 
      catch (Exception exception) { // catch exception thrown in try
         System.err.println(
            "2 - Exception handled in method throwException");
         throw exception; // rethrow for further processing

         // code here would not be reached; would cause compilation errors

      } 
      finally { // executes regardless of what occurs in try...catch
         System.err.println("3 - Finally executed in throwException");  
      }                                                             

      // code here would not be reached; would cause compilation errors
   } 

   // demonstrate finally when no exception occurs
   public static void doesNotThrowException() {
      try { // try block does not throw an exception
         System.out.println("5 - Method doesNotThrowException");
      } 
      catch (Exception exception) { // does not execute
         System.err.println(exception);
      }
      finally { // executes regardless of what occurs in try...catch     
         System.err.println("6 - Finally executed in doesNotThrowException");
      }                                                                  
 
      System.out.println("7 - End of method doesNotThrowException");
   } 
} 

In the book, Deitel agrees with me that the logical sequence of this code is:

1 - Method throwException
2 - Exception handled in method throwException
3 - Finally executed in throwException
4 - Exception handled in main
5 - Method doesNotThrowException
6 - Finally executed in doesNotThrowException
7 - End of method doesNotThrowException

However, when executing the code described in the book in Eclipse, Java inverts line 6 with line 5 and the result is like this:

1 - Method throwException
2 - Exception handled in method throwException
3 - Finally executed in throwException
4 - Exception handled in main
6 - Finally executed in doesNotThrowException
5 - Method doesNotThrowException
7 - End of method doesNotThrowException

That is, Java is calling the block finally of the method doesNotThrowException before entering the method. Someone can explain why?

  • 3

    Your console or even the operating system may be giving different priorities to stdout and stderr. Have you tried replacing the calls to System.err for System.out?

  • 1

    That’s what @Anthonyaccioly said. This "surprise behavior" of a block finally be executed before the corresponding block try is an inversion of a well-established behavior that does not happen in Java under any circumstances. The execution of finally (that only in a few very peculiar situations ceases to happen, as in the case of being preceded by a System.exit() terminating the JVM immediately) is always, and by definition, subsequent to the normal completion (or abbreviated by exception release) of the block try.

  • The @Anthony tip worked. Renaming all messages to System.err or System.out works. Searching better on err and out priorities, I found that this behavior is due to an Eclipse bug that has already been fixed. More details here: https://bugs.eclipse.org/bugs/show_bug.cgi?id=32205. I am using Eclipse Photon 2018, so the inversion happens.

1 answer

4

What is happening is not that the finally is executed before the try, rather that System.out and System.err are not being synchronized. In this case, while your app is writing first on System.out, the result of System.err is appearing first on your terminal.

On the Java side, when you run a CLI application it tries to find and configure the outputs for the streams pattern stdout and stderr the environment in which the application is running.

While both System.out and System.err do flush automatic of their respective buffers every line break, what happens after that is out of JVM control.

In the common case, when you run your application on a shell interactive, these streams are connected to the text terminal where the shell is running. These components in turn interact directly or indirectly with operating system Apis to display the end result to the user.

There is no guarantee of synchronization between stderr and stdout. Flush the stdout before the stderr is no guarantee that your terminal will display the contents of stdout first. These components out of control of your application may be using buffers own, multiple threads, Apis connected to GPU and various other tricks to process and display streams efficiently. Maintain the streams synchronized can be quite costly, and by default most environments do not do this.

For this type of exercise it is worth using a single stream. In case you can replace calls to System.err for System.out.

Alternatively you can investigate if there is any way to configure your terminal to synchronize the streams. For example, the OP mentioned that it is using an old version of Eclipse. In modern versions there is already an option to synchronize streams in accordance with that answer in Soen.

  • 1

    I guess that’s it. I imagine it’s not convenient for Eclipse to adopt this synchronized writing as a standard but offer it as an optional configuration because it probably impacts performance and you might be developing an application where it makes a difference, for example one that has many threads running at the same time and generating logs frequently, and writing them all in the same output (for example, redirecting the logs of any of these streams to a single log file) (although I’m not quite sure about how it works underneath the scenes).

  • 1

    Yes, I’ve suffered with it in Intellij IDEA too. In general you can’t trust the order of multiple logs streams. In the case of OP the good thing is that the try has a relationship Happens-before with finally, but in other cases (e. g., System.out and System.err in the same block), the Java memory model allows reordering of operations. I’ve seen including stacktraces shuffled with other log entries. In the case of logs a solution is to send stdout and stderr for different files and make the merge as per timestamps of the entries.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.