it-swarm-vi.tech

Liệu một khối cuối cùng luôn luôn được thực thi trong Java?

Xem xét mã này, tôi có thể hoàn toàn chắc chắn rằng khối finally luôn luôn thực thi, bất kể something() là gì?

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

Có, finally sẽ được gọi sau khi thực thi các khối mã try hoặc catch.

Lần duy nhất finally sẽ không được gọi là:

  1. Nếu bạn gọi System.exit()
  2. Nếu JVM gặp sự cố trước
  3. Nếu JVM đạt đến một vòng lặp vô hạn (hoặc một số câu lệnh không ngắt, không ngắt kết thúc khác) trong khối try hoặc catch
  4. Nếu HĐH buộc chấm dứt quá trình JVM; ví dụ: kill -9 <pid> trên UNIX
  5. Nếu hệ thống máy chủ chết; ví dụ: mất điện, lỗi phần cứng, hoảng loạn hệ điều hành, et cetera
  6. Nếu khối finally sẽ được thực thi bởi một luồng daemon và tất cả các luồng không phải daemon khác thoát trước khi finally được gọi
2415
jodonnell

Mã ví dụ:

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.");
    }
}

Đầu ra:

finally trumps return. 
0
502
Kevin

Ngoài ra, mặc dù đó là thông lệ xấu, nhưng nếu có một tuyên bố trả lại trong khối cuối cùng, nó sẽ vượt qua bất kỳ sự trở lại nào khác từ khối thông thường. Đó là, khối sau sẽ trả về false:

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

Điều tương tự với ném ngoại lệ từ khối cuối cùng.

362
MooBob42

Đây là những từ chính thức từ Đặc tả ngôn ngữ Java.

14.20.2. Thực hiện thử cuối cùng và thử bắt cuối cùng

Câu lệnh try có khối finally được thực thi bằng cách thực hiện đầu tiên khối try. Sau đó, có một lựa chọn:

  • Nếu việc thực thi khối try hoàn thành bình thường, [...]
  • Nếu việc thực thi khối try hoàn thành đột ngột vì một throw có giá trị V, [...]
  • Nếu việc thực thi khối try đột ngột hoàn thành vì bất kỳ lý do nào khác R, thì khối finally được thực thi. Sau đó, có một lựa chọn: [.__.]
    • Nếu khối cuối cùng hoàn thành bình thường, thì câu lệnh try hoàn thành đột ngột vì lý do R.
    • Nếu khối finally hoàn thành đột ngột vì lý do S, thì câu lệnh try hoàn thành đột ngột vì lý do S ( và lý do R bị loại bỏ ).

Đặc tả cho return thực sự làm cho điều này rõ ràng:

JLS 14,17 Tuyên bố hoàn trả

ReturnStatement:
     return Expression(opt) ;

Một câu lệnh return không có Expression nỗ lực để chuyển điều khiển sang invoker của phương thức hoặc hàm tạo có chứa nó.

Một câu lệnh return với một Expression nỗ lực để chuyển điều khiển cho kẻ xâm lược của phương thức có chứa nó; giá trị của Expression trở thành giá trị của lời gọi phương thức.

Các mô tả trước nói " lần thử để chuyển điều khiển" thay vì chỉ "điều khiển chuyển" bởi vì nếu có bất kỳ câu lệnh try nào trong phương thức hoặc hàm tạo có khối try chứa câu lệnh return, sau đó mọi mệnh đề finally của các câu lệnh try đó sẽ được thực thi, theo thứ tự, trong cùng đến ngoài cùng, trước khi quyền điều khiển được chuyển cho kẻ xâm phạm phương thức hoặc hàm tạo. Việc hoàn thành một mệnh đề finally đột ngột có thể phá vỡ sự chuyển giao quyền kiểm soát được bắt đầu bằng câu lệnh return.

249
polygenelubricants

Ngoài các phản hồi khác, điều quan trọng là chỉ ra rằng 'cuối cùng' có quyền ghi đè bất kỳ giá trị ngoại lệ/trả về nào bằng khối try..catch. Ví dụ: đoạn mã sau trả về 12:

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

