Was ist der Sinn von finally

Beim (wieder einmal) Lesen in Clean Code bin ich gerade über “7.2 Try-Catch-Finally-Anweisungen zuerst schreiben” gestolpert und mir ist aufgefallen, dass ich zwar viele try-ctach-Anweisungen nutze, aber seltenst finally.

Was genau ist eigentlich der Unterschied zwischen einem finally und einfach Code hinter try-catch? Geht es dabei nur um eine Verdeutlichung, was ja auch schon einen Wert hätte, oder resultiert wirklich unterschiedliches Verhalten?

            throw new RuntimeException("bla bla");
        }
        catch (Exception expeption) {
            expeption.printStackTrace();
        }
        System.out.println("Danach...");

gibt

java.lang.RuntimeException: bla bla
	at javathings.trycatchfinally.WithOutFinally.main(WithOutFinally.java:7)
Danach...

aus (manchmal gerät auch die Reihenfolge durcheinander, das ist aber in der unteren Variante genauso…

            throw new RuntimeException("bla bla");
        }
        catch (Exception expeption) {
            expeption.printStackTrace();
        }
        finally {
            System.out.println("Danach...");
        }

gibt

java.lang.RuntimeException: bla bla
	at javathings.trycatchfinally.WithFinally.main(WithFinally.java:7)
Danach...

aus. Lässt man das Werfen der Ausnahme weg, kommt auch beidesmal einfach die Ausgabe “Danach…”.

Das Finaly sorgt dafür das dieser Code immer ausgeführt wird.
So z.b. wenn ich eine lese Operation habe, und dort eine IO Exception fliegt, das ich danach immer das File schließe, auch wenn in dem Catch steht ich beende die Funktion.

Der Code nach dem try-catch wird ja aber auch beide Male ausgeführt. Welchen Vorteil hat das close() im finally gegenüber der Variante, es einfach unter den catch-Block zu schreiben?

(Mal davon abgesehen, dass ich solche Konstrukte ungern mit anderem Code in einer Methode mische… das wäre aber nur der oben erwähnte dokumentarische Vorteil, kein inhaltlicher.)

Wie @Unregistered schon schreibt,
probiere mal diese zwei Codeschnippsel aus, da müsste meine ich ein Unterschied sein.

            throw new RuntimeException("bla bla");
        }
        catch (Exception expeption) {
            expeption.printStackTrace();

            return;
        }
        System.out.println("Danach...");
            throw new RuntimeException("bla bla");
        }
        catch (Exception expeption) {
            expeption.printStackTrace();

            return;
        }
        finally {
            System.out.println("Danach...");
        }

Ah, das ist doch mal ein wirklicher Vorteil. Eclipse bemängelt im obigen Fall natürlich gleich, dass das sysout nie ausgeführt wird. Aber mit

        try {
            throw new RuntimeException("bla bla");
        }
        catch (Exception expeption) {
            expeption.printStackTrace();
            tuWas();
        }
        System.out.println("Danach...");
    }

    private static void tuWas() {
        throw new RuntimeException("Dumm gelaufen");
    }

lässt sich der Unterschied sehen.

oder auch ganz ohne catch und tuwas() die originale ‘bla bla’-Exception die Methode beenden lassen, ein finally kommt trotzdem dran, try-finally geht also auch

wenn man es braucht dann meist für Aktionen wie DB-Session schließen, wobei wieder Exceptions auftreten können und noch ein inneres finally wichtig sein kann,
meist unübersichtlich, trotzdem wichtig

Wenn schon dabei mit finally etc - man sollte vermeiden im finally block return zu nutzen

Folgender Code gibt “yikes!” aus, die Exception information geht verloren.

public class Test {

  public static void main(String[] args) {
    try {
      doSomething();
      System.out.println("yikes!");
    } 
     catch (RuntimeException e) {
      System.out.println("got it.");
    }
  }
  
  public static void doSomething() {
    try {
      throw new RuntimeException();
    }
    finally {
      return;
    }
  }
}```

siehe auch JLS:


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

Oha. Gut zu wissen.