finally块总是在Java中执行吗?

时间:2008-09-15 17:43:55

标签: java return try-catch-finally

考虑到这段代码,我可以完全确定 finally块始终执行,无论something()是什么?

try {  
    something();  
    return success;  
}  
catch (Exception e) {   
    return failure;  
}  
finally {  
    System.out.println("I don't know if this will get printed out");
}

49 个答案:

答案 0 :(得分:2432)

是的,在finallytry代码块执行后,系统会调用catch

finally唯一不会被调用的时间是:

  1. 如果您调用System.exit()
  2. 如果JVM首先崩溃
  3. 如果JVM在trycatch块中达到无限循环(或其他一些不可中断的,非终止语句)
  4. 如果操作系统强行终止JVM进程;例如,UNIX上的kill -9 <pid>
  5. 如果主机系统死亡;例如,电源故障,硬件错误,操作系统恐慌等等
  6. 如果finally块将由守护程序线程执行,而所有其他非守护程序线程在调用finally之前退出

答案 1 :(得分:505)

示例代码:

public static void main(String[] args) {
    System.out.println(Test.test());
}

public static int test() {
    try {
        return 0;
    }
    finally {
        System.out.println("finally trumps return.");
    }
}

输出:

finally trumps return. 
0

答案 2 :(得分:363)

此外,虽然这是不好的做法,但如果在finally块中有一个return语句,它将胜过常规块中的任何其他返回。也就是说,以下块将返回false:

try { return true; } finally { return false; }

从finally块中抛出异常也是一样。

答案 3 :(得分:249)

以下是Java语言规范中的官方文字。

14.20.2. Execution of try-finally and try-catch-finally

  

首先执行try块,执行带有finally块的try语句。然后有一个选择:

     
      
  • 如果try块的执行正常完成,[...]
  •   
  • 如果try块的执行突然因为{{1>}值 V 而突然完成,[...]
  •   
  • 如果由于 R 的任何其他原因导致throw块的执行突然完成,则执行try块。然后有一个选择:      
        
    • 如果finally块正常完成,则finally语句突然完成,原因是 R
    •   
    • 如果try块突然因 S 而突然完成,则finally语句突然完成,原因是 S 和原因 R 被丢弃)。
    •   
  •   

try的规范实际上明确指出:

JLS 14.17 The return Statement

return
     

ReturnStatement: return Expression(opt) ; 语句,没有return 尝试将控制权转移给包含它的方法或构造函数的调用者。

     

Expression语句,return 尝试将控制权转移给包含它的方法的调用者; Expression的值成为方法调用的值。

     