Tương tự, phương pháp sau đây không đưa ra một ngoại lệ:

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

Trong khi phương pháp sau đây ném nó:

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

Tôi đã thử ví dụ trên với sửa đổi nhỏ-

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.");
    }
}

Các đầu ra mã trên:

cuối cùng vấp ngã trở về.
[.__.] 2

Điều này là do khi return i; được thực thi i có giá trị 2. Sau đó, khối finally được thực thi trong đó 12 được gán cho i và sau đó System.out out được thực thi.

Sau khi thực hiện khối finally, khối try trả về 2, thay vì trả về 12, vì câu lệnh trả về này không được thực hiện lại.

Nếu bạn sẽ gỡ lỗi mã này trong Eclipse thì bạn sẽ có cảm giác rằng sau khi thực thi System.out của finally chặn câu lệnh return của khối try lại được thực thi. Nhưng đây không phải là trường hợp. Nó chỉ đơn giản trả về giá trị 2.

109
vibhash

Đây là một công phu của Câu trả lời của Kevin . Điều quan trọng cần biết là biểu thức được trả về được đánh giá trước finally, ngay cả khi nó được trả về sau.

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");
    }
}

Đầu ra:

X
finally trumps return... sort of
0
95
WoodenKitty

Đó là toàn bộ ý tưởng của một khối cuối cùng. Nó cho phép bạn chắc chắn rằng bạn thực hiện việc dọn dẹp có thể bị bỏ qua vì bạn quay lại, trong số những thứ khác, tất nhiên.

Cuối cùng được gọi bất kể điều gì xảy ra trong khối thử ( trừ khi bạn gọi System.exit(int) hoặc Máy ảo Java khởi động vì một số lý do khác).

52
Chris Cooper

Một cách hợp lý để suy nghĩ về điều này là:

  1. Mã được đặt trong một khối cuối cùng phải được thực thi bất cứ điều gì xảy ra trong khối thử
  2. Vì vậy, nếu mã trong khối thử cố gắng trả về giá trị hoặc ném ngoại lệ, mục đó được đặt 'trên giá' cho đến khi khối cuối cùng có thể thực thi
  3. Bởi vì mã trong khối cuối cùng có (theo định nghĩa) mức độ ưu tiên cao, nó có thể trả về hoặc ném bất cứ thứ gì nó thích. Trong trường hợp bất cứ điều gì còn lại "trên kệ" sẽ bị loại bỏ.
  4. Ngoại lệ duy nhất cho điều này là nếu VM tắt hoàn toàn trong khối thử, ví dụ: bởi 'System.exit'
38
Garth Gilmour

cuối cùng luôn được thực thi trừ khi có sự chấm dứt chương trình bất thường (như gọi System.exit (0) ..). vì vậy, sysout của bạn sẽ được in

18
shyam

Ngoài ra một sự trở lại trong cuối cùng sẽ vứt bỏ bất kỳ ngoại lệ. http://jamesjava.blogspot.com/2006/03/dont-return-in-finally-clause.html

18
James A. N. Stauffer

Khối cuối cùng luôn được thực thi trừ khi có sự chấm dứt chương trình bất thường, hoặc là do sự cố JVM hoặc từ một cuộc gọi đến System.exit(0).

Trên hết, bất kỳ giá trị nào được trả về từ trong khối cuối cùng sẽ ghi đè giá trị được trả về trước khi thực hiện khối cuối cùng, vì vậy hãy cẩn thận kiểm tra tất cả các điểm thoát khi sử dụng thử cuối cùng.

17
user9189

Không, không phải lúc nào cũng có một trường hợp ngoại lệ là // System.exit (0); trước khi khối cuối cùng ngăn chặn cuối cùng được thực thi.

  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");
        }
    }
}
17
Rajendra Jadi

Cuối cùng luôn luôn là toàn bộ vấn đề, chỉ vì nó xuất hiện trong mã sau khi hoàn trả không có nghĩa đó là cách nó được triển khai. Thời gian chạy Java có trách nhiệm chạy mã này khi thoát khỏi khối try.

