2010-04-13 3 views
37

Mi è stato detto che c'è un sovraccarico nell'uso del meccanismo di try-catch di Java. Quindi, mentre è necessario mettere i metodi che generano un'eccezione controllata all'interno di un blocco try per gestire la possibile eccezione, è buona norma limitare le dimensioni del blocco try a contenere solo le operazioni che potrebbero generare eccezioni.I blocchi di prova java devono essere esaminati il ​​più strettamente possibile?

Non sono sicuro che questa sia una conclusione ragionevole.

Considerare le due implementazioni seguenti di una funzione che elabora un file di testo specificato.

Anche se è vero che il primo causa un sovraccarico non necessario, trovo molto più facile da seguire. È meno chiaro dove esattamente le eccezioni provengono solo dal guardare le dichiarazioni, ma i commenti mostrano chiaramente quali dichiarazioni sono responsabili.

Il secondo è molto più lungo e complicato del primo. In particolare, il bel linguaggio di lettura della prima riga deve essere stroncato per adattarsi alla chiamata readLine in un blocco try.

Qual è la procedura migliore per gestire le eccezioni in una funzione in cui possono essere generate più eccezioni nella sua definizione?

Questo contiene tutto il codice di elaborazione all'interno del blocco 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); 
    } 
} 

Questo contiene solo i metodi che producono eccezioni all'interno di blocchi 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

