2008-10-01 12 views
30

bien, he estado haciendo las siguientes variables (nombres han sido cambiados):Java FileInputStream


FileInputStream fis = null; 
try 
{ 
    fis = new FileInputStream(file); 

    ... process ... 

} 
catch (IOException e) 
{ 
    ... handle error ... 
} 
finally 
{ 
    if (fis != null) 
     fis.close(); 
} 
 

Recientemente, he empezado a utilizar FindBugs, lo que sugiere que no estoy cerrando adecuadamente corrientes. Decido ver si hay algo que se pueda hacer con un bloque finally {}, y luego veo, oh sí, close() puede lanzar IOException. ¿Qué se supone que las personas hagan aquí? Las bibliotecas de Java arrojan demasiadas excepciones comprobadas.

+0

'fis' no puede ser nulo en el punto en que lo está probando. Puede ser nulo en el bloque 'finally' que falta donde debería probarlo y cerrarlo. Pero la pregunta es obsoleta desde la introducción de la sintaxis 'try-with-resources'. – EJP

+0

He modificado el código en consecuencia solo para que las personas no sean engañosas. –

Respuesta

38

Para Java 7 y superior try-with-resources debe usarse:

try (InputStream in = new FileInputStream(file)) { 
    // TODO: work 
} catch (IOException e) { 
    // TODO: handle error 
} 

Si está atrapado en Java 6 o por debajo ...

Este patrón Evita rededor del nula:

try { 
     InputStream in = new FileInputStream(file); 
     try { 
      // TODO: work 
     } finally { 
      in.close(); 
     } 
    } catch (IOException e) { 
     // TODO: error handling 
    } 

Para un mayor detalle sobre cómo tratar eficazmente con cerca, lea esta entrada del blog: Java: how not to make a mess of stream handling. Tiene más código de muestra, más profundidad y cubre los peligros de envolver cerrar en un atrapar el bloque.

25

Algo como lo siguiente debería hacerlo, dependiendo de si tira o traga la IOException al intentar cerrar la secuencia.

FileInputStream fis = null; 
try 
{ 
    fis = new FileInputStream(file); 

    ... process ... 


} 
catch (IOException e) 
{ 
    ... blah blah blah ... 
} 
finally 
{ 
    try 
    { 
     if (fis != null) 
      fis.close(); 
    } 
    catch (IOException e) 
    { 
    } 
} 
4

También es posible usar un simple método de ayuda estática:

public static void closeQuietly(InputStream s) { 
    if (null == s) { 
     return; 
    } 
    try { 
     s.close(); 
    } catch (IOException ioe) { 
     //ignore exception 
    } 
} 

y utilizar esta información de su bloque finally.

+0

Bueno, eso parece invitar a una NullPointerException ... –

+0

gracias por el comentario, lo cambió en consecuencia – squiddle

0

Esperemos que tengamos cierres en Java algún día, y luego perderemos mucha verbosidad.