Ví dụ: nếu bạn có những điều sau đây:

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

Thời gian chạy sẽ tạo ra một cái gì đó như thế này:

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

Nếu một ngoại lệ chưa được ném được ném, khối finally sẽ chạy và ngoại lệ sẽ tiếp tục lan truyền.

12
Motti

Điều này là do bạn đã gán giá trị của i là 12, nhưng không trả về giá trị của i cho hàm. Mã chính xác như sau:

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

Bởi vì một khối cuối cùng sẽ luôn được gọi trừ khi bạn gọi System.exit() (hoặc chuỗi bị hỏng).

9
Jay Riggs

Trả lời rất đơn giảnC&OACUTE;.

ĐẦU VÀO:

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");
}

ĐẦU RA:

catch
finally
9
Meet

Vâng, nó sẽ được gọi. Đó là toàn bộ quan điểm của việc có một từ khóa cuối cùng. Nếu nhảy ra khỏi khối thử/bắt có thể bỏ qua khối cuối cùng thì nó cũng giống như đặt System.out.println bên ngoài thử/bắt.

8
Mendelt

Vâng, cuối cùng khối luôn luôn được thực thi. Hầu hết các nhà phát triển sử dụng khối này để đóng kết nối cơ sở dữ liệu, đối tượng resultset, đối tượng câu lệnh và cũng sử dụng vào chế độ ngủ đông Java để khôi phục giao dịch.

8
Gautam Viradiya

Chính xác, trong Tài liệu Java chính thức (Bấm tại đây ), nó được viết rằng -

Nếu JVM thoát trong khi mã thử hoặc bắt đang được thực thi, thì khối cuối cùng có thể không thực thi. Tương tự, nếu luồng thực thi mã thử hoặc bắt bị gián đoạn hoặc bị giết, khối cuối cùng có thể không thực thi mặc dù toàn bộ ứng dụng vẫn tiếp tục.

8
bikz05

Nó sẽ được thôi. Chỉ có trường hợp nó không thoát JVM hoặc gặp sự cố

7
abhig

Nó sẽ được thôi. Bất kể điều gì xảy ra trong khối thử hoặc bắt của bạn trừ khi System.exit () được gọi hoặc JVM bị hỏng. nếu có bất kỳ câu lệnh return nào trong (các) khối, cuối cùng sẽ được thực thi trước câu lệnh return đó.

7
Karthikeyan

Hãy xem xét chương trình sau:

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");
        }
    }
}

Kể từ Java 1.8.162, khối mã trên cho ra kết quả sau:

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

điều này có nghĩa là sử dụng finally để giải phóng các đối tượng là một cách thực hành tốt như đoạn mã sau:

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.
    }
}
7
Samim

Tôi đã thử điều này, nó là luồng đơn.

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");
       }
   }
}

Chuỗi chính sẽ ở trạng thái chờ mãi mãi, do đó cuối cùng sẽ không bao giờ được gọi,

vì vậy, đầu ra của bàn điều khiển sẽ không in chuỗi: after wait() hoặc finally

Đồng ý với @Stephen C, ví dụ trên là một trong những trường hợp thứ 3 được đề cập tại đây :

Thêm một số khả năng lặp vô hạn như vậy trong mã sau:

// 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");
        }
    }
}

Trường hợp 2: Nếu JVM gặp sự cố trước

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);
    }
}

Tham chiếu: Làm thế nào để bạn đánh sập một JVM?

Trường hợp 6: Nếu khối cuối cùng sẽ được thực thi bởi luồng daemon và tất cả các luồng không phải daemon khác thoát trước khi cuối cùng được gọi.

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("-------------------------------------------------");
    }
}

đầu ra: Điều này không in "cuối cùng" có nghĩa là "Cuối cùng chặn" trong "chủ đề daemon" đã không thực thi

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
6
dkb

Có, bởi vì không có câu lệnh kiểm soát có thể ngăn finally được thực thi.