"Ottimizzazione prematura" è una frase utilizzata per descrivere una situazione in cui un programmatore consente alle considerazioni sulle prestazioni di influenzare la progettazione di un pezzo di codice. Ciò può comportare un design che non è pulito come avrebbe potuto essere o un codice non corretto, perché il codice è complicato dall'ottimizzazione e il programmatore è distratto dall'ottimizzazione. - Wikipedia [http://en.wikipedia.org/wiki/Program_optimization#When_to_optimize] –

+2

"Dovremmo dimenticare le piccole efficienze, diciamo circa il 97% delle volte: l'ottimizzazione prematura è la radice di tutto il male. le nostre opportunità in quel 3% critico ". - Knuth [http://stackoverflow.com/questions/211414/is-premature-optimization-really-the-root-of-all-evil] –

risposta

45

La premessa di base è false: la dimensione di un blocco try non fa alcuna differenza nelle prestazioni. Le prestazioni sono influenzate dall'aumento delle eccezioni in fase di esecuzione e ciò è indipendente dalla dimensione del blocco try.

Tuttavia, mantenere piccoli blocchi di prova può portare a programmi migliori.

È possibile rilevare eccezioni per il ripristino e procedere, oppure è possibile individuarle semplicemente per segnalarle al chiamante (oa un utente, tramite un'interfaccia utente).

Nel primo caso, i guasti da cui è possibile eseguire il ripristino sono spesso molto specifici e ciò porta a blocchi try più piccoli.

Nel secondo caso, quando viene rilevata un'eccezione in modo che possa essere spostata da un'altra eccezione e reinserita o visualizzata all'utente, i piccoli blocchi try significano che si conosce più precisamente quale operazione ha avuto esito negativo e più in alto livello di livello in cui è stata effettuata la chiamata. Ciò consente di creare report di errore più specifici.

Ovviamente, ci sono & hellip; eccezioni (mi dispiace!) a queste linee guida. Ad esempio, in alcuni casi, rapporti di errore molto specifici potrebbero essere un problema di sicurezza.


Potrebbe essere utile sapere quale effetto di un blocco try ha sul codice compilato. Non cambia affatto le istruzioni compilate! (Naturalmente, il blocco catch corrispondente è, poiché è come qualsiasi altro codice.)

Un blocco try crea una voce nella tabella delle eccezioni associata al metodo. Questa tabella ha un intervallo di contatori di istruzioni di origine, un tipo di eccezione e un'istruzione di destinazione. Quando viene sollevata un'eccezione, questa tabella viene esaminata per verificare se esiste una voce con un tipo corrispondente e un intervallo che include l'istruzione che ha generato l'eccezione. Se lo fa, l'esecuzione si dirama al numero di destinazione corrispondente.

La cosa importante da comprendere è che questa tabella non viene consultata (e non ha alcun effetto sull'esecuzione delle prestazioni) a meno che non sia necessaria. (Trascurando un piccolo sovraccarico nel caricamento della classe.)

+6

Esattamente. Vale anche la pena notare, per la cronaca, che l'overhead associato con try-catch si verifica solo se viene lanciata un'eccezione o se il blocco ha una clausola finally, come dettagliato nelle specifiche della VM http://java.sun.com/docs/ libri/JVM/second_edition/html/Compiling.doc.html # 9934. – ig0774

+0

@ ig0774 Quindi, se aggiungo un blocco finally come 'finally {in.close(); } ', quindi l'overhead associato diventa effettivo? Da quanto ho capito delle specifiche, la clausola finally aggiunge semplicemente un'ulteriore istruzione al blocco try compilato per chiamare il blocco finally come subroutine prima di tornare normalmente. –

+0

+1 per il riferimento alle specifiche, btw. –

1

c'è ben poco beneficio per il 2o metodo. dopotutto se riesci ad aprire un file ma non a leggerlo, allora c'è qualcosa di molto sbagliato nel tuo computer. quindi sapere che l'eccezione io proviene dal metodo readLine() è molto raramente utile. anche come sapete, vengono comunque lanciate eccezioni diverse per problemi diversi (FileNotFoundException, ecc.)

finché lo si scopre con un blocco "logico", vale a dire aprire, leggere e chiudere un file in 1 go, vorrei vai con il primo metodo. è molto più semplice da leggere e, specialmente quando si ha a che fare con l'IO, i cicli del processore usati dall'overhead try-catch sarebbero minimi se ce ne fossero.

0

Il secondo metodo genererà un errore del compilatore che reader potrebbe non essere stato inizializzato. È possibile aggirare il problema inizializzandolo su null, ma ciò significa che è possibile ottenere un NPE e non c'è alcun vantaggio.

+0

Non verrà generato alcun errore del compilatore. L'istruzione 'return' alla fine del blocco' catch' fa in modo che il lettore venga inizializzato o non venga mai letto! Ovviamente, ho solo aggiunto questa dichiarazione dopo che il compilatore ha emesso esattamente l'errore che descrivi. :) –

+0

Proprio così - mi sono perso. Grazie al cielo, il compilatore è qui per capire queste cose per noi. :) –

11

Mi è stato detto che c'è un sovraccarico nell'utilizzo del meccanismo di intercettazione Java.

Assolutamente. E anche le chiamate al metodo sono eccessive. Ma non dovresti mettere tutto il tuo codice in un solo metodo.

Per non Toot il corno prematura di ottimizzazione, ma l'attenzione dovrebbe essere su facilità di lettura, organizzazione, ecc Lingua costruisce raramente prestazioni impatto tanto quanto l'organizzazione del sistema e la scelta degli algoritmi.

Per me, il primo è più facile da leggere.

1

Mettere i blocchi di prova attorno al codice specifico che può generare un'eccezione, lo rende, a mio parere, più facile da leggere.È probabile che tu voglia visualizzare un messaggio diverso per ogni errore e fornire istruzioni all'utente, che saranno diverse a seconda di dove si verifica l'errore.

Tuttavia, il problema di prestazioni a cui la maggior parte delle persone fa riferimento è correlato all'innalzamento dell'eccezione, non al blocco try stesso.

In altre parole, finché non si verifica un errore, il blocco try non influisce in modo sensibile sulle prestazioni. Non dovresti considerare un blocco try solo un altro costrutto di controllo del flusso e generare un errore per ramificare il tuo codice. Questo è quello che vuoi evitare.

+2

Solo una nota aggiuntiva sul tuo ultimo paragrafo. Se si dispone di un try/catch * inside * di un ciclo, è possibile rilevare un'eccezione e continuare il ciclo. Se il tuo try/catch è * outside * del loop, il tuo loop verrà interrotto. O uno potrebbe essere quello che vuoi e influenzerà dove metti il ​​try/catch. –

3

No. L'unica cosa che dovresti considerare è dove puoi gestire ragionevolmente l'eccezione e quali risorse hai bisogno per reclamare (con infine).

+0

+1 per menzionare la pulizia delle risorse. Nell'OP solo la prima implementazione si presta ad aggiungere una sola clausola 'finally' che chiude il file. –

+0

+1 per aver menzionato la pulizia. Fino ad ora non avevo mai chiamato 'close' su alcun oggetto' Reader' che ho usato. Mi vergogno! –

+0

Quanto Java-try-with-resources modifica le tue preferenze? –

2

Questo è l'ottimizzazione prematura al suo peggiore. Non farlo

"Dovremmo dimenticare le piccole efficienze, diciamo circa il 97% delle volte: l'ottimizzazione prematura è la radice di tutti i mali" - Knuth.