2013-08-07 36 views
166

Tôi có một vấn đề nhỏ lý thuyết với các cấu trúc cố gắng nắm bắt.Java ngoại lệ không bị bắt?

Tôi hít một kỳ thi thực tế ngày hôm qua về Java và tôi không hiểu ví dụ sau:

try { 
    try { 
     System.out.print("A"); 
     throw new Exception("1"); 
    } catch (Exception e) { 
     System.out.print("B"); 
     throw new Exception("2"); 
    } finally { 
     System.out.print("C"); 
     throw new Exception("3"); 
    } 
} catch (Exception e) { 
    System.out.print(e.getMessage()); 
} 

Câu hỏi đặt ra là "những gì đầu ra sẽ trông như thế nào?"

Tôi khá chắc chắn nó sẽ là AB2C3, NHƯNG bất ngờ, điều đó không đúng.

Câu trả lời đúng là ABC3 (đã được kiểm tra và thực sự là như thế).

Câu hỏi của tôi là, Ngoại lệ ("2") ở đâu?

+8

+1 Ahh người đàn ông, tôi biết câu trả lời này. Tôi đã được hỏi điều này trong một cuộc phỏng vấn. Đó là một câu hỏi rất hay để hiểu cách try/catch/cuối cùng hoạt động trên stack. –

+10

Chỉ có một lệnh in có thể in một số (lệnh cuối cùng: 'print (e.getMessage())'). Bạn nghĩ đầu ra sẽ là 'AB2C3': bạn có nghĩ rằng khối' catch' ngoài cùng sẽ được thực hiện hai lần không? –

+0

Trong java, trước khi một lệnh chuyển điều khiển ra khỏi khối catch được thực hiện, khối cuối cùng được thực hiện miễn là nó tồn tại. Nếu chỉ có mã trong khối cuối cùng không chuyển điều khiển ra bên ngoài, lệnh trì hoãn từ khối catch được thực thi. – Thomas

Trả lời

192

Từ Java Language Specification 14.20.2.:

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, sau đó báo cáo kết quả thử hoàn thành đột ngột vì lý do S (và lý do R bị loại bỏ).

Vì vậy, khi có một khối catch đó ném một ngoại lệ:

try { 
    // ... 
} catch (Exception e) { 
    throw new Exception("2"); 
} 

nhưng đó cũng là một khối cuối cùng cũng ném một ngoại lệ:

} finally { 
    throw new Exception("3"); 
} 

Exception("2") sẽ bị hủy và chỉ Exception("3") sẽ được nhân rộng.

+71

Điều này thậm chí còn đúng với các câu lệnh 'return'. Nếu khối cuối cùng của bạn có trả về, nó sẽ ghi đè bất kỳ trở lại nào trong khối 'try' hoặc' catch'. Bởi vì những "tính năng", một thực hành tốt là cuối cùng khối nên ** không bao giờ ** ném một ngoại lệ hoặc có một tuyên bố trở lại. – Augusto

+0

Đây cũng là lợi ích kế thừa của tài nguyên thử có trong Java 7. Nó bảo tồn ngoại lệ ban đầu nếu một ngoại lệ thứ cấp được tạo ra khi đóng tài nguyên, thường làm cho việc gỡ rối dễ dàng hơn. – w25r

19

Ngoại lệ được ném vào cuối cùng chặn chặn ngoại lệ được ném trước đó trong khối thử hoặc chặn.

Java 7 Ví dụ: http://ideone.com/0YdeZo

Từ Javadoc's dụ:


static String readFirstLineFromFileWithFinallyBlock(String path) 
                throws IOException { 
    BufferedReader br = new BufferedReader(new FileReader(path)); 
    try { 
     return br.readLine(); 
    } finally { 
     if (br != null) br.close(); 
    } 
} 

Tuy nhiên, trong ví dụ này, nếu các phương pháp Readline và gần cả hai ném trường hợp ngoại lệ, sau đó phương pháp readFirstLineFromFileWithFinallyBlock ném ngoại lệ được ném từ khối cuối cùng; ngoại lệ được ném từ khối thử bị chặn.


mới try-with cú pháp của Java 7 có thêm một bước của ức chế ngoại lệ: Các ngoại lệ ném vào khối try ngăn chặn những ném vào đầu thử với các phần.

từ cùng một ví dụ:

try (
     java.util.zip.ZipFile zf = new java.util.zip.ZipFile(zipFileName); 
     java.io.BufferedWriter writer = java.nio.file.Files.newBufferedWriter(outputFilePath, charset) 
    ) { 
     for (java.util.Enumeration entries = zf.entries(); entries.hasMoreElements();) { 
      String newLine = System.getProperty("line.separator"); 
      String zipEntryName = ((java.util.zip.ZipEntry)entries.nextElement()).getName() + newLine; 
      writer.write(zipEntryName, 0, zipEntryName.length()); 
     } 
    } 

