web-dev-qa-db-fra.com

Java io moche essayer-finalement bloquer

Existe-t-il un moyen moins laid de traiter l'exception close() pour fermer les deux flux alors:

    InputStream in = new FileInputStream(inputFileName);
    OutputStream out = new FileOutputStream(outputFileName);

    try {
        copy(in, out);
    } finally {
        try {
            in.close();
        } catch (Exception e) {
            try {
                // event if in.close fails, need to close the out
                out.close();
            } catch (Exception e2) {}
                throw e; // and throw the 'in' exception
            }
        }
        out.close();
    }

mise à jour: tout le code ci-dessus est dans un plus try-catch, merci pour les avertissements.

FINALEMENT (après les réponses):

Et une bonne méthode d’utilité peut être utilisée avec Execute Around idiom (merci Tom Hawtin).

42
Tom Brito

Ceci est l'idom correct (et cela fonctionne bien):

   InputStream in = null;
   OutputStream out = null;
   try {
       in = new FileInputStream(inputFileName);
       out = new FileOutputStream(outputFileName);
       copy(in, out);
   finally {
       close(in);
       close(out);
   }

  public static void close(Closeable c) {
     if (c == null) return; 
     try {
         c.close();
     } catch (IOException e) {
         //log the exception
     }
  }

La raison pour laquelle cela fonctionne bien est que l'exception levée avant votre arrivée sera finalement levée après la fin de votre code, à condition que votre code final ne lance pas lui-même une exception ou ne se termine pas autrement de façon anormale.

Edit: Depuis Java 7 (et Android SDK 19 - KitKat), il existe maintenant une syntaxe Try avec ressources pour rendre cela plus propre. Comment traiter cela est abordé dans cette question .

51
Yishai

Vous pouvez implémenter une méthode utilitaire:

public final class IOUtil {
  private IOUtil() {}

  public static void closeQuietly(Closeable... closeables) {
    for (Closeable c : closeables) {
        if (c != null) try {
          c.close();
        } catch(Exception ex) {}
    }
  }
}

Ensuite, votre code serait réduit à:

try {
  copy(in, out);
} finally {
  IOUtil.closeQuietly(in, out);
}

Additionnel

J'imagine qu'il y aura une telle méthode dans une bibliothèque open source tierce. Cependant, je préfère éviter les dépendances inutiles à la bibliothèque, sauf si j'utilise une grande partie de ses fonctionnalités. Par conséquent, j'ai tendance à mettre en œuvre des méthodes d'utilité simples comme celle-ci.

32
Adamski
try {
    final InputStream in = new FileInputStream(inputFileName);
    try {
        final OutputStream out = new FileOutputStream(outputFileName);    
        try {
            copy(in, out);
            out.flush(); // Doesn't actually do anything in this specific case.
        } finally {
            out.close();
        }
    } finally {
        in.close();
    }
} catch (IOException exc) {
    throw new SomeRelevantException(exc);
}

N'oubliez pas que l'ouverture d'un flux peut générer une exception, vous avez donc besoin d'une try entre les ouvertures du flux (veuillez ne pas faire de piratage impliquant nulls. Tout ce qui peut lancer une Error (qui n'est pas une instance de Exception).

Il s'avère que catch et finally devraient rarement partager la même try.

Depuis Java SE 7, vous pouvez écrire en utilisant try-with-resource pour éviter autant d’indentation. Il fait plus ou moins la même chose bien qu'il y ait une exception supprimée cachée.

try (
    final InputStream in = new FileInputStream(inputFileName);
    final OutputStream out = new FileOutputStream(outputFileName);    
) {
    copy(in, out);
    out.flush(); // Doesn't actually do anything in this specific case.
} catch (IOException exc) {
    throw new SomeRelevantException(exc);
}

Vous voudrez peut-être utiliser le Execute Around idiom .

Je crois que le bon moyen standard de copier consiste à utiliser la variable transferTo/transferFrom de NIO.

18

Guava has very Nice IO - Des API qui en éliminent le besoin. Par exemple, votre exemple serait:

Files.copy(new File(inputFileName), new File(outputFileName));

Plus généralement, il utilise les concepts InputSuppliers et OutputSuppliers pour permettre la création des InputStreams et OutputStreams au sein de ses méthodes utilitaires, lui permettant ainsi un contrôle total sur ces dernières afin qu'il puisse gérer la fermeture correctement.

De plus, il a Closeables.closeQuietly(Closeable) qui est fondamentalement le type de méthode suggéré par la plupart des réponses.

Les éléments IO qu'il contient sont toujours en version bêta et peuvent être modifiés, mais cela vaut la peine de vérifier et même d'utiliser, en fonction de votre travail.

8
ColinD

Je crois fermement qu'en Java 7.0, vous n'avez plus besoin de fermer explicitement le flux vous-même. Fonctions linguistiques en Java 7

try (BufferedReader br = new BufferedReader(new FileReader(path)) {
   return br.readLine();
}
7
vodkhang

DepuisJava 7, il existe un moyen beaucoup plus agréable d’écrire bloc try-finally en ce qui concerne les ressources Closeable.

Maintenant, vous pouvez créer vos ressources entre parenthèses après le mot clé try, comme ceci:

try (initialize resources here) {
   ...
}

Et ils seront fermésautomatiquementaprès la fin du bloc de code. La partie finally n'est pas nécessaire. 

Un exemple :

try (
   ZipFile zf = new ZipFile(zipFileName);
   BufferedWriter writer = Files.newBufferedWriter(outputFilePath, charset);
) {
    // Enumerate each entry
    for (Enumeration entries = zf.entries(); entries.hasMoreElements();) {
        // Get the entry name and write it to the output file
        String newLine = System.getProperty("line.separator");
        String zipEntryName = ((Java.util.Zip.ZipEntry)entries.nextElement()).getName() + newLine;
        writer.write(zipEntryName, 0, zipEntryName.length());
    }
}

Et après que la boucle for soit terminée, les ressources seront fermées!

6
darijan

Vous avez, dans les biens communs, dans IOUtils , certaines méthodes closeQuietly.

5
Istao

Une astuce que j’utilise parfois est de définir une méthode appelée closeQuietly(Closeable) qui teste pour voir si son argument est null puis le ferme, en ignorant toutes les exceptions. Mais vous devez être prudent en fermant OutputStreams et Writers de cette façon, car ils risquent en réalité de générer une exception qui Matters; par exemple. si le flush final échoue.

Les choses vont probablement s'améliorer avec Java 7. Selon les rapports, il disposera d'une nouvelle construction offrant un moyen plus concis de gérer les ressources gérées. par exemple. flux qui doivent être fermés quand ils ont fini avec.

Enfin, vous devez savoir que votre exemple présente un bogue. Si la méthode appelle pour ouvrir le deuxième flux, le premier flux ne sera pas fermé. La deuxième ouverture doit être effectuée à l'intérieur du bloc try.

2
Stephen C

Dans la plupart des cas, l'exception 'in' close () n'est pas pertinente, donc:

    try {
      copy(in, out);
    } finally {
    try {  in.close()  }  catch (Exception e) { /* perhaps log it */ }
    try {  out.close() }  catch (Exception e) {/* perhaps log it */ }
    } 

C'est généralement une mauvaise pratique d'avaler des exceptions, mais dans ce cas, je pense que ça va.

1
leonbloy

Voici ma réponse si tout va bien beaucoup mieux

https://stackoverflow.com/a/35623998/2585433

try {
    fos = new FileOutputStream(new File("..."));
    bos = new BufferedOutputStream(fos);
    oos = new ObjectOutputStream(bos);
}
catch (Exception e) {
}
finally {
    Stream.close(oos,bos,fos);
}


class Stream {

public static void close(AutoCloseable... array) {
    for (AutoCloseable c : array) {
        try {c.close();}
        catch (IOException e) {}
        catch (Exception e) {}
    }
  } 
}
0
Noor Nawaz