2016-04-26 32 views
21

Oggi al college abbiamo parlato un po 'di try, catch e finally. Mi sono confuso su questi due esempi:Infine a volte mi confondo

PrintWriter out = null; 
try { 
    out = new PrintWriter(...); // We open file here 
} catch (Exception e) { 
    e.printStackTrace(); 
} finally { // And we close it here 
    out.close(); 
} 

Qual è la differenza tra la chiusura del file in finally e se abbiamo appena fatto in questo modo:

PrintWriter out = null; 
try { 
    out = new PrintWriter(...); // We open file here 
} catch (Exception e) { 
    e.printStackTrace(); 
} 
out.close(); 

Questo pezzo di codice dopo cattura sarà sempre eseguire.

Potete darmi alcuni buoni esempi sulle differenze tra quando usiamo finally e quando inseriamo il codice dopo la cattura? So che alla fine verrà sempre eseguito, ma il programma continuerà a funzionare anche dopo il blocco catch.

+3

E se la tua cattura ha fatto qualcosa come lanciare un'altra (possibilmente non controllata) eccezione? Sarebbe 'out.close()' eseguito in quella situazione? In altre parole, la semplice stampa della traccia dello stack e lo spostamento non sono sempre il modo in cui viene gestita un'eccezione. – rmlan

+3

Se si lancia nuovamente l'eccezione o non si cattura tutto, il blocco finale è tuo amico. Esistono anche condizioni Throwable che non sono Eccezioni, e quindi il 2 ° esempio sarebbe problematico. – KevinO

+0

Sto per iniziare a conoscere Throwable, quindi credo che dovrò imparare prima a capire questo, giusto? –

risposta

33

Farebbe comunque la differenza se il codice genera Error. Questo non è catturato nel codice e quindi nessuna parte dopo try/catch/finally non verrà catturata. Se fa parte di finally, verrà comunque eseguito anche per Error.

In secondo luogo, se per qualsiasi motivo e.printStackTrace() genera un'eccezione (anche se sarebbe molto raro), lo stesso accadrà - finally verrà ancora eseguito.

In generale finally è un modo molto sicuro per il rilascio di risorse, qualunque cosa accada. Ancora più sicuro è il try-with-resources supportato da Java 7 in quanto può gestire facilmente eventuali eccezioni generate durante le operazioni di chiusura. In questo esempio, sarebbe simile:

try (PrintWriter out = new PrintWriter(...)) { 
    // do whatever with out 
} 
catch (Exception e) { 
    e.print... (whatever) 
} 
// no need to do anything else, close is invoked automatically by try block 

EDIT: Si noti inoltre che il codice non è propriamente corretto (non importa quale versione). Se il costruttore PrintWriter genera un'eccezione, la riga out.close() non riuscirà su NullPointerException.

+0

Non sapevo che potevano capitare errori nel blocco catch. Grazie, questo ha senso ora. –

+15

@MiljanRakita Gli errori possono capitare ovunque. Fa parte del divertimento! – Gusdor

+2

Ricorda che 'finally' non * verrà * eseguito in situazioni veramente catastrofiche (ad esempio, un'interruzione dell'alimentazione), quindi non contare su di esso per cose come il mantenimento della coerenza delle transazioni del database. – Mark

3

Se un errore non rilevato si verifica all'interno del blocco try, o anche se si verifica un errore all'interno del tuo catch blocco, il 'pezzo di codice' dopo la cattura non verrà eseguito, ma il blocco finally faranno.

finally verrà sempre eseguito.

Dalla documentazione Java:

Il blocco finally viene eseguito sempre quando il blocco try viene chiuso. Questo assicura che il blocco finally venga eseguito anche se si verifica un'eccezione imprevista . Ma alla fine è utile per più di una semplice gestione di eccezione - consente al programmatore di evitare di avere il codice di pulizia bypassato accidentalmente da un ritorno, continua o interruzione. Mettere il codice cleanup in un blocco finally è sempre una buona pratica, anche quando non sono previste le eccezioni .

2

Cosa succede se qualcosa nel blocco catch genera Eccezione? out.close non verrà eseguito. Puoi anche usare "try with resources" per assicurarti che tutte le risorse siano chiuse dopo essere state utilizzate.Prova questo esempio:

public static void withFinnaly() { 
     try { 
      throwException(); 
      System.out.println("This won't execute"); 
     } catch (Exception e) { 
      System.out.println("Exception is caught"); 
      throwException(); 
     } finally { 
      System.out.println("Finally is always executed," + 
        " even if method in catch block throwed Exception"); 
     } 
    } 

    public static void withOutFinnaly() { 
     try { 
      throwException(); 
      System.out.println("This won't execute"); 
     } catch (Exception e) { 
      System.out.println("Exception is caught"); 
      throwException(); 
     } 
     System.out.println("Looks like we've lost this... " + 
       "This wont execute"); 
    } 

    public static void throwException() throws RuntimeException { 
     throw new RuntimeException(); 
    } 
+0

Perfettamente chiaro! Grazie mille ! –

1

Il tuo secondo esempio può gettare un indesiderato NullPointerException se si verifica un'eccezione nella contructor di PrintWriter.

Un'altra possibilità è che out.close(); genererà un errore, che non viene rilevato.