Một ngoại lệ có thể được ném từ các khối mã liên quan đến báo cáo kết quả thử với các nguồn tổng hợp. Trong ví dụ trên, một ngoại lệ có thể được bỏ ra từ khối thử và có thể ném tối đa hai trường hợp ngoại lệ từ báo cáo try-with-resources khi nó cố gắng đóng các đối tượng ZipFile và BufferedWriter. Nếu một ngoại lệ được ném từ khối thử nghiệm và một hoặc nhiều trường hợp ngoại lệ được đưa ra từ tuyên bố thử nghiệm với tài nguyên , thì những ngoại lệ được đưa ra từ câu lệnh try-with-resources bị chặn và ngoại lệ được ném bởi khối là khối được đưa ra bởi phương thức writeToFileZipFileContents. Bạn có thể truy xuất các trường hợp ngoại lệ bị chặn này bằng cách gọi phương thức Throwable.getSuppressed từ trường hợp ngoại lệ được ném bởi khối thử.


Trong mã từ câu hỏi, mỗi khối được rõ ràng loại bỏ các ngoại lệ cũ, thậm chí không đăng nhập nó, không tốt khi bạn đang cố gắng để giải quyết một số lỗi:

http://en.wikipedia.org/wiki/Error_hiding

9

throw new Exception("2"); được ném từ catch khối và không được try, nó sẽ không bị bắt lại.
Xem 14.20.2. Execution of try-finally and try-catch-finally.

Đây là những gì xảy ra:

try { 
    try { 
     System.out.print("A");   //Prints A 
     throw new Exception("1"); 
    } catch (Exception e) { 
     System.out.print("B");   //Caught from inner try, prints B 
     throw new Exception("2"); 
    } finally { 
     System.out.print("C");   //Prints C (finally is always executed) 
     throw new Exception("3"); 
    } 
} catch (Exception e) { 
    System.out.print(e.getMessage()); //Prints 3 since see (very detailed) link 
} 
+0

vâng, tôi thấy điều này đang xảy ra, nhưng tôi đang tìm kiếm giải thích - tại sao nó hoạt động theo cách này – Kousalik

2

Khối finally luôn chạy. Hoặc là bạn return từ bên trong khối thử hoặc một ngoại lệ được ném. Ngoại lệ được ném trong khối finally sẽ ghi đè lên khối được ném vào nhánh bắt.

Ngoài ra, việc ném ngoại lệ sẽ không tự gây ra bất kỳ đầu ra nào. Dòng throw new Exception("2"); sẽ không ghi bất cứ điều gì ra ngoài.

+1

vâng, tôi biết ném đầu ra Ngoại lệ không có gì, nhưng tôi không thấy lý do, tại sao Ngoại lệ 2 nên bị loại bỏ . Tôi là một chút thông minh hơn một lần nữa :-) – Kousalik

+0

luôn luôn là thời gian rất dài và trong thời gian rất dài bất cứ điều gì có thể xảy ra (kiểm tra câu đố http://wouter.coekaerts.be/2012/puzzle-dreams) – Dainius

4

Câu hỏi của bạn rất rõ ràng và câu trả lời rất đơn giản ở cùng mức độ. Đối tượng ngoại lệ với thông báo là "2" được ghi đè bởi đối tượng Ngoại lệ với thông báo là "3".

Giải thích: Khi một ngoại lệ xảy ra, đối tượng của nó ném để bắt khối để xử lý. Nhưng khi ngoại lệ xảy ra trong khối catch, đối tượng của nó được chuyển tới OUTCH CATCH Block (nếu có) để xử lý ngoại lệ. Và cùng xảy ra ở đây. Đối tượng ngoại lệ với thông điệp "2" được chuyển tới khối catch OUTER. Nhưng hãy đợi ..Trước khi rời khỏi khối try-catch bên trong, HAS TO EXECUTE FINALLY. Ở đây xảy ra sự thay đổi mà chúng ta quan tâm. Một đối tượng EXCEPTION mới (với thông báo "3") được ném ra hoặc khối cuối cùng này đã thay thế đối tượng Exception đã được ném (với thông báo "2"). giá trị ghi đè có nghĩa là "3" và không phải "2".

Hãy nhớ rằng: Chỉ có một đối tượng ngoại lệ có thể được xử lý bởi trên khối CATCH.

0

Theo mã của bạn:

try { 
    try { 
     System.out.print("A"); 
     throw new Exception("1"); // 1 
    } catch (Exception e) { 
     System.out.print("B");  // 2 
     throw new Exception("2"); 
    } finally {      // 3 
     System.out.print("C");  // 4 
     throw new Exception("3"); 
    } 
} catch (Exception e) {    // 5 
    System.out.print(e.getMessage()); 
} 

Như bạn có thể thấy ở đây:

  1. in A và ném ngoại lệ # 1;
  2. ngoại lệ này đã xảy ra do phát biểu bắt và in B - # 2;
  3. khối cuối cùng # 3 thực thi sau tuyên bố try-catch (hoặc chỉ thử, nếu không xảy ra bất kỳ ngoại lệ) và in C - # 4 và ném ngoại lệ mới;
  4. điều này đã bị bắt bởi tuyên bố khai thác bên ngoài # 5;

Kết quả là ABC3. Và 2 bị bỏ qua theo cùng một cách như 1

+0

Xin lỗi, ngoại lệ (" 1 ") không được bỏ qua, nhưng được bắt thành công –

+0

@Black Maggie Nó được lưu trữ và ném ngoại lệ mới => điều này không được lưu trữ và chương trình bị chấm dứt. Và trước khi khối này cuối cùng được thực hiện. –