2013-10-29 12 views
5

Załóżmy piszemy biblioteki Java, która zapewnia pewne funkcje I/O ulitity na przykład wygodny sposób odczytać pliki tekstowe jako ciągi:Generowanie fatalny błąd w Javie

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

Ten kod odczytuje tekst plik do String za pomocą kodowania ISO-8859-1 i zwraca String do użytkownika.

Konstruktor String(byte[], String) wysyła UnsupportedEncodingException, gdy określone kodowanie nie jest obsługiwane. Ale, jak wiadomo, ISO-8859-1 musi być wspierany przez środowisko JRE, jak powiedział here (see the Standard charsets section).

Stąd oczekujemy blok

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

nigdy nie zostanie osiągnięty, jeśli dystrybucja JRE jest zgodny ze standardem.

Ale co, jeśli nie? Jak obsługiwać ten wyjątek w najbardziej poprawny sposób? Pytanie brzmi, jak prawidłowo powiadomić o takim błędzie?

sugestie są:

  1. Rzut jakiś RuntimeException.
  2. Nie należy wyłączać rejestratora w kodzie produkcyjnym, wpisywać w dzienniku szczegółów wyjątku i ignorować go.
  3. Umieść tutaj kod assert false, aby utworzyć AssertionError, jeśli użytkownik uruchomił maszynę wirtualną pod numerem -ea.
  4. Wyrzuć ręcznie AssertionError.
  5. Dodaj UnsupportedEncodingException w deklaracji metody i pozwól użytkownikowi wybrać. Niezbyt wygodne, jak sądzę.
  6. Zadzwoń pod System.exit(1).

Dzięki.

Odpowiedz

7

Ale co, jeśli nie?

Wtedy jesteś w bardzo złej sytuacji i prawdopodobnie powinieneś wyjść z tego tak szybko, jak to możliwe. Kiedy środowisko JRE narusza własne obietnice, na czym chcesz polegać?

Byłbym szczęśliwy, używając w tym przypadku AssertionError.

Ważne jest, aby pamiętać, że nie wszystkie niesprawdzonych wyjątki są traktowane jednakowo - Nie jest niczym niezwykłym dla kodu złapać Exception na najwyższym poziomie stosu, należy zalogować się błąd, a następnie wracamy ... jeśli tylko rzucać RuntimeException, że będzie złapany przez taki system. AssertionError byłby przechwycony tylko wtedy, gdy blok catch wyszczególnił Throwable (lub konkretnie Error lub AssertionError, ale jest to znacznie rzadsze). Biorąc pod uwagę, jak to niemożliwe, myślę, że rozsądnie jest naprawdę mocno się z tym uporać.

Należy również zauważyć, że w języku Java 7 można użyć nazwy StandardCharsets.ISO_8859_1 zamiast nazwy ciągu, która jest czystsza i usuwa problem.

Istnieją inne rzeczy chciałbym zmienić w kodzie, tak przy okazji:

  • Wolałbym uniknąć stosując available() tak daleko, jak to możliwe. To mówi ci, ile bajtów jest dostępnych teraz - nie mówi ci, jak długo plik jest koniecznie.
  • Zdecydowanie nie zakładam, że read() odczyta cały plik za jednym razem. Zadzwoń pod numer read() w pętli, najlepiej dopóki nie powie, że nie ma więcej danych.
  • Ja osobiście akceptuję parametr Charset zamiast kodowania twardego ISO-8859-1. - Pozwoliłbym, aby IOException bańka się z metody, a nie tylko zwracając null. Ostatecznie, jeśli nie sprawdzasz wartości zwracanej każdego połączenia pod kątem nieważności, zamiast tego pojawi się NullPointerException, który jest trudniejszy do zdiagnozowania niż oryginalny .

Alternatywnie, wystarczy użyć Guava „s Files.toString(File, Charset) zacząć :) (Jeśli nie korzystasz już z guawy, teraz jest dobry moment, aby rozpocząć ...)

+2

Chciałbym dać +1 raz dla każdego akapitu w tej odpowiedzi. –

4

Jest to dość powszechne zjawisko w kodzie.

Dokonano niezaznaczonych wyjątków. Nie powinny się zdarzyć (dlatego nie są zaznaczone), ale jeśli tak, to nadal istnieje wyjątek.

Rzuć więc RuntimeException, która jest oryginalna jako Exception.

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

assert(false); rzuca również niesprawdzony wyjątek, ale twierdzenia można wyłączyć, więc polecam RuntimeException.

+0

' assert (false) 'będzie tylko rzut, gdy sprawdzanie asercji jest włączone. –

+0

Mogłem edytować, gdy robiłeś ten komentarz: asercje IMO powinny być zwykle używane do debugowania związanego z założeniem, ponieważ można je włączać i wyłączać. –

+0

Wolę 'AssertionError' ponad' RuntimeException'. Czyni to wyraźniej, że jest to coś, czego nigdy się nie spodziewasz. 'RuntimeException' jest również czasami używany do zawijania innych regularnie oczekiwanych wyjątków, których nie można po prostu wyrzucić. Również inni ludzie rzadziej wychwytują "Błąd" niż "wyjątek". –

Powiązane problemy