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


2382

Xem xét mã này, tôi có thể hoàn toàn chắc chắn rằng finallykhối 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");
}

473
Nếu không, từ khóa nên được đặt tên probablythay thế.
Trưa Tơ



2
Java hiệu quả nói cách khác là notifyit.com/articles/article.aspx?p=1216151&seqNum=7
Binoy Babu

27
@BinoyBabu, hoàn thiện ! = finally; quyết toán == finalize()phương thức.
jaco0646

Câu trả lời:


2698

Có, finallysẽ được gọi sau khi thực hiện các khối tryhoặc catchmã.

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

  1. Nếu bạn gọi System.exit()
  2. Nếu bạn gọi Runtime.getRuntime().halt(exitStatus)
  3. Nếu JVM gặp sự cố trước
  4. 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 kết thúc khác) trong tryhoặc catchkhối
  5. Nếu HĐH buộc chấm dứt quá trình JVM; ví dụ: kill -9 <pid>trên UNIX
  6. 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
  7. Nếu finallykhối 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 finallyđược gọi

44
Trên thực tế thread.stop()không nhất thiết ngăn finallychặn khối được thực thi.
Piotr Findeisen

181
Làm thế nào về chúng ta nói rằng các finallykhối sẽ được gọi sau khi các trykhối, và trước khi điều khiển đi đến những điều khoản sau đây. Điều đó phù hợp với khối thử liên quan đến một vòng lặp vô hạn và do đó khối cuối cùng không bao giờ thực sự được gọi.
Andrzej Doyle

9
đó cũng là một trường hợp khác, khi chúng ta sử dụng lồng nhau try-catch-cuối cùng khối
ruhungry

7
Ngoài ra, khối cuối cùng không được gọi trong trường hợp ngoại lệ được ném bởi luồng daemon.
Amrish Pandey

14
@BinoyBabu - Đó là về quyết toán, cuối cùng không phải là chặn
avmohan

567

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

18
FYI: Trong C #, hành vi này giống hệt với thực tế là việc thay thế câu lệnh trong phần finally-clse bằng return 2;không được phép (Lỗi trình biên dịch).
Alexander Pacha

15
Dưới đây là một chi tiết quan trọng cần lưu ý: stackoverflow.com/a/20363941/2684342
WoodenKitty

18
Bạn thậm chí có thể thêm một câu lệnh return trong khối cuối cùng, chính nó sẽ ghi đè giá trị trả về trước đó. Điều này cũng kỳ diệu loại bỏ các ngoại lệ chưa được xử lý. Tại thời điểm đó, bạn nên xem xét tái cấu trúc mã của mình.
Zyl

8
Điều đó không thực sự chứng minh rằng cuối cùng đã vượt qua trở lại. Giá trị trả về được in từ mã người gọi. Dường như không chứng minh được nhiều.
Trimtab

20
Xin lỗi, nhưng đây là một cuộc biểu tình không phải là một bằng chứng. Nó chỉ là một bằng chứng nếu bạn có thể chỉ ra rằng ví dụ này luôn hành xử theo cách này trên tất cả các nền tảng Java, VÀ các ví dụ tương tự cũng luôn hành xử theo cách này.
Stephen C

391

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.


95
Đây là một thực tế xấu. Xem stackoverflow.com/questions/48088/ trên để biết thêm thông tin về lý do tại sao nó xấu.
John Meagher

23
Đã đồng ý. Trả về trong vòng cuối cùng {} bỏ qua mọi ngoại lệ được đưa vào thử {}. Đáng sợ!
neu242

8
@ dominicbri7 Tại sao bạn nghĩ rằng đó là một thực hành tốt hơn? Và tại sao nó phải khác khi hàm / phương thức bị trống?
corsiKa

8
Vì lý do tương tự, tôi KHÔNG BAO GIỜ sử dụng goto trong mã C ++ của mình. Tôi nghĩ rằng nhiều lợi nhuận làm cho nó khó đọc hơn và khó gỡ lỗi hơn (tất nhiên trong những trường hợp thực sự đơn giản, nó không áp dụng). Tôi đoán đó chỉ là sự ưa thích cá nhân và cuối cùng bạn có thể đạt được điều tương tự bằng cách sử dụng một trong hai phương pháp
dominicbri7

16
Tôi có xu hướng sử dụng một số lợi nhuận khi một số trường hợp đặc biệt xảy ra. Giống như nếu (có một lý do để không tiếp tục) trở lại;
iHearGeoff

257

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

Một trycâu lệnh với một finallykhối được thực thi bằng cách thực hiện đầu tiên trykhối. Sau đó, có một sự lựa chọn:

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

