2015-06-22 16 views
63

Đây là một câu hỏi phỏng vấn:Nhiều báo cáo lợi nhuận mà không cần biên dịch lỗi

public class Demo { 

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

    static String foo() { 
     try { 
      return "try ..."; 
     } catch (Exception e) { 
      return "catch ..."; 
     } finally { 
      return "finally ..."; //got as result 
     } 
    } 
} 

Câu hỏi của tôi là lý do tại sao không có lỗi thời gian biên dịch. Khi tôi có tuyên bố trả về trong khối finally của mình, nó nhất định trả về từ finally thay vì số trycatch. Tôi đã cố gắng biên dịch mã này với tùy chọn -Xlint, nó đưa ra cảnh báo là.

warning: [finally] finally clause cannot complete normally 
+32

Ngôn ngữ nào trong đặc tả Java bạn có tin rằng điều này bị hỏng, điều đó sẽ yêu cầu một lỗi? –

Trả lời

76

Nó không đưa ra lỗi biên dịch vì nó được đặc tả ngôn ngữ Java cho phép. Tuy nhiên, nó đưa ra một thông điệp cảnh báo bởi vì bao gồm một tuyên bố return trong khối finally thường là một ý tưởng tồi.

Điều gì xảy ra trong ví dụ của bạn là như sau. Câu lệnh return trong khối try được thực hiện. Tuy nhiên, khối finally phải luôn được thực hiện để nó được thực thi sau khi khối catch kết thúc. Câu lệnh return xảy ra ở đó ghi đè kết quả của câu lệnh return trước đó và do đó phương thức trả về kết quả thứ hai.

Tương tự, khối finally thường không được ném ngoại lệ. Đó là lý do tại sao cảnh báo cho biết rằng khối finally phải hoàn thành bình thường, nghĩa là không có return hoặc ném một ngoại lệ.

+2

Câu trả lời hay! Giải thích tốt và ví dụ rõ ràng. Tốt hơn là chỉ trích dẫn thông số – higuaro

10

Không có lỗi thời gian biên dịch vì chỉ 1 và chính xác 1 câu hỏi return sẽ thực sự trả về điều khiển quay lại mã gọi.

Như được giải thích @Hoopje, return trong phạm vi try hoặc catch sẽ thực thi trước, câu lệnh trả về tương ứng của chúng cũng sẽ thực thi. Nhưng ngay trước khi trả lại quyền kiểm soát trở lại mã gọi, nó sẽ thực thi khối finally. Bây giờ, điều này block cũng return s một cái gì đó, do đó, điều này trở lại ghi đè trước đó.

+2

Điều này thực sự không đúng. Hai câu lệnh return sẽ thực thi, nhưng câu lệnh thứ hai sẽ ghi đè lên câu lệnh đầu tiên. – Hoopje

+0

Ồ. Tôi không biết về điều đó. Xin vui lòng cho tôi một số tài liệu tham khảo để tôi có thể tìm hiểu về điều này. – Aakash

+0

Vâng, bạn có thể thử nó cho mình bằng cách sử dụng không phải là một hằng số nhưng một chức năng mà in một cái gì đó như là đối số. Bạn sẽ thấy rằng đối số của cả hai câu lệnh 'return' được đánh giá. – Hoopje

5

Mã của bạn hoạt động tốt vì chỉ có một câu lệnh trả về trong thử, bắt và cuối cùng là chặn. Lỗi biên dịch sẽ xảy ra nếu bạn cố gắng viết hai câu lệnh trả về bên trong một trong các lệnh try, catch hoặc finally, cho biết có một câu lệnh return không thể truy cập được.

8

Câu hỏi nổi bật .. Theo tuyên bố trả về kiến ​​thức của tôi về khối try và catch được chuyển đến cuối cùng nếu bạn đã thêm cuối cùng sẽ chặn mã của bạn. Đó là cách hoạt động của nó.

Vì vậy, trong trường hợp này tất cả các dòng mã đang thực thi và bạn có thể thử gỡ lỗi. Cả ba khối tôi đã thử bên dưới mã.

public class Main { 

    public static void main(String[] args) { 
     System.out.println(foo()); 
    } 
    static String foo() { 
     try { 
      throw new Exception(); 
     } catch (Exception e) { 
      return "catch ..."; 
     } finally { 
      return "finally ..."; //got as result 
     } 
    } 
} 

Bạn có thể lấy ý tưởng từ liên kết bên dưới. Multiple returns: Which one sets the final return value?

8

Đó là về cơ bản giống như thế này:

public boolean someMethod(){ 
     if(1 == 1){ 
      return true; 
     } 
     return false; 
} 

Nó sẽ không đưa ra một lỗi biên dịch, mặc dù nó sẽ cung cấp một cảnh báo. Trình biên dịch sẽ chỉ đưa ra một lỗi khi có cơ hội số câu lệnh trả về đang được thực thi.