Dưới đây là một ví dụ tham khảo, trong đó tất cả các khối mã sẽ được thực thi:

| 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      |

Trong biến thể bên dưới, return x; sẽ bị bỏ qua. Kết quả vẫn là 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--;
    }
}

Tài liệu tham khảo, tất nhiên, theo dõi tình trạng của họ. Ví dụ này trả về một tham chiếu với 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
    }
}
6
Dávid Horváth

finally sẽ thực thi và đó là điều chắc chắn.

finally sẽ không thực thi trong các trường hợp dưới đây:

trường hợp 1 :

Khi bạn đang thực thi System.exit().

trường hợp 2:

Khi JVM/Thread của bạn gặp sự cố.

trường hợp 3:

Khi thực hiện của bạn được dừng lại ở giữa bằng tay.

6
Utkash Bhatt

try- catch- finally là những từ khóa để sử dụng trường hợp xử lý ngoại lệ.
[.__.] Như lời giải thích bình thường

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
}

Khối cuối cùng ngăn chặn thực thi ...

  • Khi bạn gọi System.exit(0);
  • Nếu JVM thoát.
  • Lỗi trong JVM
6
Poorna Senani Gamage

Nếu bạn không xử lý ngoại lệ, trước khi kết thúc chương trình, JVM cuối cùng sẽ thực thi chặn. Nó sẽ không được thực thi chỉ khi việc thực thi chương trình bình thường sẽ thất bại trong việc chấm dứt chương trình vì những lý do sau đây ..

  1. Bằng cách gây ra một lỗi nghiêm trọng khiến quá trình hủy bỏ.

  2. Chấm dứt chương trình do bộ nhớ bị hỏng.

  3. Bằng cách gọi System.exit ()

  4. Nếu chương trình đi vào vòng lặp vô cực.

6
Vikas Suryawanshi

Điều đó thực sự đúng trong bất kỳ ngôn ngữ nào ... cuối cùng sẽ luôn luôn thực thi trước câu lệnh return, bất kể sự trở lại đó nằm ở đâu trong thân phương thức. Nếu đó không phải là trường hợp, khối cuối cùng sẽ không có nhiều ý nghĩa.

6
Scott Dorman

Thêm vào @ Vibhash 'câu trả lời vì không có câu trả lời nào khác giải thích điều gì xảy ra trong trường hợp của một đối tượng có thể thay đổi như câu hỏi dưới đây.

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 ");
    }
}

Sẽ xuất

sbupdated 
6
Pradeep Kumaresan

Vâng, nó được viết ở đây

Nếu JVM thoát trong khi mã thử hoặc bắt đang được thực thi, thì khối cuối cùng có thể không thực thi. Tương tự, nếu luồng thực thi mã thử hoặc bắt bị gián đoạn hoặc bị giết, khối cuối cùng có thể không thực thi mặc dù toàn bộ ứng dụng vẫn tiếp tục.

5
Danail Tsvetanov

Ngoài điểm về lợi nhuận cuối cùng thay thế lợi nhuận trong khối thử, điều tương tự cũng đúng với một ngoại lệ. Khối cuối cùng ném ngoại lệ sẽ thay thế trả lại hoặc ngoại lệ được ném từ bên trong khối thử.

5
Alex Miller

KHÔNG LUÔN

Đặc tả ngôn ngữ Java mô tả cách các khối try-Catch-cuối cùng và try-Catch hoạt động tại 14.20.2
[.___.] Không có nơi nào xác định rằng khối cuối cùng luôn được thực thi. Nhưng đối với tất cả các trường hợp trong đó các khối thử-bắt-cuối và thử cuối cùng hoàn thành, nó xác định rằng trước khi hoàn thành cuối cùng phải được thực thi.

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 không đảm bảo rằngFINđược thực thi sauCODE. JLS đảm bảo rằng nếuCODENEXTđược thực thi thìFINsẽ luôn được thực thi sauCODEvà trướcTIẾP THEO.

