Question

Je viens de jouer avec l'API du système de fichiers Java et la fonction suivante, utilisée pour copier des fichiers binaires, a été utilisée. La source originale provient du Web, mais j’ai ajouté des clauses try / catch / finally pour être sûr que, si quelque chose se passait mal, les flux de mémoire tampon seraient fermés (et donc mes ressources de système d’exploitation libérées) avant de quitter la fonction.

J'ai coupé la fonction pour afficher le motif:

public static void copyFile(FileOutputStream oDStream, FileInputStream oSStream) throw etc...
{
   BufferedInputStream oSBuffer = new BufferedInputStream(oSStream, 4096);
   BufferedOutputStream oDBuffer = new BufferedOutputStream(oDStream, 4096);

   try
   { 
      try
      { 
         int c;

         while((c = oSBuffer.read()) != -1)  // could throw a IOException
         {
            oDBuffer.write(c);  // could throw a IOException
         }
      }
      finally
      {
         oDBuffer.close(); // could throw a IOException
      }
   }
   finally
   {
      oSBuffer.close(); // could throw a IOException
   }
}
Pour autant que je sache, je ne peux pas mettre les deux close () dans la clause finally car le premier close () pourrait bien lancer, puis le la seconde ne serait pas exécutée.

Je sais que C # possède le modèle Dispose qui l'aurait traité avec le mot clé à l'aide de .

Je sais même mieux qu'un code C ++ aurait ressemblé à (avec une API de type Java):

void copyFile(FileOutputStream & oDStream, FileInputStream & oSStream)
{
   BufferedInputStream oSBuffer(oSStream, 4096);
   BufferedOutputStream oDBuffer(oDStream, 4096);

   int c;

   while((c = oSBuffer.read()) != -1)  // could throw a IOException
   {
      oDBuffer.write(c);  // could throw a IOException
   }

   // I don't care about resources, as RAII handle them for me
}

Il me manque quelque chose ou dois-je vraiment produire du code moche et gonflé en Java uniquement pour gérer les exceptions dans la méthode close () d'un flux mis en tampon?