前面的说明说“ 尝试转移控件”而不仅仅是“转移控件”,因为如果有{{1}在Expression块包含try语句的方法或构造函数中的语句,那些try语句的任何return子句将按顺序执行,从最里面到最外面,在将控制权转移给方法或构造函数的调用者之前。突然完成finally子句可能会破坏由try语句启动的控制权转移。

答案 4 :(得分:148)

除了其他响应之外,重要的是要指出'finally'有权通过try..catch块覆盖任何异常/返回值。例如,以下代码返回12:

public static int getMonthsInYear() {
    try {
        return 10;
    }
    finally {
        return 12;
    }
}

同样,以下方法不会抛出异常:

public static int getMonthsInYear() {
    try {
        throw new RuntimeException();
    }
    finally {
        return 12;
    }
}

虽然以下方法确实抛出它:

public static int getMonthsInYear() {
    try {
        return 12;          
    }
    finally {
        throw new RuntimeException();
    }
}

答案 5 :(得分:109)

我稍微修改了上面的例子 -

public static void main(final String[] args) {
    System.out.println(test());
}

public static int test() {
    int i = 0;
    try {
        i = 2;
        return i;
    } finally {
        i = 12;
        System.out.println("finally trumps return.");
    }
}

以上代码输出:

  

最终胜过回归   2

这是因为执行return i;i的值为2.此后执行finally块,其中12被分配给i,然后{{1执行。

执行System.out块后,finally块返回2,而不是返回12,因为此return语句不会再次执行。

如果您将在Eclipse中调试此代码,那么您会感觉在执行try System.out块之后finally returntry块再次执行。但这种情况并非如此。它只返回值2。

答案 6 :(得分:96)

以下是Kevin's answer的详细说明。重要的是要知道要返回的表达式是在finally之前计算的,即使它是在之后返回的。

public static void main(String[] args) {
    System.out.println(Test.test());
}

public static int printX() {
    System.out.println("X");
    return 0;
}

public static int test() {
    try {
        return printX();
    }
    finally {
        System.out.println("finally trumps return... sort of");
    }
}

输出:

X
finally trumps return... sort of
0

答案 7 :(得分:52)

这是一个终极块的整个想法。它可以让你确保你做的清理工作,否则你可能会因为你返回而被跳过。

最后在try块中调用,无论发生什么除非你调用System.exit(int)或者Java虚拟机因其他原因而启动)。

答案 8 :(得分:38)

考虑这一点的合理方式是:

  1. 放在finally块中的代码必须在try块
  2. 中执行发生的任何内容
  3. 因此,如果try块中的代码尝试返回值或抛出异常,则该项目将“置于架子上”,直到finally块可以执行
  4. 因为finally块中的代码(根据定义)具有高优先级,所以它可以返回或抛出它喜欢的任何内容。在这种情况下,“架子上”留下的任何东西都将被丢弃。
  5. 唯一的例外是VM在try块期间完全关闭,例如通过'System.exit'

答案 9 :(得分:18)

最后总是执行,除非程序异常终止(如调用System.exit(0)..)。所以,你的sysout会被打印

答案 10 :(得分:18)

答案 11 :(得分:17)

始终执行finally块,除非由于JVM崩溃或调用System.exit(0)导致程序异常终止。

最重要的是,finally块中返回的任何值都将覆盖执行finally块之前返回的值,因此在最后使用try时请小心检查所有出口点。

答案 12 :(得分:17)

不,并非总是有一个例外情况// System.exit(0); 在finally块阻止最终执行之前。

  class A {
    public static void main(String args[]){
        DataInputStream cin = new DataInputStream(System.in);
        try{
            int i=Integer.parseInt(cin.readLine());
        }catch(ArithmeticException e){
        }catch(Exception e){
           System.exit(0);//Program terminates before executing finally block
        }finally{
            System.out.println("Won't be executed");
            System.out.println("No error");
        }
    }
}

答案 13 :(得分:12)

最后总是运行这一点,只是因为它在返回后出现在代码中并不意味着它是如何实现的。 Java运行时有责任在退出try块时运行此代码。

例如,如果您有以下内容:

int foo() { 
    try {
        return 42;
    }
    finally {
        System.out.println("done");
    }
}

运行时将生成如下内容:

int foo() {
    int ret = 42;
    System.out.println("done");
    return 42;
}

如果抛出未捕获的异常,finally块将运行,异常将继续传播。

答案 14 :(得分:10)

这是因为您将i的值指定为12,但未将i的值返回给函数。正确的代码如下:

public static int test() {
    int i = 0;
    try {
        return i;
    } finally {
        i = 12;
        System.out.println("finally trumps return.");
        return i;
    }
}

答案 15 :(得分:9)

因为除非你调用System.exit()(或线程崩溃),否则将始终调用finally块。

答案 16 :(得分:9)

  

答案很简单

<强> INPUT:

try{
    int divideByZeroException = 5 / 0;
} catch (Exception e){
    System.out.println("catch");
    return;    // also tried with break; in switch-case, got same output
} finally {
    System.out.println("finally");
}

<强>输出:

catch
finally

答案 17 :(得分:8)

是的,最后块总是执行。大多数开发人员使用这个块来关闭数据库连接,结果集对象,语句对象以及使用java hibernate来回滚事务。

答案 18 :(得分:8)

简而言之,在官方Java文档(点击here)中写道 -

  

如果在执行try或catch代码时JVM退出,那么   finally块可能无法执行。同样,如果线程正在执行   try或catch代码被中断或终止,finally块可以   即使整个应用程序仍在继续,也不会执行。

答案 19 :(得分:8)

是的,它会被调用。这就是拥有finally关键字的重点。如果跳出try / catch块可能只是跳过finally块,那么就像将System.out.println放在try / catch之外一样。

答案 20 :(得分:7)

考虑以下计划:

public class SomeTest {

    private static StringBuilder sb = new StringBuilder();

    public static void main(String args[]) {

        System.out.println(someString());
        System.out.println("---AGAIN---");
        System.out.println(someString());
        System.out.println("---PRINT THE RESULT---");
        System.out.println(sb.toString());
    }

    private static String someString() {

        try {
            sb.append("-abc-");
            return sb.toString();

        } finally {
            sb.append("xyz");
        }
    }
}

从Java 1.8.162开始,上面的代码块给出了以下输出:

-abc-
---AGAIN---
-abc-xyz-abc-
---PRINT THE RESULT---
-abc-xyz-abc-xyz

这意味着使用finally释放对象是一种很好的做法,如下面的代码:

private static String someString() {

    StringBuilder sb = new StringBuilder();

    try {
        sb.append("abc");
        return sb.toString();

    } finally {
        sb = null; // Just an example, but you can close streams or DB connections this way.
    }
}

答案 21 :(得分:7)

finally总是在返回x(计算值)之前执行。

System.out.println(foo());

....

int foo(){
    int x = 2;
    try{
        return x++;
    } finally{
        System.out.println(x);
    }
}

输出:

3
2

答案 22 :(得分:7)

是的。 只有它不会出现JVM退出或崩溃

答案 23 :(得分:7)

是的,它会的。无论你的try或catch块发生什么,除非调用System.exit()或JVM崩溃。如果块中有任何return语句,则最终将在该return语句之前执行。

答案 24 :(得分:6)

在任何语言中都是如此......最终将始终在return语句之前执行,无论方法体中返回的位置如何。如果不是这样的话,那么finally块就没有多大意义了。

答案 25 :(得分:6)

try - catch - finally是使用异常处理案例的关键词。
作为正常的解释

try {
     //code statements
     //exception thrown here
     //lines not reached if exception thrown
} catch (Exception e) {
    //lines reached only when exception is thrown
} finally {
    // always executed when the try block is exited
    //independent of an exception thrown or not
}

finally块阻止执行......

  • 当您致电System.exit(0);
  • 如果JVM退出。
  • JVM中的错误

答案 26 :(得分:6)

finally将执行,这是肯定的。

finally将不会在以下情况下执行:

案例1:

执行System.exit()

案例2:

当您的JVM /线程崩溃时。

案例3:

手动停止执行时。

答案 27 :(得分:6)

添加到@vibhash's answer,因为没有其他答案解释了如下所示的可变对象会发生什么。

public static void main(String[] args) {
    System.out.println(test().toString());
}

public static StringBuffer test() {
    StringBuffer s = new StringBuffer();
    try {
        s.append("sb");
        return s;
    } finally {
        s.append("updated ");
    }
}

将输出

sbupdated 

答案 28 :(得分:6)

我试过这个, 它是单线程的。

class Test {
    public static void main(String args[]) throws Exception {
       Object obj = new Object();
       try {
            synchronized (obj) {
            obj.wait();
            System.out.println("after wait()");
           }
       } catch (Exception e) {
       } finally {
           System.out.println("finally");
       }
   }
}

主线程将永远处于等待状态,因此最终永远不会被调用,

因此控制台输出不会打印字符串:after wait()finally

同意@Stephen C,上面的例子是提到的第3个案例之一here

在以下代码中添加更多此类无限循环可能性:

// import java.util.concurrent.Semaphore;
class Test {
    public static void main(String[] args) {
        try {
            // Thread.sleep(Long.MAX_VALUE);
            // Thread.currentThread().join();
            // new Semaphore(0).acquire();
            // while (true){}
            System.out.println("after sleep join semaphore exit infinite while loop");
        } catch (Exception e) {
        } finally {
            System.out.println("finally");
        }
    }
}

案例2:如果JVM首先崩溃

import sun.misc.Unsafe;
import java.lang.reflect.Field;
class Test {
    public static void main(String args[]) {
        try {
            unsafeMethod();
//            Runtime.getRuntime().halt(123);
            System.out.println("After Jvm Crash!");
        } catch (Exception e) {
        } finally {
            System.out.println("finally");
        }
    }

    private static void unsafeMethod() throws NoSuchFieldException, IllegalAccessException {
        Field f = Unsafe.class.getDeclaredField("theUnsafe");
        f.setAccessible(true);
        Unsafe unsafe = (Unsafe) f.get(null);
        unsafe.putAddress(0, 0);
    }
}

参考:How do you crash a JVM?

案例6:如果最终块将由守护程序线程执行,并且所有其他非守护程序线程在最终被调用之前退出。

class Test {
    public static void main(String args[]) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try {
                    printThreads("Daemon Thread printing");
                    // just to ensure this thread will live longer than main thread
                    Thread.sleep(10000);
                } catch (Exception e) {
                } finally {
                    System.out.println("finally");
                }
            }
        };
        Thread daemonThread = new Thread(runnable);
        daemonThread.setDaemon(Boolean.TRUE);
        daemonThread.setName("My Daemon Thread");
        daemonThread.start();
        printThreads("main Thread Printing");
    }

    private static synchronized void printThreads(String str) {
        System.out.println(str);
        int threadCount = 0;
        Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
        for (Thread t : threadSet) {
            if (t.getThreadGroup() == Thread.currentThread().getThreadGroup()) {
                System.out.println("Thread :" + t + ":" + "state:" + t.getState());
                ++threadCount;
            }
        }
        System.out.println("Thread count started by Main thread:" + threadCount);
        System.out.println("-------------------------------------------------");
    }
}

