Câu hỏi Cuối cùng có luôn luôn thực thi trong Java không?


Xem xét mã này, tôi có thể hoàn toàn chắc chắn rằng finally khối luôn thực hiện, không có vấn đề gì something() Là?

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

1907
2017-09-15 17:43


gốc


Ngay cả sau khi tự mình thử nghiệm, bạn chỉ biết của bạn triển khai hoặc phiên bản Java / JVM sẽ thực hiện và sẽ không biết mã Nên làm (theo tiêu chuẩn), và do đó câu hỏi vẫn là một câu hỏi hợp lệ. Tôi rất vui khi thấy những khía cạnh này được giải quyết trong số các câu trả lời khác nhau. - Rhubbarb
không phải luôn luôn - Boann
Java hiệu quả nói cách khác informit.com/articles/article.aspx?p=1216151&seqNum=7 - Binoy Babu
@BinoyBabu, người hoàn thành ! = finally; người hoàn thành == finalize() phương pháp. - jaco0646
@LordFarquaad ;-) Điều đó thực sự luôn được đảm bảo. - MC Emperor


Các câu trả lời:


Vâng, finally sẽ được gọi sau khi thực hiện các khối mã thử hoặc nắm bắt.

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 bị lỗi 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 kết nối khác, không kết thúc) trong try hoặc là catch khối;
  4. Nếu hệ điều hành buộc chấm dứt quá trình JVM; ví dụ. "kill -9" 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, hệ điều hành hoảng loạn, vv.
  6. Nếu cuối cùng khối sẽ được thực hiện bởi chủ đề daemon và tất cả các chủ đề không daemon khác thoát trước khi cuối cùng được gọi.

2122
2017-09-15 17:45



Thực ra thread.stop() không nhất thiết phải ngăn chặn finally chặn được thực hiện. - Piotr Findeisen
Làm thế nào về chúng tôi nói rằng finally khối sẽ được gọi sau các try chặn và trước kiểm soát chuyển đến các câu sau. Đó là 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
cũng có trường hợp khác, khi chúng ta sử dụng lồng nhau cố gắng nắm bắt khối - ruhungry
Ngoài ra, cuối cùng khối không được gọi trong trường hợp ngoại lệ được ném bởi chuỗi daemon. - Amrish Pandey
@BinoyBabu - Đó là về finalizer, cuối cùng không chặn - avmohan


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

446
2017-09-15 17:59



FYI: Trong C #, hành vi giống hệt với thực tế là thay thế câu lệnh trong finally-nhưng với return 2; không được phép (Compiler-Error). - Alexander Pacha
Dưới đây là một chi tiết quan trọng cần lưu ý: stackoverflow.com/a/20363941/2684342 - WoodenKitty
Bạn thậm chí có thể thêm một câu lệnh trả về trong chính khối cuối cùng, sau đó nó sẽ ghi đè lên giá trị trả về trước đó. Điều này cũng kỳ diệu loại bỏ các ngoại lệ. Tại thời điểm đó, bạn nên xem xét tái cấu trúc mã của bạn. - Zyl
Điều đó không thực sự chứng minh rằng cuối cùng trumps trở lại. Giá trị trả lại được in từ mã người gọi. Dường như không chứng minh được nhiều. - Trimtab
Xin lỗi, nhưng đây là một minh chứng không phải là 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 luôn hoạt động 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 luôn hành xử theo cách này. - Stephen C


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

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

Cùng một điều với ném ngoại lệ từ khối cuối cùng.


325
2017-09-15 18:19



Đây là một thực tế xấu thực sự. Xem stackoverflow.com/questions/48088/… để biết thêm về lý do tại sao nó xấu. - John Meagher
Đã đồng ý. Cuối cùng, {} bỏ qua bất kỳ ngoại lệ nào được đưa ra trong try {}. Đáng sợ! - neu242
@ 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ó nên khác khi hàm / phương thức bị vô hiệu? - corsiKa
Vì lý do tương tự, tôi KHÔNG BAO GIỜ sử dụng goto trong mã C ++ của tôi. Tôi nghĩ rằng nhiều lợi nhuận làm cho nó khó đọc hơn và khó khăn hơn để gỡ lỗi (tất nhiên trong trường hợp thực sự đơn giản nó không áp dụng). Tôi đoán đó chỉ là sự thích hợp của mọi người 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
Tôi có xu hướng sử dụng một số lợi nhuận khi một số trường hợp ngoại lệ xảy ra. Giống như (có một lý do không tiếp tục) trở lại; - iHearGeoff


