inputstream java se8
Cerrando un Java FileInputStream (9)
De acuerdo, he estado haciendo lo siguiente (los nombres de variables han sido cambiados):
FileInputStream fis = null;
try
{
fis = new FileInputStream(file);
... process ...
}
catch (IOException e)
{
... handle error ...
}
finally
{
if (fis != null)
fis.close();
}
Recientemente, comencé a usar FindBugs, lo que sugiere que no estoy cerrando correctamente las transmisiones. 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.
En la mayoría de los casos, creo que es mejor no detectar 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();
}
A excepción de FileNotFoundException
, generalmente no puede "evitar" una 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.
Como IOException
es una excepción comprobada, deberá 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 excepciones que envuelve la IOException
en una RuntimeException
o un tipo de excepción abstracta.
Detalle: soy consciente de que el código anterior absorbe cualquier excepción del bloque try
cuando la operación de close
en el bloque finally
produce una IOException
. No creo que sea un gran problema: en general, la excepción del bloque try
será la misma IOException
que hace que falle el 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.
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)
{
}
}
Esperemos que tengamos cierres en Java algún día, y luego perderemos mucha verbosidad.
Entonces, en su lugar, habrá un método de ayuda en alguna parte de Java que pueda importar, probablemente llevará una interfaz de "cierre" y también un bloque. Dentro de ese método auxiliar, try {closable.close ()} catch (IOException ex) {// blah} se define de una vez y para siempre, y luego podrás escribir
Inputstream s = ....;
withClosable(s) {
//your code here
}
No hay mucho que agregar, a excepción de una sugerencia estilística muy pequeña. En este caso, se aplica el ejemplo canónico de código de autoedición : proporcione un nombre de variable descriptiva a IOException
ignorada que debe captar en close()
.
Entonces, la respuesta de Squiddle es:
public static void closeQuietly(InputStream s) {
try {
s.close();
} catch (IOException ignored) {
}
}
También puedes usar un método de ayudante estático simple:
public static void closeQuietly(InputStream s) {
if (null == s) {
return;
}
try {
s.close();
} catch (IOException ioe) {
//ignore exception
}
}
y usa esto desde tu bloque final.
Podría usar la función de prueba con recursos que se agregó a JDK7. Fue creado precisamente para tratar con este tipo de cosas
static String readFirstLineFromFile(String path) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
}
}
La documentacion dice:
La instrucción try-with-resources garantiza que cada recurso se cierre al final de la declaración.
¿Le preocupa principalmente obtener un informe limpio de FindBugs o tener un código que funcione? Estos no son necesariamente lo mismo. Su código original está bien (aunque me desharía de la verificación redundante if (fis != null)
OutOfMemoryException
if (fis != null)
ya que de lo OutOfMemoryException
se hubiera lanzado una 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. Simplemente no vale la pena molestarse en hacer su código más sofisticado para evitar el escenario extremadamente improbable de
- obtienes una IOException y
- esto sucede tan a menudo que comienza a toparse con problemas de retraso del finalizador.
Editar: si obtiene tantas IOExcepciones que está teniendo problemas con la cola del finalizador, entonces ¡tendrá peces mucho más grandes para freír! Se trata de obtener una sensación de perspectiva.
Para Java 7 y superior , debe usar try-with-resources :
try (InputStream in = new FileInputStream(file)) {
// TODO: work
} catch (IOException e) {
// TODO: handle error
}
Si estás atascado en Java 6 o menos ...
Este patrón evita perder el tiempo con nulo :
try {
InputStream in = new FileInputStream(file);
try {
// TODO: work
} finally {
in.close();
}
} catch (IOException e) {
// TODO: error handling
}
Para obtener más detalles sobre cómo lidiar con el cierre de manera efectiva, lea esta publicación en el blog: Java: cómo no hacer un desastre en el manejo de flujos . Tiene más código de muestra, más profundidad y cubre las trampas de envolverse en un bloque de captura .
La siguiente solución arroja correctamente una excepción si falla el cierre 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 llamar por segunda vez no tiene ningún efecto .
Esto depende de los cerrables de guayaba, pero uno puede escribir su propio método closeQuietly si lo prefiere, como lo muestra squiddle (ver también serg10 ).
Informar sobre un error cercano, en el caso general, es importante porque close podría escribir algunos bytes finales en la secuencia, por ejemplo, debido a la memoria intermedia. 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).