输出:这不打印&#34;最后&#34;这意味着&#34;最后阻止&#34; in&#34;守护程序线程&#34;没有执行

main Thread Printing  
Thread :Thread[My Daemon Thread,5,main]:state:BLOCKED  
Thread :Thread[main,5,main]:state:RUNNABLE  
Thread :Thread[Monitor Ctrl-Break,5,main]:state:RUNNABLE   
Thread count started by Main thread:3  
-------------------------------------------------  
Daemon Thread printing  
Thread :Thread[My Daemon Thread,5,main]:state:RUNNABLE  
Thread :Thread[Monitor Ctrl-Break,5,main]:state:RUNNABLE  
Thread count started by Main thread:2  
-------------------------------------------------  

Process finished with exit code 0

答案 29 :(得分:6)

是的,因为无控制声明可以阻止执行finally

这是一个参考示例,其中将执行所有代码块:

| x | Current result | Code 
|---|----------------|------ - - -
|   |                |     
|   |                | public static int finallyTest() {
| 3 |                |     int x = 3;
|   |                |     try {
|   |                |        try {
| 4 |                |             x++;
| 4 | return 4       |             return x;
|   |                |         } finally {
| 3 |                |             x--;
| 3 | throw          |             throw new RuntimeException("Ahh!");
|   |                |         }
|   |                |     } catch (RuntimeException e) {
| 4 | return 4       |         return ++x;
|   |                |     } finally {
| 3 |                |         x--;
|   |                |     }
|   |                | }
|   |                |
|---|----------------|------ - - -
|   | Result: 4      |