(S'il vous plaît, dites-moi que je me trompe quelque part ...)

MODIFIER: s’agit-il de moi ou lors de la mise à jour de cette page, j’ai vu la question et toutes les réponses diminuées d’un point en quelques minutes? Est-ce que quelqu'un s'amuse trop en restant anonyme?

EDIT 2: McDowell a proposé un lien très intéressant que j’avais le sentiment de devoir mentionner ici: http: //illegalargumentexception.blogspot .com / 2008/10 / java-how-not-to-make-mess-of-stream.html

EDIT 3: Après le lien de McDowell, je suis tombé sur une proposition pour Java 7 d’un modèle similaire au modèle C #: http://tech.puredanger.com/java7/#resourceblock . Mon problème est explicitement décrit. Apparemment, même avec Java do , les problèmes persistent.

Était-ce utile?

La solution

Le modèle try / finally est le moyen correct de gérer les flux dans la plupart des cas pour Java 6 et versions antérieures.

Certains préconisent la fermeture silencieuse des flux. Faites attention en procédant de la sorte pour ces raisons: Java: comment ne pas gâcher la gestion des flux

Java 7 introduit try-with-resources :

/** transcodes text file from one encoding to another */
public static void transcode(File source, Charset srcEncoding,
                             File target, Charset tgtEncoding)
                                                             throws IOException {
    try (InputStream in = new FileInputStream(source);
         Reader reader = new InputStreamReader(in, srcEncoding);
         OutputStream out = new FileOutputStream(target);
         Writer writer = new OutputStreamWriter(out, tgtEncoding)) {
        char[] buffer = new char[1024];
        int r;
        while ((r = reader.read(buffer)) != -1) {
            writer.write(buffer, 0, r);
        }
    }
}

AutoClosable types seront automatiquement fermés:

public class Foo {
  public static void main(String[] args) {
    class CloseTest implements AutoCloseable {
      public void close() {
        System.out.println("Close");
      }
    }
    try (CloseTest closeable = new CloseTest()) {}
  }
}

Autres conseils

Il y a des problèmes, mais le code que vous avez trouvé sur le Web est vraiment médiocre.

La fermeture des flux de mémoire tampon ferme le flux situé en dessous. Vous ne voulez vraiment pas faire ça. Tout ce que vous voulez faire est de vider le flux de sortie. De plus, il est inutile de spécifier que les flux sous-jacents concernent des fichiers. Les performances sont nulles parce que vous copiez un octet à la fois (en fait, si vous utilisez java.io, vous pouvez utiliser transferTo / transferFrom, qui est encore un peu plus rapide). Tant que nous en sommes, les noms de variables sont nuls. Donc:

public static void copy(
    InputStream in, OutputStream out
) throw IOException {
    byte[] buff = new byte[8192];
    for (;;) {
        int len = in.read(buff);
        if (len == -1) {
            break;
        }
        out.write(buff, 0, len);
    }
}

Si vous constatez que vous utilisez souvent try-finally, vous pouvez le factoriser avec l'option "exécuter autour de". idiome.

À mon avis: Java devrait avoir un moyen de fermer les ressources en fin de champ. Je suggère d’ajouter private en tant qu’opérateur postfix unaire pour fermer à la fin du bloc englobant.

Oui, c'est comme ça que Java fonctionne. Il y a inversion de contrôle - l'utilisateur de l'objet doit savoir comment nettoyer l'objet au lieu de l'objet lui-même nettoyant après lui-même. Cela conduit malheureusement à beaucoup de code de nettoyage dispersé dans votre code java.

C # a le " en utilisant " mot-clé pour appeler automatiquement Dispose lorsqu'un objet sort de la portée. Java n’a rien de tel.

Malheureusement, ce type de code a tendance à être un peu gonflé en Java.

En passant, si l'un des appels à oSBuffer.read ou oDBuffer.write lève une exception, vous voudrez probablement laisser cette exception imprégner la hiérarchie des appels.

Si un appel non protégé à close () se trouve dans une clause finally, l'exception d'origine sera remplacée par une exception produite par l'appel close (). En d'autres termes, une méthode close () défaillante peut masquer l'exception d'origine générée par read () ou write (). Donc, je pense que vous voulez ignorer les exceptions levées par close () si et seulement si les autres méthodes ne l’ont pas été.

Je résous généralement ce problème en incluant un appel explicite à l'intérieur de la tentative intérieure:

  try {
    while (...) {
      read...
      write...
    }
    oSBuffer.close(); // exception NOT ignored here
    oDBuffer.close(); // exception NOT ignored here
  } finally {
    silentClose(oSBuffer); // exception ignored here
    silentClose(oDBuffer); // exception ignored here
  }
  static void silentClose(Closeable c)  {
    try {
      c.close();
    } catch (IOException ie) {
      // Ignored; caller must have this intention
    }
  }

Enfin, pour des performances optimales, le code devrait probablement fonctionner avec des tampons (plusieurs octets par lecture / écriture). Cela ne peut pas être sauvegardé par des numéros, mais moins d’appels devraient être plus efficaces que d’ajouter des flux mis en mémoire tampon.

Pour les tâches IO courantes telles que la copie d'un fichier, un code tel que celui présenté ci-dessus réinvente la roue. Malheureusement, le JDK ne fournit aucun utilitaire de niveau supérieur, contrairement à apache commons-io.

Par exemple, FileUtils contient diverses méthodes utilitaires permettant de travailler avec des fichiers et des répertoires (y compris la copie). Par contre, si vous devez vraiment utiliser le support IO dans le JDK, IOUtils contient un ensemble de méthodes closeQuietly () permettant de fermer les lecteurs, les rédacteurs, les flux, etc. sans générer d'exceptions.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top