2013-07-26 8 views
7

W tym kodzie java,jaka jest dokładna kolejność wykonania dla try, catch i wreszcie?

import java.io.IOException; 

public class Copy 
{ 
    public static void main(String[] args) 
    { 
     if (args.length != 2) 
     { 
     System.err.println("usage: java Copy srcFile dstFile"); 
     return; 
     } 

     int fileHandleSrc = 0; 
     int fileHandleDst = 1; 
     try 
     { 
     fileHandleSrc = open(args[0]); 
     fileHandleDst = create(args[1]); 
     copy(fileHandleSrc, fileHandleDst); 
     } 
     catch (IOException ioe) 
     { 
     System.err.println("I/O error: " + ioe.getMessage()); 
     return; 
     } 
     finally 
     { 
     close(fileHandleSrc); 
     close(fileHandleDst); 
     } 
    } 

    static int open(String filename) 
    { 
     return 1; // Assume that filename is mapped to integer. 
    } 

    static int create(String filename) 
    { 
     return 2; // Assume that filename is mapped to integer. 
    } 

    static void close(int fileHandle) 
    { 
     System.out.println("closing file: " + fileHandle); 
    } 

    static void copy(int fileHandleSrc, int fileHandleDst) throws IOException 
    { 
     System.out.println("copying file " + fileHandleSrc + " to file " + 
         fileHandleDst); 
     if (Math.random() < 0.5) 
     throw new IOException("unable to copy file"); 

     System.out.println("After exception"); 
    } 
} 

wyjście że spodziewam się

copying file 1 to file 2 
I/O error: unable to copy file 
closing file: 1 
closing file: 2 

Jednak czasami otrzymuję ten oczekiwany wynik, a czasami pojawia się następujący komunikat:

copying file 1 to file 2 
closing file: 1 
closing file: 2 
I/O error: unable to copy file 

, a czasami nawet to wyjście:

I/O error: unable to copy file 
copying file 1 to file 2 
closing file: 1 
closing file: 2 

i czy pierwsze, drugie lub trzecie wyjście wydaje się przypadkowe podczas każdej realizacji. Znalazłem THIS POST, który najwyraźniej mówi o tym samym problemie, ale nadal nie rozumiem, dlaczego czasami otrzymuję wyjście 1, 2 lub 3. Jeśli rozumiem ten kod poprawnie, to wynik 1 powinien być tym, co dostaję za każdym razem (pojawia się wyjątek) . W jaki sposób zapewnić, że otrzymuję wynik 1 w sposób ciągły lub będę w stanie określić, kiedy otrzymam dane wyjściowe 1 lub kiedy będę uzyskiwał wynik 2 lub 3?

+0

możliwy duplikat [losowej kolejności drukowania dla System.out & System.err nazywa] (http://stackoverflow.com/questions/12594537/ random-printing-order-for-system-out-system-err-calls) – user93353

Odpowiedz

16

Problem polega na tym, że piszesz niektóre dane wyjściowe do System.out, a niektóre do System.err. Są to niezależne strumienie z niezależnym buforowaniem. Moment, w którym są one przepłukiwane, jest, o ile wiem, nieokreślony.

W skrócie, podczas zapisywania do różnych strumieni nie można użyć kolejności, w której wyświetla się dane wyjściowe, aby określić kolejność wywołań do println(). Zauważ, że wyjście do System.out zawsze pojawia się w oczekiwanej kolejności.

Jeśli chodzi o kolejność wykonywania, ciało try jest wykonywane jako pierwsze. Jeśli zgłosi wyjątek, wówczas zostanie wykonana treść odpowiedniej klauzuli catch. Blok finally jest zawsze wykonywany jako ostatni.

+0

, ale teoretycznie, kolejność powinna być taka, jak pokazano na wyjściu 1, czyż nie? – user13267

+1

@ user13267 - Tak. Kolejność wykonywania jest określona i odpowiada kolejności, której oczekuje się na wyjściu. Zrób wszystko, aby przejść do pojedynczego strumienia (np. 'System.out') i za każdym razem powinieneś zobaczyć oczekiwaną kolejność. Alternatywnie, wywołaj 'flush()' na każdym strumieniu wyjściowym natychmiast po każdym zapisie. Powinno to również ustalić kolejność, w jakiej dane wyjściowe pojawiają się na konsoli. –

+0

Próbowałem 'System.out.flush()' i 'System.err.flush()' przy każdym wywołaniu, ale nadal kolejność wyjścia pozostaje taka sama. – user13267

2

Rzeczą przy obsłudze wyjątków przy użyciu funkcji catch catch jest to, że kontrola przejdzie do środka, jeśli będzie jakikolwiek wyjątek, dostanie się do bloku catch. Ale kontrola przejdzie do końcowego bloku za każdym razem, gdy zostanie wykonany.

1

Zapisujesz komunikat o błędzie zarówno na standardowe wyjście, jak i na wyjście błędów. Mają różne bufory, więc nie ma gwarancji, że dane wyjściowe będą wyświetlane w tej samej kolejności, w jakiej zostały utworzone, między dwoma strumieniami wyjściowymi.

Ponieważ widzę żadnych błędów w kodzie (chociaż zbędne return; w swoim segmencie catch utkwiło mi w gardle trochę), pozwól mi sugerować, że piszesz wszystkie wiadomości do stderr i sprawdź, czy kolejność wiadomość jest trochę bardziej zgodny z tym, czego się spodziewałeś.

1

Masz jeden błąd w swoim przykładzie, który bym usunął. Piszemy do System.out i System.err i oczekujemy, że konsola zsynchronizuje oba strumienie poprawnie. Aby usunąć efekty uboczne, użyłbym tutaj tylko jednego strumienia.

3

Pierwszy blok wykonać Try jeśli jest sukces wreszcie wykona, jeśli try blok nie wtedy catch wykona i finally wykonać. Cokolwiek się stanie, w końcu zostanie wykonany blok.

Ale

Jeśli zadzwonisz System.exit(0) wreszcie wolno blokować wykonywany

+0

Co się stanie, w końcu blok zostanie wykonany, ale nie zgodzę się na to, jeśli wywoływana jest funkcja System.exit()? –

+0

@Sunny Tak, masz rację, ale mówię tylko o tym przypadku. Jeśli wywołasz System.exit (0) w try. Albo stwórz coś, co sprawia, że ​​JVM przestaje działać lub zawiesza się (jak impas). W przeciwnym razie - nie. –