2013-10-29 30 views
5

Giả sử chúng ta đang viết một thư viện Java, cung cấp một số chức năng I/O ulitity, ví dụ, một phương pháp thuận tiện để đọc các file văn bản như Strings:Tạo lỗi nghiêm trọng trong Java

public class StringReader { 

private static final Logger log = LoggerFactory.getLog(StringReader.class); 

/** 
* Returns the contents of file <b>fileName</b> as String. 
* @param fileName file name to read 
* @return null on IO error 
*/ 
public static String readString(String fileName) { 
    FileInputStream fis = null; 
    try { 
     fis = new FileInputStream(fileName); 
     byte[] data = new byte[fis.available()]; 
     fis.read(data); 
     return new String(data, "ISO-8859-1"); // may throw UnsupportedEncodingException! 
    } catch (IOException e) { 
     log.error("unable to read file", e); 
    } catch (UnsupportedEncodingException e) { 
     log.fatal("JRE does not support ISO-8859-1!", e); 
     // ??? 
    } finally { 
     closeQuiet(fis); 
    } 

    return null; 
} 
} 

Mã này đọc một văn bản tập tin vào một String sử dụng mã hóa ISO-8859-1 và trả về String cho người dùng.

Nhà xây dựng String(byte[], String) ném một số UnsupportedEncodingException khi mã hóa được chỉ định không được hỗ trợ. Nhưng, như chúng ta biết, ISO-8859-1 phải được JRE hỗ trợ, như đã nói here (see the Standard charsets section).

Do đó, chúng tôi kỳ vọng khối

catch (UnsupportedEncodingException e) { 
    log.fatal("encoding is unsupported", e); 
    // ??? 
} 

không bao giờ đạt được nếu phân phối JRE phù hợp với tiêu chuẩn.

Nhưng điều gì sẽ xảy ra nếu không? Làm thế nào để xử lý ngoại lệ này một cách chính xác nhất? Câu hỏi đặt ra là, cách cảnh báo đúng về lỗi như vậy?

Những gợi ý là:

  1. Ném một số loại RuntimeException.
  2. Không tắt trình ghi nhật ký trong mã sản xuất, viết chi tiết ngoại lệ trong nhật ký và bỏ qua nó.
  3. Đặt assert false tại đây, do đó, nó tạo AssertionError nếu người dùng khởi chạy VM với -ea.
  4. Ném AssertionError theo cách thủ công.
  5. Thêm UnsupportedEncodingException vào khai báo phương pháp và cho phép người dùng chọn. Không phải rất thuận tiện, tôi nghĩ vậy.
  6. Gọi System.exit(1).

Cảm ơn.

Trả lời

7

Nhưng điều gì sẽ xảy ra nếu không?

Sau đó, bạn đang ở trong tình huống thực sự tồi tệ và có thể bạn nên thoát khỏi nó càng nhanh càng tốt. Khi JRE vi phạm những lời hứa riêng của mình, những gì sẽ bạn muốn phụ thuộc vào?

Tôi rất vui khi sử dụng AssertionError trong trường hợp này.Điều quan trọng cần lưu ý là không phải tất cả các trường hợp ngoại lệ không được kiểm soát đều được xử lý như nhau - không bắt buộc phải viết mã số Exception ở cấp cao nhất của ngăn xếp, ghi lỗi và tiếp tục ... nếu bạn chỉ cần ném RuntimeException, sẽ bị phát hiện bởi một chương trình như vậy. AssertionError sẽ chỉ bị bắt nếu khối catch được chỉ định Throwable (hoặc cụ thể là Error hoặc AssertionError, nhưng điều đó hiếm hơn nhiều để xem). Do điều này không thể thực hiện được như thế nào, tôi nghĩ việc hủy bỏ thật sự là rất hợp lý.

Cũng lưu ý rằng trong Java 7, bạn có thể sử dụng StandardCharsets.ISO_8859_1 thay vì tên chuỗi, đó là sạch hơn và loại bỏ vấn đề.

Có những điều khác tôi muốn thay đổi về mã của bạn, bằng cách này:

  • tôi sẽ tránh sử dụng available() càng xa càng tốt. Điều đó cho bạn biết có bao nhiêu byte có sẵn ngay bây giờ - nó không cho bạn biết tệp này dài bao nhiêu.
  • Tôi sẽ chắc chắn không giả định rằng read() sẽ đọc toàn bộ tệp trong một lần. Gọi read() trong một vòng lặp, lý tưởng cho đến khi nó nói rằng không có nhiều dữ liệu hơn.
  • Cá nhân tôi sẽ chấp nhận thông số Charset thay vì mã hóa ISO-8859-1. - Tôi sẽ để cho IOException bong bóng từ phương thức thay vì chỉ trả lại null. Xét cho cùng, trừ khi bạn đang thực hiện thực sự sẽ kiểm tra giá trị trả lại của mọi cuộc gọi cho giá trị rỗng, bạn sẽ chỉ kết thúc với một thay thế NullPointerException, điều này khó chẩn đoán hơn so với IOException gốc.

Ngoài ra, chỉ cần sử dụng Guava 's Files.toString(File, Charset) để bắt đầu với :) (Nếu bạn chưa sử dụng ổi, bây giờ là thời điểm tốt để bắt đầu ...)

+2

Tôi muốn cung cấp +1 một lần cho mỗi đoạn trong câu trả lời này. –

4

Đây là một sự xuất hiện khá phổ biến trong mã.

Trường hợp ngoại lệ không được kiểm tra được thực hiện cho việc này. Họ không nên xảy ra (đó là lý do tại sao họ không được kiểm soát), nhưng nếu họ làm, vẫn còn một ngoại lệ.

Vì vậy, hãy ném RuntimeException có nguyên bản Exception làm nguyên nhân.

catch (UnsupportedEncodingException e) { 
    throw new RuntimeException(e); //should not happen 
} 

assert(false); cũng ném một ngoại lệ được kiểm soát, nhưng nó khẳng định có thể được tắt, vì vậy tôi muốn giới thiệu RuntimeException.

+0

' assert (false) 'sẽ chỉ ném khi kiểm tra xác nhận được bật mặc dù. –

+0

Tôi có thể đã chỉnh sửa khi bạn đưa ra nhận xét này:/IMO xác nhận thường nên được sử dụng cho các giả định liên quan đến gỡ lỗi, vì chúng có thể được bật và tắt. –

+0

Tôi thích 'AssertionError' hơn' RuntimeException'. Nó làm cho nó rõ ràng hơn rằng đây là một cái gì đó bạn không bao giờ mong đợi xảy ra. 'RuntimeException' đôi khi cũng được sử dụng để bọc các ngoại lệ được mong đợi thường xuyên khác mà không thể đơn giản được ném ra. Ngoài ra những người khác ít có khả năng bắt được một 'Lỗi' hơn là một' Ngoại lệ '. –

Các vấn đề liên quan