19 votes

Comment gérer correctement une IOException de close() ?

Les classes d'E/S Java java.io.Reader , java.io.Writer , java.io.InputStream , java.io.OutpuStream et leurs diverses sous-classes ont toutes une close() qui peut lancer un IOException .

Existe-t-il un consensus sur la manière appropriée de traiter ces exceptions ?

J'ai souvent vu des recommandations pour les ignorer silencieusement, mais cela ne semble pas correct, et au moins dans le cas de ressources ouvertes à l'écriture, un problème lors de la fermeture du fichier pourrait signifier que les données non vidées ne pourraient pas être écrites/envoyées.

D'autre part, lorsque je lis des ressources, je ne comprends pas du tout pourquoi close() pourrait lancer et ce qu'il faut faire.

Existe-t-il donc une recommandation standard ?

Une question connexe est Est-ce que close ne lève jamais une IOException ? mais il s'agit plutôt de savoir quelles sont les implémentations réellement faire et non sur la façon de gérer les exceptions.

7voto

Qwerky Points 10847

Enregistrez-le.

Tu ne peux pas vraiment faire n'importe quoi (par exemple, écrire un code qui récupère l'erreur), mais il est généralement utile de le faire savoir.

Edit :
Après une enquête plus approfondie et la lecture des autres commentaires, je dirais que si vous voulez le gérer, vous devrez connaître les détails de la mise en œuvre. Inversement, vous devez probablement connaître les détails de l'implémentation pour décider si vous devez la gérer.

D'un point de vue réaliste, je ne vois pas d'exemple de flux où la lecture ou l'écriture fonctionnerait correctement sans lever d'exception, mais où la fermeture le ferait.

4voto

ron Points 4636

"Ignorer ou se contenter d'enregistrer est généralement une mauvaise idée." Cela ne répond pas à la question. Que doit-on faire d'une IOException provenant de close() ? La relancer ne fait que repousser le problème plus haut, où il est encore plus difficile à gérer.

Théorie

Pour répondre directement à votre question : Lorsque cette action IO a échoué, alors, selon les circonstances, vous pouvez vouloir

  • annuler les modifications (ne pas laisser un fichier partiellement écrit sur le disque, le supprimer)
  • réessayer (peut-être après un rollback)
  • ignorer (et continuer)
  • interrompre la tâche en cours (annuler)

Vous pouvez souvent voir les 3 derniers dans les boîtes de dialogue montrées à l'utilisateur. En effet, déléguer le choix à l'utilisateur est une possibilité.

Je crois que l'essentiel est de ne pas laisser le système dans un état incohérent. Le fait d'avaler une exception fermée pourrait vous laisser avec un fichier endommagé, ce qui provoquerait des erreurs désagréables par la suite.

Pratique