Se si sposta il codice in un blocco finally, verrà sempre eseguito, indipendentemente dal fatto che il blocco try abbia esito positivo o meno. Questo è in particolare utile se il tuo blocco try genera un'eccezione che è non catturata. Nel secondo esempio, questo non porterà all'esecuzione di out.close(), mentre con un blocco finally verrà eseguito anche se il blocco try genera un errore non rilevato.

+0

Grazie mille. Non sapevo che è possibile lanciare un'eccezione! –

+1

Anche nel primo esempio, verrà generata NPE. La cosa migliore è usare try-with-resources. – Axel

+0

Sì, provare-con-risorse è meglio, ma la domanda riguardava "finalmente", quindi non l'ho menzionato. – Polygnome

2

L'usecase normale per infine è quando non si desidera rilevare l'eccezione nello stesso metodo.

in tal caso si utilizza un tentativo con blocco finally senza avere un fermo. In questo modo è possibile garantire la chiusura delle risorse senza dover rilevare l'eccezione nel metodo stesso.

1

Sebbene non risposte complete stessi, questi due esempi di try-finally (mis) utilizzano può essere illuminante:

public class JavaApplication3 
{ 
    static int foo() 
    { 
     try 
     { 
      return 6; 
     } 
     finally 
     { 
      return 4; 
     } 

    } 

    public static void main(String[] args) 
    { 
     System.out.println("foo: " + foo()); 
    } 
} 


public class JavaApplication3 
{ 
    static int foo() 
    { 
     try 
     { 
      throw new Exception(); 
     } 
     finally 
     { 
      return 4; 
     } 

    } 
    public static void main(String[] args) 
    { 
     System.out.println("foo: " + foo()); 
    } 
} 

Entrambi programmi uscita .

La ragione di questo può essere trovato sul Chapter 14.20.2 of JLS

viene eseguita un'istruzione try con un blocco finally dalla prima esecuzione del blocco try. Poi v'è una scelta:

• Se l'esecuzione del blocco try completa bruscamente a causa di un lancio di un valore V, allora non è una scelta:
[...]
- Se la corsa -time tipo di V non è un'assegnazione compatibile con una classe di eccezione catchable di qualsiasi clausola catch dell'istruzione try, quindi viene eseguito il blocco finally . Poi v'è una scelta:
[...]
>Se il blocco finally completa bruscamente per la ragione S, allora l'istruzione try completa bruscamente per motivi S (e il lancio del valore di V viene scartato e dimenticato).

Se l'esecuzione del blocco try completa bruscamente per qualsiasi altro motivo R, allora il blocco finally viene eseguita, e quindi v'è una scelta:
- Se il blocco finally completa normalmente, quindi try completa per il motivo R.
- Se il blocco finally termina in modo improvviso per il motivo S, l'istruzione try completa improvvisamente il motivo S (e il motivo R viene scartato).

Editing miniera

ritenere che un return è un modo brusco di completamento di un blocco o di tryfinally.

2

In Java, il codice sorgente:

void foo() 
{ 
    try { 
    if (W()) 
     return; 
    } 
    catch (FooException ex) { 
    if (X()) 
     throw; 
    } 
    finally { 
    Y(); 
    } 
    Z(); 
} 

sarà convertita dal compilatore in:

void foo() 
{ 
    try { 
    if (W()) { 
     Y(); 
     return; 
    } 
    } 
    catch (FooException ex) { 
    if (X()) { 
     Y(); 
     throw; 
    } 
    } 
    catch { 
    Y(); 
    throw; 
    } 
    Y(); 
    Z(); 
} 

L'effetto è quello di provocare il codice nel blocco finally essere duplicate tutti dei luoghi in cui il controllo potrebbe lasciare il metodo. Qualsiasi try blocco che ha una finally ma non prendere-tutto gestore è equivalente ad uno con un catch-all gestore che lancia immediatamente (in cui il processore potrebbe quindi inserire una copia del codice finally prima onnicomprensiva gestore.

0

Un esempio più piccola:

PrintWriter out = null; 
try { 
    out = new PrintWriter(); 
    out.print(data); 
} finally { 
    out.close(); 
} 

Qui, noi non intercettare eventuali eccezioni (che è gestito dal chiamante), ma noi vogliamo close lo scrittore sia che

  • funzionano normalmente tramite il blocco try oppure
  • lasciare un'eccezione.

In entrambi i casi, il codice in finally viene eseguito come parte dell'abbandono del blocco.


Qui, prendiamo un sottoinsieme di eccezioni:

PrintWriter out = null; 
try { 
    out = new PrintWriter(); 
    out.print(data); 
} catch (IOException e) { 
    log(e); 
} finally { 
    out.close(); 
} 
do_something_else(); 

Qui, ci sono tre possibili percorsi:

  • normale esecuzione della parte try, seguito dal finally, e poi do_something_else(),
  • il try parte tiri IOException, che viene catturato e registrato, poi i finally piste di blocco, e poi do_something_else(), o
  • parte try getta un throwable diverso, che non viene catturato, ma i finally piste di blocco, quindi il codice salta al prossimo racchiude try , possibilmente nel chiamante.

si prendono cura quando si scrive il tuo blocco finally - non è all'interno del try, quindi tutte le eccezioni ci saranno anticipare qualsiasi eccezione che è in corso. Il breve consiglio è quello di cercare di evitare cose che potrebbero buttare dall'interno finally o catch.