Các đặc điểm kỹ thuật returnthực sự làm cho điều này rõ ràng:

JLS 14.17 Tuyên bố trở lại

ReturnStatement:
     return Expression(opt) ;

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

Một returntuyên bố với Expression nỗ lực chuyển quyền kiểm soát cho kẻ xâm lược phương thức có chứa nó; giá trị của Expressiontrở thành giá trị của lời gọi phương thức.

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


163

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

63
Cần lưu ý rằng trường hợp giữa chính xác là lý do tại sao có một tuyên bố trả lại bên trong một khối cuối cùng là hoàn toàn khủng khiếp (nó có thể che giấu bất kỳ Ném nào).
Dimitris Andreou

2
Ai không muốn bị khuất phục OutOfMemoryError? ;)
RecursiveExceptionException

Tôi đã thử nó và nó không khắc phục được lỗi như vậy (yipes!). Nó cũng tạo ra một cảnh báo khi tôi biên dịch nó (yay!). Và bạn có thể làm việc xung quanh nó bằng cách định nghĩa một biến trở lại và sau đó sử dụng return retVal sau khi các finallykhối, mặc dù điều đó dĩ nhiên giả định rằng bạn bị ức chế một số trường hợp ngoại lệ khác vì mã sẽ không có ý nghĩa khác.
Maarten Bodewes

120

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 icó giá trị 2. Sau đó, finallykhối này được thực thi trong đó 12 được gán cho ivà sau đó System.outđược thực thi.

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

Nếu bạn sẽ gỡ lỗi mã này trong Eclipse sau đó bạn sẽ nhận được một cảm giác rằng sau khi thực hiện System.outcủa finallykhối các returntuyên bố của trykhối được thực hiện một lần nữa. Nhưng đây không phải là trường hợp. Nó chỉ đơn giản trả về giá trị 2.


10
Ví dụ này là tuyệt vời, nó thêm một cái gì đó đã không được đề cập trong hàng chục chủ đề liên quan cuối cùng. Tôi nghĩ rằng hầu như không có nhà phát triển sẽ biết điều này.
Hy vọng hữu ích

4
Điều gì xảy ra nếu ikhông phải là một nguyên thủy, mà là một đối tượng Integer.
Yamcha

Tôi đang có một thời gian khó hiểu trường hợp này. docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.17 nói rằng " nó .... Nếu việc đánh giá Biểu thức hoàn thành bình thường, tạo ra giá trị V .. "Điều tôi có thể đoán từ tuyên bố này là - có vẻ như trả về không đánh giá lại biểu thức một lần nữa khi nó đánh giá giá trị V, đó là lý do tại sao tôi thay đổi không ảnh hưởng đến giá trị trả về, sửa tôi.
meexplorer

Nhưng tôi không tìm thấy bất kỳ bằng chứng nào liên quan đến vấn đề này, nơi được đề cập rằng lợi nhuận không đánh giá lại biểu thức một lần nữa.
meexplorer

1
@meexplorer hơi muộn, nhưng nó được giải thích trong JLS 14.20.2. Thi hành thử-cuối cùng và thử-bắt-cuối - có vẻ hơi phức tạp, 14,17. Tuyên bố hoàn trả cũng phải được đọc
user85421

117

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

8
Quan trọng phải biết.
Aminadav Glickshtein

Tốt để biết, và có ý nghĩa là tốt. Có vẻ như thực sự trả lại giá trị của lợi nhuận là những gì đến sau finally. Tính giá trị trả về ( printX()ở đây) vẫn đến trước nó.
Albert

một ví dụ điển hình với cả 3 điểm "trở về"!
radistao

Không. Mã ở trên nên thay thế System.out.println("finally trumps return... sort of");bằngSystem.out.print("finally trumps return in try"); return 42;
Pacerier

54

Đó 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).


Đó là một câu trả lời cực kỳ yếu. stackoverflow.com/a/65049/715269
Gangnus

42

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ụ: 'System.exit'

10
Đây chỉ là "một cách hợp lý để suy nghĩ về nó" hay nó thực sự là cách khối cuối cùng dự định hoạt động theo các thông số kỹ thuật? Một liên kết đến một nguồn tài nguyên Mặt trời sẽ rất thú vị ở đây.
matias

21

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

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


18

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

Và đó là một trong những lý do bạn thực sự không bao giờ nên gọi System.exit () ...
Franz D.

13

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 trykhối.

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 ra, finallykhối sẽ chạy và ngoại lệ sẽ tiếp tục lan truyền.


11

Đ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

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


10