39

này được mô tả trong ngôn ngữ Java Specification:

§14.17

hoàn đột ngột của một điều khoản finally có thể phá vỡ việc chuyển giao kiểm soát khởi xướng bởi một tuyên bố return.

§14.20.2

Nếu thực hiện của khối try hoàn bình thường, sau đó các finally khối được thực thi, và sau đó là một sự lựa chọn:

  • Nếu khối finally hoàn bình thường, sau đó câu lệnh try hoàn thành bình thường.
  • Nếu khối finally hoàn đột ngột vì lý do S, sau đó báo cáo kết quả try hoàn đột ngột vì lý do S.

Nếu thực hiện của khối try hoàn đột ngột đối với bất kỳ lý do nào khác R, sau đó khối finally được thực hiện , và sau đó là một sự lựa chọn:

  • Nếu khối finally hoàn bình thường, sau đó báo cáo kết quả try hoàn độ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ị hủy).
4

(Đối với ngắn answer- Đọc đậm và nghiêng phần của câu trả lời)

Dòng chảy thực hiện theo các tài liệu Java 8. Nó cung cấp cho bạn các chi tiết. Bạn có thể suy ra việc thực hiện các câu lệnh trả về dựa trên những điều sau đây.

Tuyên bố thử với khối cuối cùng được thực hiện bằng cách thực hiện khối thử lần đầu tiên.

Sau đó, có một sự lựa chọn:

• Nếu thực hiện các khối try hoàn bình thường, sau đó khối cuối cùng được thực hiện, và sau đó là một sự lựa chọn:

- Nếu khối cuối cùng hoàn thành bình thường , sau đó câu lệnh thử hoàn thành bình thường.

- Nếu khối cuối cùng hoàn thành đột ngột vì lý do S, sau đó báo cáo kết quả thử hoàn đột ngột vì lý do S.

• Nếu thực hiện các khối try hoàn đột ngột vì một ném của một giá trị V, sau đó có một sự lựa chọn:

- Nếu kiểu thời gian chạy của V được gán tương ứng với một ngoại lệ bắt buộc lớp bất kỳ điều khoản bắt nào của câu lệnh thử, thì điều khoản bắt buộc đầu tiên (ngoài cùng bên trái) là . Giá trị V được gán cho tham số của mệnh đề bắt được chọn là và khối Lệnh bắt được thực thi.

Sau đó, có một sự lựa chọn:

> Nếu khối catch hoàn bình thường, sau đó khối cuối cùng đượ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ì câu lệnh try hoàn tất bình thường.

»Nếu khối cuối cùng hoàn thành đột ngột vì bất kỳ lý do gì, thì câu lệnh try hoàn thành đột ngột vì lý do tương tự.

Nếu khối catch hoàn thành đột ngột vì lý do R, thì khối cuối cùng đượ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, sau đó báo cáo kết quả thử hoàn đột ngột vì lý do R.

» Nếu khối cuối cùng hoàn thành đột ngột vì lý do S thì thử câu lệnh hoàn thành đột ngột vì lý do S (và lý do R bị hủy).

- Nếu kiểu thời gian chạy của V không phải là nhiệm vụ tương thích với một lớp ngoại lệ catchable của bất kỳ mệnh đề catch của câu lệnh try, sau đó khối cuối cùng đượ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, sau đó báo cáo kết quả thử hoàn đột ngột vì một ném của V. giá trị

> Nếu khối cuối cùng hoàn thành đột ngột cho lý do S, sau đó câu lệnh try hoàn thành đột ngột vì lý do S (và vứt bỏ giá trị V bị loại bỏ và bị lãng quên).

• Nếu thực hiện các khối try hoàn đột ngột đối với bất kỳ lý do R khác, sau đó khối finally được thực thi, và sau đó là một sự lựa chọn:

- Nếu khối cuối cùng hoàn thành bình thường, sau đó báo cáo kết quả thử hoàn thành đột ngột vì lý do R.

- Nếu khối cuối cùng hoàn thành đột ngột vì lý do S, thì câu lệnh thử hoàn thành đột ngột vì lý do S (và lý do R bị hủy).

lời giải thích rõ ràng trong việc này link- javaDoc

2

thử chạy này:

nó sẽ in: 1, 2, 3 và sau đó ném một phép chia cho không ngoại lệ

public class Demo { 
    public static void main(String[] args) { 
    System.out.println(foo()); 
    } 
    public static String print(int a){ 
    System.out.println(a); 
    return String.valueOf(a/0); 
    } 
    static String foo() { 
    try { 
     return print(1); 
    } catch (Exception e) { 
     return print(2); 
    } finally { 
     return print(3); 
    } 
    } 
} 
Các vấn đề liên quan