2012-06-29 10 views
21

Mam następujący fragment kodu w bloku try/catchzamykające inputstreams w Javie

InputStream inputstream = conn.getInputStream(); 
InputStreamReader inputstreamreader = new InputStreamReader(inputstream); 
BufferedReader bufferedreader = new BufferedReader(inputstreamreader); 

moje pytanie jest, że kiedy mam do zamknięcia tych strumieni w bloku finally, muszę zamknąć wszystkie 3 strumienie lub po prostu zamknięcie czytnikazamkną wszystkie inne strumienie?

+1

Możliwy duplikat [Czy muszę zamknąć() zarówno FileReader, jak i BufferedReader?] (Http://stackoverflow.com/questions/1388602/do-i-need-to-close-both-filereader-and-bufferedreader) – River

Odpowiedz

24

Zgodnie z konwencją, strumienie owijki (które zawijają istniejące strumienie) zamykają zamknięty strumień, gdy są zamknięte, tak więc należy tylko zamykać bufferedreader w twoim przykładzie. Poza tym zamknięcie już zamkniętego strumienia jest zwykle nieszkodliwe, więc zamknięcie wszystkich 3 strumieni nie zaszkodzi.

+0

Nie można zagwarantować, że implementacja strumienia nie wygeneruje wyjątku podczas zamykania, jeśli jest już blisko. Lepiej więc trzymać się właściwej drogi i zamknąć tylko BufferedReader – Soronthar

+1

@Soronthar Następnie ta implementacja strumienia jest nieważna na mocy umowy, która stwierdza: "Jeśli strumień jest już zamknięty, wywołanie tej metody nie ma żadnego skutku." Wszystkie prawidłowe implementacje MUSZĄ zachowywać się w ten sposób. Jest całkowicie legalne (i często) zamykanie już zamkniętego strumienia. Zazwyczaj zamykasz go po raz pierwszy w try-block i po raz drugi w bloku finally, gdzie zamykanie w try-block daje ci szansę zareagowania na wyjątek, a zamknięcie w finally block to zapewnienie zamknięcia. –

+0

@FabianBarney masz rację, zapomniałem, że doposażono strumienie w zamknięty interfejs w JSE 6. – Soronthar

0

Zgodnie z ogólną zasadą zamknij wszystko w odwrotnej kolejności, w jakiej je otworzyłeś.

0

chciałbym zamknąć wszystkie z nich w odwrotnej kolejności, od których je otworzył, jakby podczas otwierania ich doprowadziłaby czytelnika do stosu i zamykanie będzie pop czytelnika ze stosu.

W końcu, po zamknięciu wszystkich, „stos czytelnik” musi być pusta.

+0

Jaki jest twój powód tego zalecenia? Kiedy filtrowane strumienie wejściowe i czytniki są określone, aby zamknąć zagnieżdżony strumień w funkcji close()? – EJP

+0

Po prostu zdrowy rozsądek, tak myślę. W tym wątku są lepsze odpowiedzi, jak pokazują głosy. Niektórzy nawet pokazali kod źródłowy biblioteki. –

3

Zamknięcie jedną zewnętrzną jest wystarczająca (tj BufferedReader). Czytając source code of BufferedReader widzimy, że to zamyka wewnętrzny Reader gdy jego własna blisko metoda nazywa się:

513  public void close() throws IOException { 
514   synchronized (lock) { 
515    if (in == null) 
516     return; 
517    in.close(); 
518    in = null; 
519    cb = null; 
520   } 
521  } 
522 } 
0

Wystarczy tylko zamknąć rzeczywistego zasobu. Należy zamknąć zasób, nawet jeśli nie uda się zbudować dekoratorów. Aby uzyskać wynik, należy opróżnić obiekt najbardziej dekorator w szczęśliwym przypadku.

Niektóre powikłania:

  • Czasami dekoratorzy są różne zasoby (niektóre implementacje kompresji użyć C sterty).
  • Zamknięcie dekoratorów w smutnych przypadkach faktycznie powoduje opróżnienia, co powoduje zamieszanie, które nie powoduje zamknięcia zasób.
  • Wygląda na to, że zasób będący podstawą to URLConnection, który nie ma metody jako takiej.

Możesz rozważyć użycie polecenia Wykonaj dookoła, aby nie musieć duplikować tego typu rzeczy.

4

Normalnie jest ok po prostu zamknąć najbardziej zewnętrzną strumienia, ponieważ zgodnie z przyjętą konwencją musi wyzwalać blisko na strumieniach bazowych.

Więc normalnie kod wygląda następująco:

BufferedReader in = null; 

try { 
    in = new BufferedReader(new InputStreamReader(conn.getInputStream())); 
    ... 
    in.close(); // when you care about Exception-Handling in case when closing fails 
} 
finally { 
    IOUtils.closeQuietly(in); // ensure closing; Apache Commons IO 
} 

jednak nie mogą być rzadkie przypadki, w których strumień bazowy konstruktor zgłasza wyjątek, jeśli strumień jest już otwarty. W takim przypadku powyższy kod nie zamknie strumienia bazowego, ponieważ zewnętrzny konstruktor nigdy nie został wywołany, a in ma wartość null. Tak więc blok finally nie zamyka niczego pozostawiając otwarty strumień bazowy.

Ponieważ Java 7 można to zrobić:

try (OutputStream out1 = new ...; OutputStream out2 = new ...) { 
     ... 
     out1.close(); //if you want Exceptions-Handling; otherwise skip this 
     out2.close(); //if you want Exceptions-Handling; otherwise skip this    
    } // out1 and out2 are auto-closed when leaving this block 

W większości przypadków nie chcesz obsługi wyjątków, gdy podniesiona podczas zamykania więc pominąć te wyraźny Close() zwraca.

Edytuj Oto kod dla niewierzących, w przypadku gdy użycie tego wzoru jest zasadne. Możesz także przeczytać Apache Commons IOUtils javadoc o metodzie closeQuietly().

OutputStream out1 = null; 
    OutputStream out2 = null; 

    try { 
     out1 = new ...; 
     out2 = new ...; 

     ... 

     out1.close(); // can be skipped if we do not care about exception-handling while closing 
     out2.close(); // can be skipped if we ... 
    } 
    finally { 
     /* 
     * I've some custom methods in my projects overloading these 
     * closeQuietly() methods with a 2nd param taking a logger instance, 
     * because usually I do not want to react on Exceptions during close 
     * but want to see it in the logs when it happened. 
     */ 
     IOUtils.closeQuietly(out1); 
     IOUtils.closeQuietly(out2); 
    } 

Korzystanie @ Toma „porady” pozostawi out1 otwarty, gdy stworzenie out2 podnosi wyjątek. Ta rada pochodzi od kogoś, kto mówi o It's a continual source of errors for obvious reasons. Cóż, mogę być ślepy, ale nie jest to dla mnie oczywiste. Mój wzorzec jest bezpieczny dla idioty w każdym przypadku użycia, jaki mogę sobie wyobrazić, podczas gdy wzorzec Toma jest podatny na błędy.

+0

Proszę nie robić tego rodzaju tańca "zerowego". Jest to ciągłe źródło błędów z oczywistych powodów. A co za bałagan. –

+1

To jest normalny wzór. Co jeszcze chcesz robić? –

+0

'acquire(); spróbuj {use(); } finally {release(); } ' –