2010-04-13 9 views
37

Powiedziano mi, że jest pewien narzut w korzystaniu z mechanizmu próbnego Java. Tak więc, chociaż konieczne jest umieszczenie metod, które rzucają zaznaczony wyjątek w bloku try, aby obsłużyć ewentualny wyjątek, dobrą praktyką jest ograniczenie rozmiaru bloku try, aby zawierał tylko te operacje, które mogą zgłaszać wyjątki.Czy java powinien wypróbowywać bloki tak ściśle, jak to możliwe?

Nie jestem pewien, czy to rozsądny wniosek.

Rozważ dwie poniższe implementacje funkcji przetwarzającej określony plik tekstowy.

Nawet jeśli prawdą jest, że pierwszy z nich powoduje pewne niepotrzebne obciążenie, o wiele łatwiej jest go śledzić. Mniej jasne jest, gdzie dokładnie wyjątki pochodzą z samego spojrzenia na stwierdzenia, ale komentarze wyraźnie pokazują, które oświadczenia są odpowiedzialne.

Druga jest znacznie dłuższa i bardziej skomplikowana niż pierwsza. W szczególności, ładny line-czytanie idiom z pierwszych ma być zniekształcone, aby pasowały do ​​readLine wezwanie do bloku try.

Co to jest najlepsze praktyki do obsługi wyjątków w funcion gdzie wiele wyjątków może być rzucony w jej definicji?

Ten jeden zawiera cały kod przetwarzanie wewnątrz bloku try:

void processFile(File f) 
{ 
    try 
    { 
    // construction of FileReader can throw FileNotFoundException 
    BufferedReader in = new BufferedReader(new FileReader(f)); 

    // call of readLine can throw IOException 
    String line; 
    while ((line = in.readLine()) != null) 
    { 
     process(line); 
    } 
    } 
    catch (FileNotFoundException ex) 
    { 
    handle(ex); 
    } 
    catch (IOException ex) 
    { 
    handle(ex); 
    } 
} 

Ten jeden zawiera tylko metody, które generują wyjątki w blokach try:

void processFile(File f) 
{ 
    FileReader reader; 
    try 
    { 
    reader = new FileReader(f); 
    } 
    catch (FileNotFoundException ex) 
    { 
    handle(ex); 
    return; 
    } 

    BufferedReader in = new BufferedReader(reader); 

    String line; 
    while (true) 
    { 
    try 
    { 
     line = in.readLine(); 
    } 
    catch (IOException ex) 
    { 
     handle(ex); 
     break; 
    } 

    if (line == null) 
    { 
     break; 
    } 

    process(line); 
    } 
} 
+10

"Przedwczesna optymalizacja" to wyrażenie używane do opisania sytuacji, w której programista uwzględnia aspekty związane z wydajnością, wpływające na projekt fragmentu kodu. Może to spowodować, że projekt nie będzie tak czysty, jak mógłby być, lub kod jest niepoprawny, ponieważ kod jest skomplikowany przez optymalizację, a programista jest rozproszony przez optymalizację. - Wikipedia [http://en.wikipedia.org/wiki/Program_optimization#When_to_optimize] –

+2

"Powinniśmy zapomnieć o małych wydajnościach, powiedzmy o 97% przypadków: przedwczesna optymalizacja jest źródłem wszelkiego zła. nasze możliwości w tym krytycznym 3%. " - Knuth [http://stackoverflow.com/questions/211414/is-premature-optimization-really-the-roof-all-evil] –

Odpowiedz

45

Podstawową przesłanką jest tutaj false: wielkość bloku try nie ma wpływu na wydajność. Wydajność jest zależna od rzeczywistego zgłaszania wyjątków w czasie wykonywania i jest to niezależne od rozmiaru bloku try.

Jednak utrzymanie małych bloków może prowadzić do lepszych programów.

Możesz wychwycić wyjątki, aby odzyskać i kontynuować, lub możesz je złapać po prostu, aby zgłosić je do dzwoniącego (lub do człowieka, za pośrednictwem jakiegoś interfejsu użytkownika).

W pierwszym przypadku awarie, z których można odzyskać, są często bardzo specyficzne, a to prowadzi do mniejszych bloków try.

W drugim przypadku, gdy wyjątek zostanie przechwycony, aby można go było zawinąć w inny wyjątek i ponownie wyświetlić lub wyświetlić użytkownikowi, małe bloki try oznaczają, że wiesz dokładniej, która operacja się nie udała, i kontekst, w którym wywołano to połączenie. Pozwala to tworzyć bardziej szczegółowe raporty o błędach.

Oczywiście istnieją & hellip; wyjątki (przepraszam!) do tych wytycznych. Na przykład w niektórych przypadkach bardzo szczegółowe raporty o błędach mogą stanowić problem z bezpieczeństwem.


To może być przydatne wiedzieć jaki wpływ try blok ma na skompilowanego kodu. W ogóle nie zmienia instrukcji skompilowanych! (Oczywiście, odpowiedni blok catch robi, ponieważ jest jak każdy inny kod.)

Blok try tworzy wpis w tabeli wyjątków związanych z tą metodą. Ta tabela zawiera zakres liczników instrukcji źródłowych, typ wyjątku i instrukcję docelową. Gdy zostanie zgłoszony wyjątek, ta tabela jest sprawdzana w celu sprawdzenia, czy istnieje wpis z pasującym typem oraz zakresu zawierającego instrukcję podnoszącą wyjątek. Jeśli tak, wykonanie rozgałęzia się na odpowiedni numer docelowy.

Należy zdać sobie sprawę, że ta tabela nie jest konsultowana (i nie ma żadnego wpływu na działanie wydajności), chyba że jest potrzebna. (Zaniedbywanie niewielkiego obciążenia podczas ładowania klasy).

+6

Dokładnie. Warto również zauważyć, dla rekordu, że narzut związany z try-catch występuje tylko wtedy, gdy zostanie zgłoszony wyjątek lub blok ma klauzulę final, zgodnie ze specyfikacją VM http://java.sun.com/docs/ books/jvms/second_edition/html/Compiling.doc.html # 9934. – ig0774

+0

@ ig0774 Tak więc, jeśli dodaję blok finally, np. 'Finally {in.close(); } ', a związane z tym obciążenie zaczyna działać? Z tego, co rozumiem w specyfikacji, klauzula finally po prostu dodaje dodatkową instrukcję do skompilowanego bloku try, aby wywołać blok finally jako podprogram przed powrotem. –

+0

+1 dla odniesienia do specyfikacji, btw. –

1

istnieje bardzo niewiele korzyści druga metoda. w końcu jeśli uda ci się otworzyć plik, ale go nie odczytasz, oznacza to, że coś jest nie tak z twoim komputerem. stąd wiedza, że ​​wyjątek io pochodzi z metody readLine(), jest bardzo rzadko przydatna. jak wiesz, różne wyjątki są zgłaszane dla różnych problemów w każdym razie (FileNotFoundException, itp)

tak długo, jak zakres go z "logiczny" blok, tj. otwieranie, czytanie i zamykanie pliku w 1 go, chciałbym idź z pierwszą metodą. jest znacznie łatwiejszy do odczytania i, szczególnie w przypadku IO, cykle procesora używane przez obciążenie try-catch byłyby minimalne, jeśli jakiekolwiek.

0

Drugi sposób generuje błąd reader kompilator, które nie mogły zostać zainicjowany. Można obejść to przez inicjowanie go null, ale to tylko oznacza, można uzyskać NPE, i nie ma przewagę do tego.

+0

Nie zostanie wygenerowany taki błąd kompilatora. Instrukcja 'return' na końcu bloku' catch' upewnia się, że czytnik zostanie zainicjowany lub nigdy nie zostanie odczytany! Oczywiście dodałem to stwierdzenie dopiero po tym, jak kompilator wydał dokładnie opisany błąd. :) –

+0

Całkiem tak - tęskniłem za tym. Dzięki Bogu, kompilator jest tutaj, aby dowiedzieć się tego dla nas. :) –

