import java.lang.ref.Cleaner;

public class FinalFinally {
    @SuppressWarnings("finally")
    static int returnInFinally() {
        try {
            return 1;
        } finally {
            return 2;               // overrides the try's return
        }
    }

    static int finallyCannotChangePrimitive() {
        int x = 1;
        try {
            return x;               // value is captured here
        } finally {
            x = 99;
        }
    }

    @SuppressWarnings("finally")
    static void finallySwallowsException() {
        try {
            throw new IllegalStateException("original problem");
        } finally {
            return;                 // the exception disappears
        }
    }

    static class Res implements AutoCloseable {
        final String name;
        Res(String n) { name = n; System.out.println("  open " + n); }
        @Override public void close() { System.out.println("  close " + name); }
    }

    static class Tracked implements Runnable {
        public void run() { System.out.println("  cleaner action ran"); }
    }

    public static void main(String[] args) throws Exception {
        System.out.println("-- final variable, final reference --");
        final int[] data = { 1, 2 };
        data[0] = 100;                      // the object is mutable; only the reference is final
        System.out.println("data[0] = " + data[0]);

        System.out.println("-- finally edge cases --");
        System.out.println("return in finally -> " + returnInFinally());
        System.out.println("primitive changed in finally -> " + finallyCannotChangePrimitive());
        finallySwallowsException();
        System.out.println("exception swallowed, no stack trace printed");

        System.out.println("-- try-with-resources closes in reverse order --");
        try (Res a = new Res("A"); Res b = new Res("B")) {
            System.out.println("  body");
        }

        System.out.println("-- Cleaner: explicit, registered, not tied to GC timing for correctness --");
        Cleaner cleaner = Cleaner.create();
        Object owner = new Object();
        Cleaner.Cleanable c = cleaner.register(owner, new Tracked());
        c.clean();                          // deterministic; calling twice is harmless
        c.clean();

        System.out.println("-- System.exit skips finally --");
        try {
            System.exit(0);
        } finally {
            System.out.println("this line never prints");
        }
    }
}