在下面的变体中,将跳过return x;。结果仍为4

public static int finallyTest() {
    int x = 3;
    try {
        try {
            x++;
            if (true) throw new RuntimeException("Ahh!");
            return x; // skipped
        } finally {
            x--;
        }
    } catch (RuntimeException e) {
        return ++x;
    } finally {
        x--;
    }
}

参考文献当然跟踪他们的状态。此示例返回带value = 4的引用:

static class IntRef { public int value; }
public static IntRef finallyTest() {
    IntRef x = new IntRef();
    x.value = 3;
    try {
        return x;
    } finally {
        x.value++; // will be tracked even after return
    }
}

答案 30 :(得分:6)

如果你没有处理异常,在终止程序之前,JVM会执行finally块。只有当程序的正常执行失败意味着由于以下原因终止程序时才会执行...

  1. 通过导致致命错误导致进程中止。

  2. 因内存损坏而终止程序。

  3. 通过调用System.exit()

  4. 如果程序进入无限循环。

答案 31 :(得分:5)

除了关于最后替换try块中的return的返回点之外,异常也是如此。抛出异常的finally块将替换try块内抛出的返回或异常。

答案 32 :(得分:5)

并不总是

Java语言规范描述了try-catch-finally和try-catch块如何在14.20.2上工作 在任何地方它都指定始终执行finally块。 但是对于try-catch-finally和try-finally块完成的所有情况,它确实指定在完成之前最终必须执行。