Por lo tanto, en su lugar, habrá un método de ayuda en alguna parte de javaIO que pueda importar, probablemente llevará una interfaz "Cerrable" y también un bloque. Dentro de ese método de ayuda el try {closable.close()} catch (IOException ex) {// bla} se define una vez por todas, y entonces usted será capaz de escribir

Inputstream s = ....; 
withClosable(s) { 
    //your code here 
} 
3

No hay mucho que añadir, a excepción de una sugerencia estilística muy pequeña. El ejemplo canónico del código de autoedición se aplica en este caso: proporcione un nombre de variable descriptivo al IOException ignorado que debe detectar en close(). respuesta

Así de squiddle se convierte en:

public static void closeQuietly(InputStream s) { 
    try { 
     s.close(); 
    } catch (IOException ignored) { 
    } 
} 
-4

¿Está preocupado principalmente con la obtención de un informe limpio de FindBugs o con tener código que funciona? Estos no son necesariamente lo mismo. Su código original está bien (aunque me desharía de la verificación redundante if (fis != null) ya que de lo contrario se hubiera arrojado OutOfMemoryException). FileInputStream tiene un método de finalizador que cerrará la transmisión por usted en el caso improbable de que realmente reciba una IOException en su procesamiento. No es simplemente la pena la molestia de hacer su código más sofisticado para evitar el escenario extremadamente improbable de

  1. se obtiene una IOException y
  2. esto ocurre tan a menudo que usted comienza a funcionar en ediciones finalizador de retraso acumulado.

Editar: si está recibiendo tantas IOExceptions que está ejecutando a tener problemas con la cola finalizador entonces usted ha pescado mucho, mucho más importantes que hacer! Se trata de obtener una sensación de perspectiva.

+2

Confiar en los finalizadores aquí es una muy mala jugada. El finalizador solo se ejecutará cuando el objeto de transmisión sea basura, y es probable que desconfíe de los identificadores de archivos mucho antes de que eso suceda. * Nunca * confíe en los finalizadores para la funcionalidad. No puedo enfatizar eso lo suficiente. – skaffman

+1

No hay garantía de si se ejecutará un finalizador. – kpirkkal

+0

Sé sobre los peligros de confiar en los finalizadores. Mi punto era simplemente que obtener IOExceptions no es un flujo normal y para este caso inusual probablemente no valga la pena obsesionarse demasiado con el cierre del descriptor de archivo en ese momento. ¿Cuándo vio por última vez múltiples IOExceptions en un FileInputStream y consideró este comportamiento normal que un programa debería tratar y seguir ejecutando? –

2

En la mayoría de los casos, me parece que es simplemente mejor no para atrapar las excepciones IO, y simplemente usar try-finally:

final InputStream is = ... // (assuming some construction that can't return null) 
try { 
    // process is 
    ... 
} finally { 
    is.close(); 
} 

excepción de FileNotFoundException, por lo general, no se puede "evitar" un IOException. Lo único que queda por hacer es informar un error, y normalmente manejará eso más arriba en la pila de llamadas, por lo que me parece mejor propagar la excepción.

Dado que IOException es una excepción marcada, tendrá que declarar que este código (y cualquiera de sus clientes) throws IOException. Eso puede ser demasiado ruidoso, o puede que no desee revelar los detalles de implementación del uso de IO. En ese caso, puede envolver todo el bloque con un controlador de excepción que envuelve IOException en un RuntimeException o un tipo de excepción abstracta.

Detalle: Soy consciente de que el código anterior se traga cualquier excepción del bloque try cuando la operación close en el bloque finally produce un IOException. No creo que sea un gran problema: generalmente, la excepción del bloque try será la misma IOException que causa la falla del close (es decir, es bastante raro que IO funcione bien y luego falle en el punto de cierre) . Si esto es una preocupación, podría valer la pena "silenciar" el cierre.

+0

Es bastante raro hasta el punto de ser desconocido en el caso de un flujo de entrada. Un flujo de salida puede fallar en 'close()' debido al 'flush()' implícito que ocurre en cualquier secuencia derivada de 'FilterInputStream'. – EJP

1

La siguiente solución arroja una excepción correctamente si el cierre falla sin ocultar una posible excepción antes del cierre.

try { 
    InputStream in = new FileInputStream(file); 
    try { 
     // work 
     in.close(); 
    } finally { 
     Closeables.closeQuietly(in); 
    } 
} catch(IOException exc) { 
    // kernel panic 
} 

Esto funciona porque se llama por segunda vez has no effect.

Esto se basa en guayaba Closeables, pero se puede escribir su propio método closeQuietly si se prefiere, como se muestra en squiddle (consulte también serg10).

Informar de un error de cierre, en el caso general, es importante porque close podría escribir algunos bytes finales en la secuencia, p. debido al almacenamiento en búfer. Por lo tanto, su usuario quiere saber si falló, o usted probablemente quiera actuar de alguna manera. Por supuesto, esto podría no ser cierto en el caso específico de un FileInputStream, no lo sé (pero por las razones ya mencionadas, creo que es mejor informar un error de cierre si se produce de todos modos).

El código anterior es un poco difícil de comprender debido a la estructura de los bloques try integrados. Podría considerarse más claro con dos métodos, uno que arroje una IOException y otro que lo atrape. Al menos eso es lo que yo optaría.

private void work() throws IOException { 
    InputStream in = new FileInputStream(file); 
    try { 
     // work 
     in.close(); 
    } finally { 
     Closeables.closeQuietly(in); 
    } 
} 

public void workAndDealWithException() { 
    try { 
     work(); 
    } catch(IOException exc) { 
     // kernel panic 
    } 
} 

Basado en http://illegalargumentexception.blogspot.com/2008/10/java-how-not-to-make-mess-of-stream.html (referenciado por McDowell).

10

Puede usar la función try-with-resources que se agregó JDK7. Fue creado precisamente para hacer frente a este tipo de cosas

static String readFirstLineFromFile(String path) throws IOException { 
    try (BufferedReader br = new BufferedReader(new FileReader(path))) { 
    return br.readLine(); 
    } 
} 

El Documenation dice:

La sentencia try-con-recursos se asegura de que cada recurso está cerrado al final de la declaración.

Cuestiones relacionadas