Il est un peu fastidieux de travailler avec des exceptions vérifiées. Des options se présentent :

  • Convertissez-les en RuntimeException les jeter et les laisser être traités à un niveau suffisamment élevé.

  • Continuez à utiliser les exceptions vérifiées et souffrez de la douleur syntaxique.

  • Utiliser des constructions de gestion des erreurs plus composables, comme la monade IO (qui n'est pas vraiment disponible en Java, du moins pas sans un nombre embarrassant d'accolades (cf. http://apocalisp.wordpress.com/2008/05/16/thrower-functor/ ) et pas à pleine puissance)

Plus d'informations sur les monades IO

Lorsque vous renvoyez un résultat dans le contexte de la monade IO, vous n'exécutez pas réellement une action IO, mais vous renvoyez plutôt la description de cette action. Pourquoi ? Ces actions disposent d'une API avec laquelle elles se composent agréablement (contrairement au massacre des throws/try/catch).

Vous pouvez décider si vous souhaitez effectuer un traitement de style exception de contrôle (avec Option o Validation˙ in the return type), or dynamic style handling, with bracketing only on the final appel unsafePerformIO` (qui exécute réellement l'action IO composée).

Pour se faire une idée, voir mon gist Scala http://gist.github.com/2709535 qui accueille le executeAndClose méthode. Elle renvoie à la fois le résultat du traitement de la ressource (si disponible) et une ressource non fermée (si la fermeture a échoué). C'est alors à l'utilisateur de décider comment traiter une telle ressource non fermable.

Il pourrait être renforcé par Validation / ValidationNEL au lieu de Option si l'on a besoin d'une ou de plusieurs exceptions réelles également.

1voto

Raedwald Points 8862

Votre code qui appelle InputStream.close() o OutputStream.close() est un code de haut niveau qui délègue au niveau inférieur InputStream o OutputStream pour effectuer des calculs de haut niveau.

Si l'on veut lancer une exception

Vous n'avez que 3 choix sur ce que vous devez faire.

  • Attrapez et avalez l'exception, en la rejetant.
  • Laissez-le se propager vers le haut.
  • Attrapez l'exception, et lancez un autre type d'exception. Normalement, vous devriez utiliser une exception chaînée dans ce cas.

Les deux dernières options sont similaires dans la mesure où votre code lève une exception. Cette question est donc en fait un cas particulier de la question du Quand mon code doit-il lever une exception ? . Le site réponse correcte à cette question dans ce contexte, c'est : si, et seulement si, l'alternative est de ne pas remplir une condition de poste. de votre code ou pour maintenir un invariant de votre code . Les conditions de poste précisent ce que votre méthode est censée faire. Les invariants précisent les caractéristiques de la classe.

Vous devez donc vous demander si le close() le fait de lancer une exception empêche votre méthode faisant ce qu'elle doit faire.

  • Si cela n'empêche pas votre méthode de faire son travail, la bonne chose à faire est d'avaler l'exception. Malgré ce que beaucoup de gens vous diront.
  • Si close() le lancer empêche votre méthode de faire son travail, et la méthode peut lancer IOException vous ne pouvez rien faire, en laissant l'exception se propager vers le haut .
  • Si close() empêche votre méthode de faire son travail, mais la méthode ne peut pas jeter IOException vous devez attraper le IOException et de la rejeter comme une classe d'exception différente, en enregistrant la IOException comme la cause de l'exception levée.

Je ne connais pas de circonstances dans lesquelles un InputStream.close() Le fait de lancer une exception peut empêcher un calcul d'aboutir. Cet appel à close() arrive naturellement après vous avez fini de lire les données qui vous intéressent.

Les flux de sortie, en revanche, peuvent mettre en mémoire tampon les données à écrire sur la destination de sortie, soit en interne (dans le code Java), soit à un niveau inférieur (dans le système d'exploitation). Vous ne pouvez donc pas être sûr que les opérations d'écriture vers un flux de sortie ont effectivement donné lieu à une sortie réelle jusqu'à ce que OutputStream.close() est retourné avec succès ( sans en lançant une exception). Vous devez donc traiter une exception lancée par OutputStream.close() comme un échec d'écriture.

Votre méthode est responsable du maintien de ses invariants. Parfois, cela nécessitera une opération de nettoyage ou de retour en arrière si close() lance une exception. Vous devriez mettre le code pour cela dans un catch o finally pour la clause IOException même si vous souhaitez que l'exception se propage vers le haut. Si vous utilisez une clause catch et que vous voulez la propager, vous devrez la relancer.

S'il faut enregistrer l'exception

Certaines personnes vous conseille d'enregistrer l'exception . C'est presque toujours un mauvais conseil. Les messages de journalisation font partie de l'interface utilisateur de votre programme. Fondamentalement, vous devez toujours vous demander s'il faut journaliser tout ce qui est pas du tout, car un verbiage inutile peut distraire et embrouiller les utilisateurs qui lisent votre fichier journal (les "utilisateurs" incluent les administrateurs système). Chaque message enregistré doit avoir un but utile. Chacun doit fournir des informations qui aident l'utilisateur à prendre des décisions .

En signalant spécifiquement que close() échoué est rarement utile. Comment peut-elle aider un utilisateur à prendre une décision ? Si l'exception n'a pas empêché votre méthode de faire son travail, il n'y a pas de problème et aucune action de l'utilisateur n'est nécessaire. Si votre programme n'a pas réussi à close() le cours d'eau, et que es un problème, que peut faire l'utilisateur pour l'aider ?

Le code de bas niveau n'est généralement pas responsable de la journalisation. Il effectue plutôt des opérations abstraites, signalant les échecs aux parties de niveau supérieur de votre programme en lançant des exceptions. Le code qui ferme un flux est typiquement de bas niveau, donc le code qui détecte que la fermeture d'un flux est une exception. close() est trop bas pour faire de la journalisation.

Le fait spécifique que close() échoué est rarement utile. Ce qui peut être utile, c'est de savoir que l'opération abstraite que votre méthode est censée exécuter a échoué. Pour ce faire, le code de niveau supérieur doit attraper toutes les exceptions attendues et signaler que l'opération a échoué, plutôt que de demander à votre méthode de signaler précisément que "close failed".

0voto

Aravind Points 508

Essayez d'utiliser la chasse d'eau avant de fermer l'écrivain. La principale raison de l'exception lors de la fermeture est que d'autres ressources peuvent avoir utilisé les données ou que le graveur/lecteur peut ne pas être ouvert du tout. Essayez de savoir si la ressource est ouverte avant de la fermer.

0voto

Reza Points 473

Tout d'abord, n'oubliez pas de placer la méthode close() dans la section "finally" de votre bloc try catch. Ensuite, vous pouvez entourer la méthode close d'un autre bloc try catch et enregistrer l'exception.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X