Domanda

Esiste un modo elegante per gestire le eccezioni che vengono generate nel blocco finally?

Ad esempio:

try {
  // Use the resource.
}
catch( Exception ex ) {
  // Problem with the resource.
}
finally {
   try{
     resource.close();
   }
   catch( Exception ex ) {
     // Could not close the resource?
   }
}

Come evitare try / catch nel blocco <=>?

È stato utile?

Soluzione

Di solito lo faccio in questo modo:

try {
  // Use the resource.
} catch( Exception ex ) {
  // Problem with the resource.
} finally {
  // Put away the resource.
  closeQuietly( resource );
}

Altrove:

protected void closeQuietly( Resource resource ) {
  try {
    if (resource != null) {
      resource.close();
    }
  } catch( Exception ex ) {
    log( "Exception during Resource.close()", ex );
  }
}

Altri suggerimenti

Di solito utilizzo uno dei metodi closeQuietly in org.apache.commons.io.IOUtils:

public static void closeQuietly(OutputStream output) {
    try {
        if (output != null) {
            output.close();
        }
    } catch (IOException ioe) {
        // ignore
    }
}

Se stai usando Java 7 e resource implementa AutoClosable, puoi farlo (usando InputStream come esempio):

try (InputStream resource = getInputStream()) {
  // Use the resource.
}
catch( Exception ex ) {
  // Problem with the resource.
}

Probabilmente un po 'sopra le righe, ma forse utile se si lasciano le eccezioni e non è possibile registrare nulla all'interno del proprio metodo (ad es. perché è una libreria e si preferisce lasciare che il codice chiamante gestisca le eccezioni e logging):

Resource resource = null;
boolean isSuccess = false;
try {
    resource = Resource.create();
    resource.use();
    // Following line will only run if nothing above threw an exception.
    isSuccess = true;
} finally {
    if (resource != null) {
        if (isSuccess) {
            // let close throw the exception so it isn't swallowed.
            resource.close();
        } else {
            try {
                resource.close();
            } catch (ResourceException ignore) {
                // Just swallow this one because you don't want it 
                // to replace the one that came first (thrown above).
            }
        }
    }
}

AGGIORNAMENTO: ho esaminato un po 'di più questo argomento e ho trovato un ottimo post sul blog di qualcuno che ha chiaramente pensato a questo più di me: http://illegalargumentexception.blogspot.com/2008/10/java-how-not-to-make-mess-of -stream.html Fa un ulteriore passo avanti e combina le due eccezioni in una, che in alcuni casi potrei vedere utile.

A partire da Java 7 non è più necessario chiudere esplicitamente le risorse in un blocco finalmente invece è possibile utilizzare la sintassi prova con risorse. L'istruzione try-with-resources è un'istruzione try che dichiara una o più risorse. Una risorsa è un oggetto che deve essere chiuso al termine del programma. L'istruzione try-with-resources assicura che ogni risorsa sia chiusa alla fine dell'istruzione. Qualsiasi oggetto che implementa java.lang.AutoCloseable, che include tutti gli oggetti che implementano java.io.Closeable, può essere utilizzato come risorsa.

Supponi il seguente codice:

try( Connection con = null;
     Statement stmt = con.createStatement();
     Result rs= stmt.executeQuery(QUERY);)
{  
     count = rs.getInt(1);
}

Se si verifica un'eccezione, il metodo chiudi verrà chiamato su ciascuna di queste tre risorse nell'ordine opposto in cui sono state create. Significa che il metodo close verrebbe chiamato prima per ResultSetm, quindi per l'istruzione e alla fine per l'oggetto Connection.

È anche importante sapere che vengono eliminate tutte le eccezioni che si verificano quando vengono chiamati automaticamente i metodi di chiusura. Queste eccezioni soppresse possono essere recuperate con il metodo getsuppressed () definito nella classe Gettabile .

Fonte: https://docs.oracle.com/javase/ tutorial / essenziali / eccezioni / tryResourceClose.html

Ignorare le eccezioni che si verificano in un blocco "finally" è generalmente una cattiva idea a meno che non si sappia quali saranno tali eccezioni e quali condizioni rappresenteranno. Nel normale schema di utilizzo try/finally, il blocco try pone le cose in uno stato che il codice esterno non si aspetta e il blocco finally ripristina lo stato di quelle cose a ciò che il codice esterno si aspetta. Il codice esterno che rileva un'eccezione generalmente prevede che, nonostante l'eccezione, tutto sia stato ripristinato in uno stato normal. Ad esempio, supponiamo che un po 'di codice avvii una transazione e quindi cerchi di aggiungere due record; il " infine " il blocco esegue un " rollback se non commesso " operazione. Un chiamante potrebbe essere preparato affinché si verifichi un'eccezione durante l'esecuzione del secondo & Quot; add & Quot; operazione, e può aspettarsi che se rileva tale eccezione, il database sarà nello stato in cui era prima che entrambe le operazioni fossero tentate. Se, tuttavia, si verifica una seconda eccezione durante il rollback, potrebbero verificarsi problemi se il chiamante fa delle ipotesi sullo stato del database. Il fallimento del rollback rappresenta una grave crisi - che non dovrebbe essere colta dal codice in attesa di un semplice & Quot; Impossibile aggiungere il record & Quot; fa eccezione.