try {
  CODE inside the try block
}
finally {
  FIN code inside finally block
}
NEXT code executed after the try-finally block (may be in a different method).

JLS不保证在 CODE 之后执行 FIN 。 JLS保证如果 CODE NEXT 被执行,那么 FIN 将始终在 CODE 之后和下一步

为什么JLS不保证在try块之后总是执行finally块?因为这是不可能的。在完成try块之后但在执行finally块之前,JVM很可能会被中止(终止,崩溃,断电)。 JLS无法避免这种情况。

因此,任何因其正确行为而依赖于最终块的软件都会在其尝试块完成后始终执行。

try块中的返回值与此问题无关。如果执行在try-catch-finally之后到达代码,则保证finally块在之前执行,有或没有在try块内返回。

答案 33 :(得分:5)

是的,它写成here

  

如果在执行try或catch代码时JVM退出,则finally块可能无法执行。同样,如果执行try或catch代码的线程被中断或终止,则即使整个应用程序仍在继续,finally块也可能无法执行。

答案 34 :(得分:5)

在几个独特的场景中返回后不会调用finally块:如果先调用System.exit(),或者JVM崩溃。

让我试着以最简单的方式回答你的问题。

规则1 :finally块始终运行  (虽然也有例外。但是让我们坚持一段时间。)

规则2 :当控制离开try或catch块时,finally块中的语句运行。控制的转移可以由于正常执行,执行中断而继续,继续,转到或返回陈述,或异常的传播。

如果返回语句具体(因为它的标题),控件必须离开调用方法,因此调用相应的try-finally结构的finally块。 return语句在finally块之后执行。

如果finally块中还有一个return语句,它肯定会覆盖try块中挂起的一个,因为它清除了调用栈。

你可以在这里引用一个更好的解释:http://msdn.microsoft.com/en-us/ ....这个概念在所有高级语言中大致相同。

答案 35 :(得分:5)

  1. 最后Block总是被执行。除非和直到 那里存在 System.exit()语句(finally块中的第一个语句)。
  2. 如果 system.exit()是第一个语句,那么finally块将不会被执行并且控制来自finally块。 每当System.exit()语句进入finally块时,直到该语句最终执行块,并且当System.exit()出现时,控制力完全从finally块中出来。

答案 36 :(得分:4)

尝试使用此代码,您将了解finally语句中的代码在返回语句后执行

public class TestTryCatchFinally {
    static int x = 0;

    public static void main(String[] args){
        System.out.println(f1() );
        System.out.println(f2() );
    }

    public static int f1(){
        try{
            x = 1;
            return x;
        }finally{
            x = 2;
        }
    }

    public static int f2(){
        return x;
    }
}

答案 37 :(得分:4)

是的,它总是被调用,但在一种情况下,当你使用System.exit()

时它不会调用
try{
//risky code
}catch(Exception e){
//exception handling code
}
finally(){
//It always execute but before this block if there is any statement like System.exit(0); then this block not execute.
}

答案 38 :(得分:4)

因为无论你遇到什么情况,总是会被召唤。你没有异常,它仍然被调用,捕获异常,它仍然被称为

答案 39 :(得分:4)

与以下代码相同:

static int f() {
    while (true) {
        try {
            return 1;
        } finally {
            break;
        }
    }
    return 2;
}

f将返回2!

答案 40 :(得分:4)

我对在不同论坛上提供的所有答案感到非常困惑,并决定最终编码和查看。输出是:

即使在try和catch块中有返回,也会执行。

try {  
  System.out.println("try"); 
  return;
  //int  i =5/0;
  //System.exit(0 ) ;
} catch (Exception e) {   
  System.out.println("catch");
  return;
  //int  i =5/0;
  //System.exit(0 ) ;
} finally {  
   System.out.println("Print me FINALLY");
}

<强>输出

  

     

最后打印

  1. 如果在上面的代码中,try和catch块中的System.exit(0)替换了返回,并且由于任何原因在它之前发生了异常。

答案 41 :(得分:4)

在正常的执行过程中考虑这一点(即没有抛出任何异常):如果方法不是'void',那么它总是显式地返回一些东西,但最终总是被执行

答案 42 :(得分:4)

如果抛出异常,最后运行。如果没有抛出异常,最后运行。如果捕获到异常,则最终运行。如果未捕获异常,则最终运行。

