En travaillant sur un projet d'école, j'ai écrit le code suivant:
FileOutputStream fos;
ObjectOutputStream oos;
try {
fos = new FileOutputStream(file);
oos = new ObjectOutputStream(fos);
oos.writeObject(shapes);
} catch (FileNotFoundException ex) {
// complain to user
} catch (IOException ex) {
// notify user
} finally {
if (oos != null) oos.close();
if (fos != null) fos.close();
}
Le problème est que Netbeans me dit que les lignes resource.close()
jettent un IOException
et doivent donc être capturées ou déclarées. Il se plaint également que oos
et fos
ne soient pas encore initialisés (malgré les vérifications nulles).
Cela semble un peu étrange, vu que le but est d'arrêter le IOException
juste là.
Ma solution de genou est de faire ceci:
} finally {
try {
if (oos != null) oos.close();
if (fos != null) fos.close();
} catch (IOException ex) { }
}
Mais au fond, cela me dérange et me sent sale.
Je viens d'un arrière-plan C #, où je voudrais simplement profiter d'un bloc using
, donc je ne sais pas quelle est la "bonne" façon de gérer cela.
Quelle est la bonne façon de gérer ce problème?
Si vous essayez de détecter et de signaler toutes les exceptions à la source, une meilleure solution est la suivante:
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream(file));
oos.writeObject(shapes);
oos.flush();
} catch (FileNotFoundException ex) {
// complain to user
} catch (IOException ex) {
// notify user
} finally {
if (oos != null) {
try {
oos.close();
} catch (IOException ex) {
// ignore ... any significant errors should already have been
// reported via an IOException from the final flush.
}
}
}
Remarques:
close
et flush
vers leurs flux encapsulés, etc. Il vous suffit donc de fermer ou de vider le plus externe emballage.IOException
puisse voir les échecs d'écriture1.Si vous devez souvent "fermer un flux éventuellement nul en ignorant les IOExceptions", alors vous pouvez vous écrire une méthode d'aide comme celle-ci:
public void closeQuietly(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (IOException ex) {
// ignore
}
}
}
alors vous pouvez remplacer le bloc finally précédent par:
} finally {
closeQuietly(oos);
}
(Une autre réponse souligne qu'une méthode closeQuietly
est déjà disponible dans une bibliothèque Apache Commons ... si cela ne vous dérange pas d'ajouter une dépendance à votre projet pour une méthode de 10 lignes. UPDATE : notez que ces méthodes sont obsolètes dans la version 2.6 de l'API.)
Mais veillez à n'utiliser closeQuietly
que sur les flux où IO exceptions vraiment ne sont pas pertinents.
1 - Cela n'est pas nécessaire lors de l'utilisation de try-with-resources.
Sur la question de flush()
versus close()
que les gens demandent:
close()
provoque le vidage de toutes les sorties tamponnées. Vous devriez constater que toutes les autres classes de sortie (standard) qui mettent en mémoire tampon de sortie se comporteront de la même manière. Ainsi, pour une classe standard, il est redondant d'appeler flush()
juste avant close()
.close()
qui ne vide pas les données tamponnées est sans doute cassé .Enfin, il y a la question de ce que flush()
fait réellement. Voici ce que dit le javadoc (pour OutputStream
...)
Si la destination prévue de ce flux est une abstraction fournie par le système d'exploitation sous-jacent, par exemple un fichier, alors le vidage du flux garantit uniquement que les octets précédemment écrits dans le flux sont transmis au système d'exploitation pour l'écriture; cela ne garantit pas qu'ils sont réellement écrits sur un périphérique physique tel qu'un lecteur de disque.
Donc ... si vous espérez/imaginez que l'appel de flush()
garantit la persistance de vos données, vous vous trompez! (Si vous avez besoin de faire ce genre de chose, regardez le FileChannel.force
Méthode ...)
D'un autre côté, si vous pouvez utiliser Java 7 ou version ultérieure, le "nouveau" try-with-resources tel que décrit dans la réponse de @Mike Clark est la meilleure solution.
Si vous n'utilisez pas Java 7 ou version ultérieure pour votre nouveau code, vous êtes probablement dans un trou profond et vous creusez plus profondément.
La meilleure pratique actuelle pour essayer/attraper/impliquer finalement des objets pouvant être fermés (par exemple, des fichiers) est d'utiliser Java 7 l'instruction try-with-resource de Java, par exemple:
try (FileReader reader = new FileReader("ex.txt")) {
System.out.println((char)reader.read());
} catch (IOException ioe) {
ioe.printStackTrace();
}
Dans ce cas, le FileReader est automatiquement fermé à la fin de l'instruction try, sans qu'il soit nécessaire de le fermer dans un bloc finally explicite. Voici quelques exemples:
http://ppkwok.blogspot.com/2012/11/Java-cafe-2-try-with-resources.html
La description officielle Java est à:
http://docs.Oracle.com/javase/7/docs/technotes/guides/language/try-with-resources.html
Java 7 ajoutera des blocs Automatic Resource Management . Ils sont très similaires aux using
de C #.
Josh Bloch a écrit la proposition technique , que je recommande fortement de lire. Non seulement parce que cela vous donnera une longueur d'avance sur une prochaine fonctionnalité de langage Java 7, mais parce que la spécification motive le besoin d'une telle construction et, ce faisant, illustre comment écrire du code correct même en l'absence d'ARM.
Voici un exemple du code d'Asker, traduit en ARM:
try (FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos))
{
oos.writeObject(shapes);
}
catch (FileNotFoundException ex)
{
// handle the file not being found
}
catch (IOException ex)
{
// handle some I/O problem
}
J'ai généralement une IOUtil de petite classe avec une méthode telle que:
public static void close(Closeable c) {
if (c != null) {
try {
c.close();
}
catch (IOException e) {
// ignore or log
}
}
}
Et les gars? Pas de chèque nul, pas de surprise. Tout est nettoyé à la sortie.
try {
final FileOutputStream fos = new FileOutputStream(file);
try {
final ObjectOutputStream oos = new ObjectOutputStream(fos);
try {
oos.writeObject(shapes);
oos.flush();
}
catch(IOException ioe) {
// notify user of important exception
}
finally {
oos.close();
}
}
finally {
fos.close();
}
}
catch (FileNotFoundException ex) {
// complain to user
}
catch (IOException ex) {
// notify user
}
Malheureusement, il n'y a pas de support de niveau de langue. Mais il existe de nombreuses bibliothèques qui rendent cela simple. Vérifiez la bibliothèque commons-io. Ou google-guava moderne @ http://guava-libraries.googlecode.com/svn/trunk/javadoc/index.html
Tu le fais bien. Cela me dérange aussi. Vous devez initialiser ces flux à null explicitement - c'est une convention courante. Tout ce que vous pouvez faire est de rejoindre le club et de vouloir using
.
Ce n'est pas une réponse directe à votre argument mais c'est un fait malheureux parce que finally
et catch
sont tous les deux associés à try
les gens pensent qu'ils appartiennent ensemble. La meilleure conception pour les blocs try
est d'avoir un catch
ou un finally
mais pas les deux.
Dans ce cas, vos commentaires suggèrent que quelque chose ne va pas. Pourquoi, dans une méthode traitant des E/S de fichiers, nous plaignons-nous de quoi que ce soit à l'utilisateur. Nous pourrions être en train de courir en profondeur sur un serveur quelque part avec aucun utilisateur en vue.
Ainsi, le code que vous présentez ci-dessus devrait avoir un finally
pour échouer gracieusement lorsque les choses tournent mal. Cependant, il n'a pas la capacité de traiter intelligemment les erreurs, donc votre catch
appartient quelque part plus haut dans la chaîne d'appel.