La mia inclinazione personale sarebbe quella di avere finalmente un metodo per catturare le eccezioni che si verificano e racchiuderle in un " CleanupFailedException " ;, riconoscendo che tale fallimento rappresenta un grave problema e tale eccezione non dovrebbe essere colta leggermente.

Una soluzione, se le due eccezioni sono due classi diverse

try {
    ...
    }
catch(package1.Exception err)
   {
    ...
   }
catch(package2.Exception err)
   {
   ...
   }
finally
  {
  }

Ma a volte non puoi evitare questo secondo tentativo di cattura. per esempio. per chiudere un flusso

InputStream in=null;
try
 {
 in= new FileInputStream("File.txt");
 (..)// do something that might throw an exception during the analysis of the file, e.g. a SQL error
 }
catch(SQLException err)
 {
 //handle exception
 }
finally
 {
 //at the end, we close the file
 if(in!=null) try { in.close();} catch(IOException err) { /* ignore */ }
 }

Perché vuoi evitare il blocco aggiuntivo? Poiché il blocco finally contiene & Quot; normale & Quot; operazioni che possono generare un'eccezione E vuoi che il blocco finally sia eseguito completamente DEVI catturare le eccezioni.

Se non ti aspetti che il blocco finally generi un'eccezione e non sai comunque come gestire l'eccezione (scarichi semplicemente la traccia dello stack), lascia che l'eccezione riempia lo stack di chiamate (rimuovi il try-catch dal blocco finally).

Se vuoi ridurre la digitazione potresti implementare un " global " blocco try-catch esterno, che catturerà tutte le eccezioni generate nei blocchi infine:

try {
    try {
        ...
    } catch (Exception ex) {
        ...
    } finally {
        ...
    }

    try {
        ...
    } catch (Exception ex) {
        ...
    } finally {
        ...
    }

    try {
        ...
    } catch (Exception ex) {
        ...
    } finally {
        ...
    }
} catch (Exception ex) {
    ...
}

Dopo molte considerazioni, trovo il seguente codice migliore:

MyResource resource = null;
try {
    resource = new MyResource();
    resource.doSomethingFancy();
    resource.close(); 
    resource = null;  
} finally {
    closeQuietly(resource)
}

void closeQuietly(MyResource a) {
    if (a!=null)
        try {
             a.close();
        } catch (Exception e) {
             //ignore
        }
}

Questo codice garantisce quanto segue:

  1. La risorsa viene liberata al termine del codice
  2. Le eccezioni generate alla chiusura della risorsa non vengono utilizzate senza elaborarle.
  3. Il codice non tenta di chiudere la risorsa due volte, non verrà creata alcuna eccezione non necessaria.

Se puoi, dovresti testare per evitare la condizione di errore.

try{...}
catch(NullArgumentException nae){...}
finally
{
  //or if resource had some useful function that tells you its open use that
  if (resource != null) 
  {
      resource.Close();
      resource = null;//just to be explicit about it was closed
  }
}

Inoltre dovresti probabilmente rilevare solo le eccezioni da cui puoi recuperare, se non riesci a recuperare, lascia che si propaghi al livello superiore del tuo programma. Se non riesci a verificare la presenza di una condizione di errore, dovrai circondare il codice con un blocco try catch come hai già fatto (anche se consiglierei di rilevare ancora errori specifici previsti).

Potresti trasformare questo in un altro metodo ...

public void RealDoSuff()
{
   try
   { DoStuff(); }
   catch
   { // resource.close failed or something really weird is going on 
     // like an OutOfMemoryException 
   }
}

private void DoStuff() 
{
  try 
  {}
  catch
  {
  }
  finally 
  {
    if (resource != null) 
    {
      resource.close(); 
    }
  }
}

Di solito lo faccio:

MyResource r = null;
try { 
   // use resource
} finally {   
    if( r != null ) try { 
        r.close(); 
    } catch( ThatSpecificExceptionOnClose teoc ){}
}

Motivazione: Se ho finito con la risorsa e l'unico problema che ho è chiuderlo, non c'è molto che posso fare al riguardo. Non ha senso nemmeno uccidere l'intero thread se ho finito con la risorsa comunque.

Questo è uno dei casi in cui almeno per me è sicuro ignorare quell'eccezione controllata.

Fino ad oggi non ho avuto problemi ad usare questo idioma.

try {
    final Resource resource = acquire();
    try {
        use(resource);
    } finally {
        resource.release();
    }
} catch (ResourceException exx) {
    ... sensible code ...
}

Lavoro svolto. Nessun test nullo. Cattura singola, include eccezioni di acquisizione e rilascio. Ovviamente puoi usare il linguaggio Execute Around e scriverlo solo una volta per ogni tipo di risorsa.

Modifica Resource da migliore risposta a Closeable

Lo streaming implementa <=> In questo modo puoi riutilizzare il metodo per tutti i flussi

protected void closeQuietly(Closeable resource) {
    if (resource == null) 
        return;
    try {
        resource.close();
    } catch (IOException e) {
        //log the exception
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top