Tại sao JLS không đảm bảo rằng khối cuối cùng luôn được thực thi sau khối thử? Bởi vì điều đó là không thể. Không thể nhưng có thể JVM sẽ bị hủy bỏ (giết, sụp đổ, tắt nguồn) ngay sau khi hoàn thành khối thử nhưng trước khi thực hiện khối cuối cùng. Không có gì JLS có thể làm để tránh điều này.

Do đó, bất kỳ phần mềm nào cho hành vi đúng của chúng phụ thuộc vào các khối cuối cùng luôn được thực thi sau khi các khối thử hoàn thành của chúng bị lỗi.

Trả về trong khối thử không liên quan đến vấn đề này. Nếu việc thực thi đạt được mã sau lần thử bắt cuối cùng thì đảm bảo rằng khối cuối cùng sẽ được thực thi trước đó, có hoặc không trả về bên trong khối thử.

5

Khối cuối cùng sẽ không được gọi sau khi trả về trong một vài tình huống duy nhất: nếu System.exit () được gọi đầu tiên hoặc nếu JVM gặp sự cố.

Hãy để tôi cố gắng trả lời câu hỏi của bạn theo cách dễ nhất có thể.

Quy tắc 1 : Khối cuối cùng luôn chạy (Mặc dù vẫn có ngoại lệ đối với nó. Nhưng đôi khi chúng ta hãy giữ nguyên điều này.)

Quy tắc 2 : các câu lệnh trong khối cuối cùng chạy khi điều khiển rời khỏi khối thử hoặc khối bắt. Việc chuyển điều khiển có thể xảy ra do thực hiện bình thường, thực hiện ngắt, tiếp tục, goto hoặc a tuyên bố trở lại, hoặc của một ngoại lệ.

Trong trường hợp câu lệnh trả về cụ thể (kể từ chú thích của nó), điều khiển phải rời khỏi phương thức gọi và do đó gọi khối cuối cùng của cấu trúc thử cuối cùng tương ứng. Câu lệnh return được thực thi sau khối cuối cùng.

Trong trường hợp cũng có câu lệnh trả về trong khối cuối cùng, nó chắc chắn sẽ ghi đè lên câu lệnh đang chờ xử lý tại khối thử, vì nó xóa ngăn xếp cuộc gọi.

Bạn có thể tham khảo một lời giải thích tốt hơn ở đây: http://msdn.Microsoft.com/en-us/ .... khái niệm này hầu hết giống nhau trong tất cả các ngôn ngữ cấp cao.

5
Sandip Solanki
  1. Cuối cùng, Block luôn được thực thi. Trừ khi và cho đến khi System.exit () câu lệnh tồn tại ở đó (câu lệnh đầu tiên trong khối cuối cùng).
  2. Nếu system.exit () là câu lệnh đầu tiên thì cuối cùng khối sẽ không được thực thi và kiểm soát ra khỏi khối cuối cùng. Bất cứ khi nào câu lệnh System.exit () được chặn cuối cùng cho đến khi câu lệnh đó cuối cùng được thực thi và khi System.exit () xuất hiện thì lực điều khiển hoàn toàn thoát ra khỏi khối cuối cùng.
5
Avinash Pande

Tương tự với mã sau:

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

f sẽ trả lại 2!

4
dibo

Bởi vì trận chung kết luôn được gọi trong bất kỳ trường hợp nào bạn có. Bạn không có ngoại lệ, nó vẫn được gọi, bắt ngoại lệ, nó vẫn được gọi

4
vodkhang

Có, nó sẽ luôn được gọi nhưng trong một tình huống, nó sẽ không gọi khi bạn sử dụng 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.
}
4
Akash Manngroliya

Cuối cùng, khối luôn thực thi cho dù xử lý ngoại lệ hay không. Nếu có ngoại lệ xảy ra trước khi khối thử thì cuối cùng khối sẽ không thực thi.

4
Rohit Chugh

Nếu một ngoại lệ được ném, cuối cùng chạy. Nếu một ngoại lệ không được ném, cuối cùng sẽ chạy. Nếu ngoại lệ bị bắt, cuối cùng chạy. Nếu ngoại lệ không bị bắt, cuối cùng chạy.