只有当JVM退出时它才会运行。

答案 43 :(得分:4)

最后块总是执行是否处理异常。如果在try块之前发生任何异常,则finally块将不会执行。

答案 44 :(得分:3)

如果在嵌套的finally块中抛出异常,

最终也可以提前退出。编译器将警告您finally块未正常完成或出现无法访问代码的错误。仅当throw不在条件语句之后或在循环内时,才会显示无法访问代码的错误。

try{
}finally{
   try{
   }finally{
      //if(someCondition) --> no error because of unreachable code
      throw new RunTimeException();
   }
   int a = 5;//unreachable code
}

答案 45 :(得分:2)

即使在try块中放入return语句,也始终执行block。 finally块将在return语句之前执行。

答案 46 :(得分:2)

最后总是在最后调用

当你尝试时,它执行一些代码,如果在try中发生了某些事情,那么catch将捕获该异常,你可以打印出一些mssg或抛出错误,然后最终执行block。

最后通常在进行清理时使用,例如,如果你在java中使用扫描仪,你应该关闭扫描仪,因为它会导致其他问题,例如无法打开某些文件

答案 47 :(得分:1)

以下是一些可以绕过finally块的条件:

  1. 如果在执行try或catch代码时JVM退出,则finally块可能不会执行。有关sun tutorial
  2. 的更多信息
  3. 正常关闭-在最后一个非守护程序线程退出时或在Runtime.exit()(some good blog)时发生。当线程退出时,JVM将对正在运行的线程进行清点,如果剩下的唯一线程是守护程序线程,则它将启动有序关闭。当JVM暂停时,所有剩余的守护程序线程都将被放弃,最后不执行块,堆栈也不会解开,而JVM只会退出。应当尽可能少地使用守护程序线程,很少有处理活动可以在不清理的情况下随时安全地放弃。特别是,将守护程序线程用于可能执行任何I / O的任务是很危险的。最好将守护程序线程保存起来以完成“内务处理”任务,例如后台线程会定期从内存缓存(source)中删除过期的条目

最后一个非守护线程退出示例:

public class TestDaemon {
    private static Runnable runnable = new Runnable() {
        @Override
        public void run() {
            try {
                while (true) {
                    System.out.println("Is alive");
                    Thread.sleep(10);
                    // throw new RuntimeException();
                }
            } catch (Throwable t) {
                t.printStackTrace();
            } finally {
                System.out.println("This will never be executed.");
            }
        }
    };

    public static void main(String[] args) throws InterruptedException {
        Thread daemon = new Thread(runnable);
        daemon.setDaemon(true);
        daemon.start();
        Thread.sleep(100);
        // daemon.stop();
        System.out.println("Last non-daemon thread exits.");
    }
}

输出:

Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Last non-daemon thread exits.
Is alive
Is alive
Is alive
Is alive
Is alive

答案 48 :(得分:0)

尝试还原的示例

<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
<style>
body{
	padding : 0px;
    margin : 0px;
}
.container{
	width : 100vw;
    height : 100vh;
    position : relative;   
}
.container img{
	width : 100%;
    height : 100%;
    display : block;
    margin : 0px auto;
}
.container video{
	width : auto;
	position: absolute;
    top: 50%;
    left: 50%;
    margin-right: -50%;
    transform: translate(-50%, -50%) 
}
</style>
</head>
<body>

<div class="container">
	<img src="https://via.placeholder.com/300" />
    <video width="400" controls>
      <source src="https://www.w3schools.com/html/mov_bbb.mp4" type="video/mp4">
      <source src="https://www.w3schools.com/html/mov_bbb.ogg" type="video/ogg">
      Your browser does not support HTML5 video.
	</video>
</div>

</body>
</html>

测试输出:

static class IamAutoCloseable implements AutoCloseable {
    private final String name;
    IamAutoCloseable(String name) {
        this.name = name;
    }
    public void close() {
        System.out.println(name);
    }
}

@Test
public void withResourceFinally() {
    try (IamAutoCloseable closeable1 = new IamAutoCloseable("closeable1");
         IamAutoCloseable closeable2 = new IamAutoCloseable("closeable2")) {
        System.out.println("try");
    } finally {
        System.out.println("finally");
    }
}