11

Powiedziano mi, że istnieje pewne obciążenie w korzystaniu z mechanizmu próbnego Java.

Absolutnie. A także do wywołań metodowych. Ale nie powinieneś umieszczać całego kodu w jednej metodzie.

Nie można wysłuchać przedwczesnego sygnału optymalizacji, ale należy skupić się na łatwości czytania, organizacji itd. Konstrukcje językowe rzadko wpływają na wydajność, a także na organizację systemu i wybór algorytmów.

Dla mnie pierwszy jest najłatwiejszy do odczytania.

1

Stawianie bloków try wokół konkretnego kodu, który może rzucić wyjątek, czyni to, moim zdaniem łatwiejsze do odczytania.Prawdopodobnie chcesz wyświetlić inny komunikat dla każdego błędu i dostarczyć użytkownikowi instrukcje, które będą różne w zależności od miejsca wystąpienia błędu.

Jednak problem z wydajnością, do którego odnosi się większość osób, dotyczy podniesienia wyjątku, a nie samego bloku try.

Innymi słowy, dopóki nie popełnisz błędu, blok try nie wpłynie znacząco na wydajność. Nie powinieneś brać pod uwagę bloku try innego konstruktora kontroli przepływu i zgłaszaj błąd, aby rozgałęziać się przez twój kod. Tego właśnie chcesz uniknąć.

+2

Tylko dodatkowa uwaga na twoim ostatnim akapicie. Jeśli masz próbę/catch * wewnątrz * pętli, możesz wychwycić wyjątek i kontynuować pętlę. Jeśli twoja próba/catch jest * poza * pętli, twoja pętla zostanie przerwana. Każda z nich może być tym, czego chcesz i wpłynie na miejsce, w którym spróbujesz/złapiesz. –

3

Nie. Jedyne, co należy wziąć pod uwagę, to gdzie można rozsądnie sobie poradzić z wyjątkiem i jakie zasoby trzeba odzyskać (w końcu).

+0

+1 za wzmiankę o czyszczeniu zasobów. W OP tylko pierwsza implementacja nadaje się do dodania pojedynczej klauzuli "finally", która zamyka plik. –

+0

+1 za wzmiankę o oczyszczeniu. Do tej pory nigdy nie nazwałem 'close' na żadnym z obiektów' Reader', których użyłem. Wstyd mi! –

+0

Ile kosztuje wypróbowanie przy użyciu zasobów Java 7 w celu zmiany preferencji? –

2

To jest przedwczesna optymalizacja w najgorszym. Nie rób tego.

"Powinniśmy zapomnieć o małych wydajnościach, powiedzmy o 97% przypadków: przedwczesna optymalizacja jest źródłem wszelkiego zła" - Knuth.

Powiązane problemy