Java try-with-resource 实现由编译器完成

Java try-with-resource implementation done by compiler

我想知道,当异常发生时,try with resource 语句如何设法在进入 catch 块之前关闭资源。 当异常发生时,执行立即跳转到 catch 块。所以实际上 try-with-resource 关闭资源的地方。

为了更好地理解它是如何工作的,我决定看看编译器是如何实现它的。我写了下面的代码并编译了它。

public class Test
{
    public static void main(final String[] args) {
       //I used same JDK for compilation and execution.
        System.out.println("Java version: " + System.getProperty("java.version") + "\n");
        try(CloseMe me = new CloseMe(); 
                CloseMeToo meToo = new CloseMeToo()){
                
                System.out.println("trying");
                throw new Exception("try failed");

            } catch(Exception e) {
                System.out.println("failed");
                System.out.println("\n");
                System.out.println(e.getMessage());
                System.out.println(e.getSuppressed()[0].getMessage());
                System.out.println(e.getSuppressed()[1].getMessage());
            }
    }
}

class CloseMe implements AutoCloseable {
    @Override
    public void close() throws Exception {
        System.out.println("me closing!");
        throw new Exception("don't close me :o");
    }
}

class CloseMeToo implements AutoCloseable {
    @Override
    public void close() throws Exception {
        System.out.println("meToo closing!");
        throw new Exception("don't close me too :O");
    }
}

输出

Java version: 15.0.1

trying
meToo closing!
me closing!
failed


try failed
don't close me too :O
don't close me :o

然后我去了 www.javadecompilers.com 并尝试了那里的反编译器。两个反编译器给出了不错的结果:CFR 0.150 和 Fernflower。 CFR 最易读和完整,因此将其张贴在这里。

public class Test
{
    public static void main(final String[] args) throws Throwable{
        System.out.println("Java version: " + System.getProperty("java.version") + "\n");
        
        try {
            Throwable throwable = null;
            Object var2_4 = null;     //<-- where this variable is used?
            try {
                CloseMe me = new CloseMe();
                try {
                    CloseMeToo meToo = new CloseMeToo();
                    try {
                        System.out.println("trying");
                        throw new Exception("try failed");
                    }
                    catch (Throwable throwable2) {
                        throwable = throwable2; //<-- I put this line to make it work
                        if (meToo != null) {
                            meToo.close();
                        }
                        throw throwable2;
                    }
                }
                catch (Throwable throwable3) {
                    if (throwable == null) {
                        throwable = throwable3;
                    } else if (throwable != throwable3) {
                        throwable.addSuppressed(throwable3);
                    }
                    if (me != null) {
                        me.close();
                    }
                    throw throwable;
                }
            }
            catch (Throwable throwable4) {
                if (throwable == null) {
                    throwable = throwable4;
                } else if (throwable != throwable4) {
                    throwable.addSuppressed(throwable4);
                }
                throw throwable;
            }
        }
        catch (Exception e) {
            System.out.println("failed");
            System.out.println("\n");
            System.out.println(e.getMessage());
            System.out.println(e.getSuppressed()[0].getMessage());
            System.out.println(e.getSuppressed()[1].getMessage());
        }
    }
}

我知道反编译器有局限性。理想的反编译器会给我同样的 try-with-resource,我不会看到这些细节。所以没关系。
我的问题是:

  1. 在上面的反编译代码中,变量var2_4没有被使用。此外,我必须添加一行以使其像 try-with-resource 一样工作。我认为代码不完整。如果有,可以add/explain缺件吗?
  2. 如果任何了解字节码的人将 class 文件翻译成准确的 java 代码,那就太好了。或者给我指点,告诉我在哪里可以获得完成这项工作的工具。

谢谢!

try-with-resources 的行为在 Java Language Specification, section 14.20.3. try-with-resources.

中完全 记录

具体说明如下简版问题代码:

try (CloseMe me = new CloseMe(); CloseMeToo meToo = new CloseMeToo()) {
    System.out.println("trying");
} catch (Exception e) {
    System.out.println("failed");
}

首先转换为:

try {
    try (CloseMe me = new CloseMe(); CloseMeToo meToo = new CloseMeToo()) {
        System.out.println("trying");
    }
} catch (Exception e) {
    System.out.println("failed");
}

然后到:

try {
    final CloseMe me = new CloseMe();
    Throwable #primaryExc1 = null;

    try (CloseMeToo meToo = new CloseMeToo()) {
        System.out.println("trying");
    } catch (Throwable #t) {
        #primaryExc1 = #t;
        throw #t;
    } finally {
        if (me != null) {
            if (#primaryExc1 != null) {
                try {
                    me.close();
                } catch (Throwable #suppressedExc) {
                    #primaryExc1.addSuppressed(#suppressedExc);
                }
            } else {
                me.close();
            }
        }
    }
} catch (Exception e) {
    System.out.println("failed");
}

然后到:

try {
    final CloseMe me = new CloseMe();
    Throwable #primaryExc1 = null;

    try {
        final CloseMeToo meToo = new CloseMeToo()
        Throwable #primaryExc2 = null;

        try {
            System.out.println("trying");
        catch (Throwable #t) {
            #primaryExc2 = #t;
            throw #t;
        } finally {
            if (meToo != null) {
                if (#primaryExc2 != null) {
                    try {
                        meToo.close();
                    } catch (Throwable #suppressedExc) {
                        #primaryExc2.addSuppressed(#suppressedExc);
                    }
                } else {
                    meToo.close();
                }
            }
        }
    } catch (Throwable #t) {
        #primaryExc1 = #t;
        throw #t;
    } finally {
        if (me != null) {
            if (#primaryExc1 != null) {
                try {
                    me.close();
                } catch (Throwable #suppressedExc) {
                    #primaryExc1.addSuppressed(#suppressedExc);
                }
            } else {
                me.close();
            }
        }
    }
} catch (Exception e) {
    System.out.println("failed");
}