Existe-t-il un moyen élégant de gérer les exceptions levées dans le bloc finally
?
Par exemple:
try {
// Use the resource.
}
catch( Exception ex ) {
// Problem with the resource.
}
finally {
try{
resource.close();
}
catch( Exception ex ) {
// Could not close the resource?
}
}
Comment éviter la try
/catch
dans le bloc finally
?
Je le fais habituellement comme ça:
try {
// Use the resource.
} catch( Exception ex ) {
// Problem with the resource.
} finally {
// Put away the resource.
closeQuietly( resource );
}
Autre part:
protected void closeQuietly( Resource resource ) {
try {
if (resource != null) {
resource.close();
}
} catch( Exception ex ) {
log( "Exception during Resource.close()", ex );
}
}
J'utilise généralement l'une des méthodes closeQuietly
dans org.Apache.commons.io.IOUtils
:
public static void closeQuietly(OutputStream output) {
try {
if (output != null) {
output.close();
}
} catch (IOException ioe) {
// ignore
}
}
Si vous utilisez Java 7 et que resource
implémente AutoClosable
, vous pouvez le faire (en utilisant InputStream comme exemple):
try (InputStream resource = getInputStream()) {
// Use the resource.
}
catch( Exception ex ) {
// Problem with the resource.
}
Peut-être un peu exagéré, mais peut-être utile si vous laissez des exceptions bouillonner et que vous ne pouvez rien journaliser dans votre méthode (par exemple parce que c'est une bibliothèque et que vous préférez laisser le code appelant gérer les exceptions et la journalisation):
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).
}
}
}
}
MISE À JOUR: J'ai examiné la question un peu plus et trouvé un bon post de blog écrit par quelqu'un qui y a clairement pensé plus que moi: http://illegalargumentexception.blogspot.com/2008/10/Java-how-not-to -make-mess-of-stream.html Il va encore plus loin et combine les deux exceptions en une, ce qui pourrait être utile dans certains cas.
Depuis Java 7, vous n'avez plus besoin de fermer explicitement les ressources d'un bloc finally. Vous pouvez également utiliser la syntaxe try -with-resources. L'instruction try-with-resources est une instruction try qui déclare une ou plusieurs ressources. Une ressource est un objet qui doit être fermé une fois que le programme est terminé. L'instruction try-with-resources garantit que chaque ressource est fermée à la fin de l'instruction. Tout objet implémentant Java.lang.AutoCloseable, qui inclut tous les objets implémentant Java.io.Closeable, peut être utilisé comme ressource.
Supposons le code suivant:
try( Connection con = null;
Statement stmt = con.createStatement();
Result rs= stmt.executeQuery(QUERY);)
{
count = rs.getInt(1);
}
Si une exception survient, la méthode close sera appelée sur chacune de ces trois ressources dans l'ordre inverse de leur création. Cela signifie que la méthode close serait d'abord appelée pour ResultSetm, puis l'instruction et à la fin pour l'objet Connection.
Il est également important de savoir que toutes les exceptions qui se produisent lorsque les méthodes de fermeture sont appelées automatiquement sont supprimées. Ces exceptions supprimées peuvent être récupérées par la méthode getsuppressed () définie dans la classe Throwable.
Source: https://docs.Oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html
Ignorer les exceptions qui se produisent dans un bloc «finally» est généralement un problème. mauvaise idée à moins de savoir quelles seront ces exceptions et quelles conditions elles représenteront. Dans le modèle d'utilisation try/finally
normal, le bloc try
place les éléments dans un état auquel le code extérieur ne s'attendrait pas, et le bloc finally
restaure l'état de ces éléments à ceux attendus par le code extérieur. Le code extérieur qui intercepte une exception s'attend généralement à ce que, malgré cette exception, tout ait été restauré dans un état normal
. Par exemple, supposons qu'un code démarre une transaction et tente ensuite d'ajouter deux enregistrements; le bloc "finally" effectue une opération "annulation si non validée". Un appelant peut être préparé pour qu'une exception se produise lors de l'exécution de la deuxième opération "add" et peut s'attendre à ce que, s'il intercepte une telle exception, la base de données sera dans l'état où elle se trouvait avant la tentative de l'une ou l'autre opération. Toutefois, si une deuxième exception se produit lors de la restauration, des problèmes peuvent survenir si l'appelant émet des hypothèses sur l'état de la base de données. L'échec de la restauration représente un majeur crise - crise qui ne devrait pas être capturée par le code dans l'attente d'une simple exception "Impossible d'ajouter un enregistrement".
Mon penchant personnel serait de faire en sorte que les exceptions de méthode méthodes se produisent et de les envelopper dans une "exception CleanupFailedException", en reconnaissant qu'un tel échec représente un problème majeur et qu'une telle exception ne doit pas être prise à la légère.
Une solution, si les deux exceptions sont deux classes différentes
try {
...
}
catch(package1.Exception err)
{
...
}
catch(package2.Exception err)
{
...
}
finally
{
}
Mais parfois, vous ne pouvez pas éviter ce deuxième essai. par exemple. pour fermer un flux
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 */ }
}
Après mûre réflexion, je trouve le code suivant le mieux:
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
}
}
Ce code garantit ce qui suit:
Pourquoi voulez-vous éviter le blocage supplémentaire? Puisque le bloc finally contient des opérations "normales" qui peuvent générer une exception ET que vous voulez que le bloc finally soit exécuté complètement, vous DEVEZ intercepter des exceptions.
Si vous ne vous attendez pas à ce que le bloc finally génère une exception et que vous ne sachiez pas quand même gérer l'exception (vous ne feriez que vider la trace de la pile), laissez l'exception écumper la pile d'appels (supprimez le try-catch de bloc).
Si vous souhaitez réduire le nombre de saisies, vous pouvez implémenter un bloc externe global "try-catch", qui interceptera toutes les exceptions renvoyées dans des blocs:
try {
try {
...
} catch (Exception ex) {
...
} finally {
...
}
try {
...
} catch (Exception ex) {
...
} finally {
...
}
try {
...
} catch (Exception ex) {
...
} finally {
...
}
} catch (Exception ex) {
...
}
Changer Resource
de meilleure réponse en Closeable
Les flux implémentent Closeable
Ainsi, vous pouvez réutiliser la méthode pour tous les flux
protected void closeQuietly(Closeable resource) {
if (resource == null)
return;
try {
resource.close();
} catch (IOException e) {
//log the exception
}
}
try {
final Resource resource = acquire();
try {
use(resource);
} finally {
resource.release();
}
} catch (ResourceException exx) {
... sensible code ...
}
Travail accompli. Aucun test nul. Prise unique, y compris les exceptions d'acquisition et de libération. Bien sûr, vous pouvez utiliser l'idiome Execute Around et ne l'écrire qu'une fois pour chaque type de ressource.
Je fais habituellement ceci:
MyResource r = null;
try {
// use resource
} finally {
if( r != null ) try {
r.close();
} catch( ThatSpecificExceptionOnClose teoc ){}
}
Raison: Si j'ai fini avec la ressource et que le seul problème que j'ai est de la fermer, je ne peux rien faire à ce sujet Cela n'a aucun sens non plus de tuer le fil entier si j'en ai fini avec la ressource de toute façon.
C'est l'un des cas où, du moins pour moi, il est prudent d'ignorer cette exception vérifiée.
À ce jour, je n'ai eu aucun problème à utiliser cet idiome.
Si vous le pouvez, vous devriez tester pour éviter la condition d'erreur.
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
}
}
En outre, vous ne devriez probablement capturer que les exceptions sur lesquelles vous pouvez récupérer. Si vous ne pouvez pas récupérer, laissez-le se propager au niveau supérieur de votre programme. Si vous ne pouvez pas rechercher une condition d'erreur, vous devrez entourer votre code d'un bloc catch catch, comme vous l'avez déjà fait (bien que je vous recommande de détecter les erreurs spécifiques attendues).
Vous pouvez reformuler ceci dans une autre méthode ...
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();
}
}
}