Chỉ có thời gian nó không chạy là khi JVM thoát.

4
Bhushan

Hãy xem xét điều này trong một quá trình thực thi thông thường (tức là không có ngoại lệ nào bị ném): nếu phương thức không phải là 'void' thì nó luôn trả về một cách rõ ràng một cái gì đó, tuy nhiên, cuối cùng luôn luôn được thực thi

4
Gala101

Hãy thử mã này, bạn sẽ hiểu mã trong khối cuối cùng được thực thi sau câu lệnh return .

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;
    }
}
4
eric2323223

Tôi đã rất bối rối với tất cả các câu trả lời được cung cấp trên các diễn đàn khác nhau và cuối cùng quyết định viết mã và xem. Thông số là:

cuối cùng sẽ được thực thi ngay cả khi có trở lại trong khối thử và bắt.

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");
}

Đầu ra

thử

In cho tôi cuối cùng

  1. Nếu trả về được thay thế bằng System.exit(0) trong khối thử và bắt trong mã ở trên và một ngoại lệ xảy ra trước nó, vì bất kỳ lý do nào.
4
milton

cuối cùng cũng có thể được thoát ra sớm nếu một ngoại lệ được ném vào bên trong khối cuối cùng được lồng. Trình biên dịch sẽ cảnh báo bạn rằng khối cuối cùng không hoàn thành bình thường hoặc đưa ra lỗi mà bạn có mã không thể truy cập được. Lỗi cho mã không thể truy cập sẽ chỉ được hiển thị nếu cú ​​ném không nằm sau câu lệnh có điều kiện hoặc bên trong vòng lặp.

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

Cuối cùng luôn được gọi ở cuối

khi bạn thử, nó sẽ thực thi một số mã, nếu có gì đó xảy ra trong thử, thì việc bắt sẽ bắt ngoại lệ đó và bạn có thể in một số mssg ra hoặc ném lỗi, cuối cùng thì khối được thực thi.

Cuối cùng thường được sử dụng khi thực hiện dọn dẹp, ví dụ, nếu bạn sử dụng máy quét trong Java, có lẽ bạn nên đóng máy quét vì nó dẫn đến các vấn đề khác như không thể mở một số tệp

2
Rubin Luitel

khối cuối cùng được thực thi luôn ngay cả khi bạn đặt câu lệnh return trong khối try. Khối cuối cùng sẽ được thực hiện trước câu lệnh return.

2
Sabrina

Dưới đây là một số điều kiện có thể bỏ qua một khối cuối cùng:

  1. Nếu JVM thoát trong khi mã thử hoặc bắt đang được thực thi, thì khối cuối cùng có thể không thực thi. Thêm về Sun guide
  2. Tắt máy bình thường - điều này xảy ra khi luồng không phải daemon cuối cùng thoát OR khi Runtime.exit () ( một số blog tốt ). Khi một luồng thoát ra, JVM thực hiện kiểm kê các luồng đang chạy và nếu các luồng duy nhất còn lại là các luồng daemon, thì nó sẽ khởi động tắt máy theo thứ tự. Khi JVM tạm dừng, mọi luồng daemon còn lại sẽ bị hủy bỏ cuối cùng các khối không được thực thi, các ngăn xếp không được giải phóng JVM vừa thoát. Chủ đề Daemon nên được sử dụng một cách tiết kiệm, một vài hoạt động xử lý có thể bị bỏ rơi một cách an toàn bất cứ lúc nào mà không cần dọn dẹp. Cụ thể, thật nguy hiểm khi sử dụng các luồng daemon cho các tác vụ có thể thực hiện bất kỳ loại I/O nào. Các chủ đề của Daemon được lưu tốt nhất cho các tác vụ "Housekeep", chẳng hạn như một chủ đề nền loại bỏ định kỳ các mục đã hết hạn khỏi bộ nhớ cache trong bộ nhớ ( nguồn )

Ví dụ thoát chủ đề không phải daemon cuối cùng:

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.");
    }
}

Đầu ra:

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
0
Mykhaylo Adamovych