Đâ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à cố gắng nắm bắt

A try tuyên bố với một finally khối được thực hiện bằng cách thực hiện đầu tiên try khố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 thực hiện try khối hoàn thành đột ngột vì một throw của một giá trị V, [...]
  • Nếu thực hiện try khối hoàn thành đột ngột vì bất kỳ lý do nào khác R, sau đó là finally khối được thực hiện. 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ì try tuyên bố hoàn thành vì lý do R.
    • Nếu finally khối hoàn thành đột ngột vì lý do S, sau đó là try tuyên bố hoàn thành vì lý do S (và lý do R bị loại bỏ).

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

JLS 14.17 Báo cáo trả lại

ReturnStatement:
     return Expression(opt) ;

A return tuyên bố không có Expression  cố gắng để chuyển quyền kiểm soát cho người gọi của phương thức hoặc hàm tạo có chứa nó.

A return tuyên bố với một Expression  cố gắng để chuyển quyền kiểm soát cho người gọi của phương thứ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 "cố gắng để chuyển điều khiển"thay vì chỉ"điều khiển chuyển"bởi vì nếu có try các câu lệnh trong phương thức hoặc hàm tạo try khối chứa return tuyên bố, sau đó bất kỳ finally các mệnh đề của những try các câ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 tới người gọi của phương thức hoặc hàm tạo. Hoàn thành đột ngột finally mệnh đề có thể làm gián đoạn việc truyền điều khiển được khởi xướng bởi return tuyên bố.


229
2018-05-25 06:50





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 đè lên bất kỳ giá trị ngoại lệ / trả về nào bằng khối try..catch. Ví dụ, mã sau trả về 12:

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

Tương tự, phương thức sau không ném ngoại lệ:

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

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

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

134
2018-05-13 07:11



Cần lưu ý rằng trường hợp trung gian chính xác là lý do tại sao có một tuyên bố trả về bên trong một khối cuối cùng là hoàn toàn khủng khiếp (nó có thể ẩn bất kỳ Throwable). - Dimitris Andreou


Tôi đã thử ví dụ trên với sự 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 cũng trở lại.
  2

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

Sau khi thực hiện finally chặn try khối trả về 2, thay vì trả về 12, bởi vì câu lệnh return 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 return tuyên bố của try khố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ỉ trả về giá trị 2.


88
2017-11-17 16:25



Ví dụ này là tuyệt vời, nó thêm một cái gì đó mà đã không được đề cập trong hàng chục chủ đề liên quan cuối cùng. Tôi nghĩ hầu như không một nhà phát triển nào cũng biết điều này. - HopefullyHelpful
Chuyện gi xảy ra nêu i không phải là một nguyên thủy, mà là một đối tượng Integer. - Yamcha
Tôi đang gặp khó khăn trong việc hiểu rõ trường hợp này. docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.17 nói rằng, "Một tuyên bố trở lại với một biểu hiện cố gắng để chuyển quyền kiểm soát cho người gọi của phương pháp hoặc cơ thể lambda có chứa nó .... Nếu đánh giá của biểu thức hoàn thành bình thường, tạo ra một giá trị V .." Những gì tôi có thể đoán từ tuyên bố này là - có vẻ như là trả lại không đánh giá biểu thức một lần nữa khi nó đánh giá giá trị V, đó là lý do tại sao thay đổi tô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 điều này, ở đâu nó nói rằng sự trở lại không đánh giá biểu hiện một lần nữa. - meexplorer
@meexplorer một chút muộn, nhưng nó được giải thích trong JLS 14.20.2. Thực hiện thử cuối cùng và cố gắng nắm bắt - nói một chút phức tạp, 14.17. Báo cáo trả lại cũng phải được đọc - Carlos Heuberger


Đây là một sự xây dựng 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ả lại 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

80
2017-12-03 23:36



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


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

Cuối cùng được gọi là 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).


46
2018-05-13 06:19





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

  1. Mã được đặt trong 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ề một giá trị hoặc ném một 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ột ưu tiên cao, nó có thể trả lại hoặc ném bất cứ điều 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'

31
2017-09-15 19:26



Đây có phải chỉ là "một cách hợp lý để suy nghĩ về nó" hoặc là nó thực sự làm thế nào khối cuối cùng được dự định để làm việc 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
Điểm 4 mâu thuẫn với whatever happens tuyên bố tại điểm 1 - ojonugwa ochalifu


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


14
2017-09-15 19:26