Chính xác, trong Tài liệu Java chính thức (Bấm vào đâ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.


10

Trả lời là đơn giản .

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

1
Trả lời là KHÔNG đơn giản.
Barshe Roussy

1
@ChristopheRoussy thế nào? Bạn có thể giải thích được không?
Gặp gỡ

1
đọc câu trả lời được chấp nhận, câu hỏi ban đầu là về 'Nó sẽ luôn luôn thực thi' và nó sẽ không luôn luôn như vậy. Trong trường hợp của bạn nó sẽ nhưng điều này không trả lời câu hỏi ban đầu và thậm chí có thể là người mới bắt đầu gây hiểu lầm.
Barshe Roussy

sau đó trong trường hợp nào nó sẽ không được thực thi?
Gặp

Trong tất cả các trường hợp được đề cập trong các câu trả lời khác, hãy xem câu trả lời được chấp nhận với hơn 1000 lượt upvote.
Barshe Roussy

9

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.


9

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 java ngủ đông để khôi phục giao dịch.


9

KHÔNG LUÔN

Các đặc điểm kỹ thuật Java Ngôn ngữ mô tả cách try- catch- finallytry- catchkhối làm việc tại 14.20.2
Trong không có chỗ nó xác định rằng finallykhối luôn thực thi. Nhưng đối với tất cả các trường hợp trong đó các khối try- catch- finallytry- finallyhoàn thành, nó chỉ định rằng trước khi hoàn thành finallyphả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ằng FIN được thực thi sau CODE . JLS đảm bảo rằng nếu CODENEXT được thực thi thì FIN sẽ luôn được thực thi sau CODE và trước NEXT .

Tại sao JLS không đảm bảo rằng finallykhối luôn được thực thi sau trykhối? Bởi vì điều đó là không thể. Không chắc 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 trykhối nhưng trước khi thực hiện finallykhối. 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 finallycác khối luôn được thực thi sau khi các trykhối của chúng hoàn thành bị lỗi.

returnhướng dẫn trong trykhối không liên quan đến vấn đề này. Nếu thực thi đạt mã sau try- catch- finallyđảm bảo rằng finallykhối sẽ được thực thi trước đó, có hoặc không có returnhướng dẫn bên trong trykhối.


8

Nó sẽ được thôi. Không có vấn đề 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 đó.


8

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


8

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

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 

Kể từ Java 1.8.162, đây không phải là đầu ra.
Sam

8

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

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

Ý main Threadchí sẽ ở waittrạng thái mãi mãi, do đó finallysẽ không bao giờ được gọi ,

vì vậy đầu ra giao diện điều khiển sẽ không print String: sau wait()hoặcfinally

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

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

// import java.util.concurrent.Semaphore;

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

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 khảo: Làm thế nào để bạn đánh sập một JVM?

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

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

4
Xem câu trả lời được chấp nhận. Đây chỉ là một trường hợp cạnh của "vòng lặp vô hạn".
Stephen C

8

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

Không phải sb.setLength(0)cuối cùng thì sao?
user7294900

sb.setLpm (0) sẽ làm trống dữ liệu trong StringBuffer. Vì vậy, sb = null sẽ tách đối tượng khỏi tham chiếu.
Sam

Không nên "xyz" được in hai lần trong đầu ra? Vì hàm được gọi hai lần, tại sao "cuối cùng" chỉ là một lần?
fresko

2
Đây không phải là một thực hành tốt. Điều đó cuối cùng chặn sb = null;chỉ với việc thêm mã không cần thiết. Tôi hiểu rằng bạn có nghĩa là một finallykhối là một nơi tốt để giải phóng tài nguyên như kết nối cơ sở dữ liệu hoặc một cái gì đó tương tự, nhưng hãy nhớ rằng ví dụ của bạn có thể gây nhầm lẫn cho người mới.
Roc Boronat

1
@Samim Cảm ơn, tôi đã thêm các dòng System.out.println("---AGAIN2---"); System.out.println(sb);và bây giờ nó rõ ràng hơn. Như vậy, đầu ra đã chống lại luận điểm của bạn: p Tôi cũng đã thêm vào câu trả lời của bạn, nhưng việc chỉnh sửa phải được chấp nhận bởi người điều hành hoặc ai đó như thế. Khác bạn có thể thêm chúng
fresko

7

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


7

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


7

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 hiện 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
  1. Cuối cùng, Block luôn được thực thi. Trừ khi và cho đến khi câu lệnh System.exit () 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à điều khiển 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.

1
Điều này đã được trả lời nhiều lần, vậy câu trả lời nào của bạn bổ sung?
Tom
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.