La declaración try-with-resources en Java

L

Introducción

try-with-resources es uno de los varios try declaraciones en Java, destinadas a liberar a los desarrolladores de la obligación de liberar los recursos utilizados en un try bloquear.

Inicialmente se introdujo en Java 7 y la idea subyacente era que el desarrollador no necesita preocuparse por la administración de recursos para los recursos que usan solo en un bloque try-catch-finalmente. Esto se logra eliminando la necesidad de finally blocks, que los desarrolladores solo usaron para cerrar recursos en la práctica.

Además, el código que usa try-with-resources suele ser más limpio y legible, por lo tanto, hace que el código sea más fácil de administrar, especialmente cuando se trata de muchos try bloques.

Sintaxis

La sintaxis de try-with-resources es casi idéntica a la sintaxis habitual de try-catch-finalmente. La única diferencia son los paréntesis después de try en el que declaramos qué recursos usaremos:

BufferedWriter writer = null;
try {
    writer = new BufferedWriter(new FileWriter(fileName));
    writer.write(str);  // do something with the file we've opened
} catch (IOException e) {
   // handle the exception
} finally {
    try {
        if (writer != null)
            writer.close();
    } catch (IOException e) {
       // handle the exception
    }
}

El mismo código escrito usando try-with-resources se vería así:

try(BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))){
    writer.write(str); // do something with the file we've opened
}
catch(IOException e){
    // handle the exception
}

La forma en que Java entiende este código:

Los recursos abiertos entre paréntesis después de la instrucción try solo serán necesarios aquí y ahora. Llamaré a su .close() métodos tan pronto como termine con el trabajo en el bloque try. Si se lanza una excepción mientras estoy en el bloque try, cerraré esos recursos de todos modos.

Antes de que se introdujera este enfoque, el cierre de recursos se realizaba manualmente, como se ve en el código anterior. Esto era esencialmente código repetitivo, y las bases de código estaban llenas de ellos, lo que reducía la legibilidad y dificultaba su mantenimiento.

los catch y finally parte del trabajo de prueba con recursos como se esperaba, con catch bloques que manejan sus respectivas excepciones y el finally bloque ejecutado independientemente de si hubo una excepción o no. La única diferencia son las excepciones suprimidas, que se explican al final de este artículo.

Nota: Desde Java 9, no es necesario declarar los recursos dentro de la declaración try-with-resources. En su lugar, podemos hacer algo como esto:

BufferedWriter writer = new BufferedWriter(new FileWriter(fileName));
try (writer) {
    writer.write(str); // do something with the file we've opened
}
catch(IOException e) {
    // handle the exception
}

Múltiples recursos

Otro buen aspecto de try-with-resources es la facilidad para agregar / eliminar recursos que estamos usando y tener la seguridad de que se cerrarán una vez que hayamos terminado.

Si quisiéramos trabajar con varios archivos, abriríamos los archivos en el try() declaración y sepárelos con un punto y coma:

try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName));
    Scanner scanner = new Scanner(System.in)) {
if (scanner.hasNextLine())
    writer.write(scanner.nextLine());
}
catch(IOException e) {
    // handle the exception
}

Java luego se encarga de llamar .close() en todos los recursos que hemos abierto en try().

Nota: Están cerrados en orden de declaración inverso, lo que significa que, en nuestro ejemplo, scanner estará cerrado antes del writer.

Clases admitidas

Todos los recursos declarados en try() debe implementar el AutoCloseable interfaz. Suelen tratarse de varios tipos de escritores, lectores, sockets, flujos de entrada o salida, etc. Todo lo que necesite escribir resource.close() una vez que haya terminado de trabajar con él.

Esto, por supuesto, incluye objetos definidos por el usuario que implementan la AutoClosable interfaz. Sin embargo, rara vez se encontrará con una situación en la que desee escribir sus propios recursos.

En caso de que eso suceda, debe implementar el AutoCloseable o Closeable (solo para preservar la compatibilidad con versiones anteriores, prefiera AutoCloseable) interfaz y anular la .close() método:

public class MyResource implements AutoCloseable {
    @Override
    public void close() throws Exception {
        // close your resource in the appropriate way
    }
}

Manejo de excepciones

Si se lanza una excepción desde dentro de un bloque de prueba con recursos de Java, cualquier recurso abierto entre paréntesis de try el bloque aún se cerrará automáticamente.

Como se mencionó anteriormente, try-with-resources funciona igual que try-catch-finalmente, excepto con una pequeña adición. La adición se llama excepciones suprimidas. Es no Es necesario comprender las excepciones suprimidas para poder usar try-with-resources, pero leer sobre ellas puede ser útil para depurar cuando nada más parece funcionar.

Imagina una situación:

  • Por alguna razón, se produce una excepción en el bloque try-with-resources
  • Java detiene la ejecución en el bloque try-with-resources y llama .close() sobre todos los recursos declarados en try()
  • Uno de los .close() los métodos arrojan una excepción
  • ¿Qué excepción sería la catch bloquear “captura”?

Esta situación nos introduce en las excepciones suprimidas antes mencionadas. Una excepción suprimida es una excepción que de alguna manera se ignora cuando se lanza dentro del bloque final implícito de un bloque try-with-resources, en el caso de que se lanza una excepción desde el try bloque también.

Esas excepciones son excepciones que ocurren en el .close() métodos y se accede a ellos de forma diferente a las excepciones “normales”.

Es importante comprender que el orden de ejecución es:

  • bloque try-with-resources
  • implícito finalmente
  • catch block (si se lanzó una excepción [1] y / o [2])
  • (explícito) finalmente

Por ejemplo, aquí hay un recurso que no hace más que lanzar excepciones:

public static class MyResource implements AutoCloseable {
    // method throws RuntimeException
    public void doSomething() {
        throw new RuntimeException("From the doSomething method");
    }

    // we'll override close so that it throws an exception in the implicit finally block of try-with-resources (when it attempts to close our resource)
    @Override
    public void close() throws Exception {
        throw new ArithmeticException("I can throw whatever I want, you can't stop me.");
    }
}

public static void main(String[] arguments) throws Exception {
    // declare our resource in try
    try (MyResource resource = new MyResource()) {
        resource.doSomething();
    }
    catch (Exception e) {
        System.out.println("Regular exception: " + e.toString());

        // getting the array of suppressed exceptions, and its length
        Throwable[] suppressedExceptions = e.getSuppressed();
        int n = suppressedExceptions.length;

        if (n > 0) {
            System.out.println("We've found " + n + " suppressed exceptions:");
            for (Throwable exception : suppressedExceptions) {
                System.out.println(exception.toString());
            }
        }
    }
}

Este código se puede ejecutar. Puede usarlo para experimentar con el uso de múltiples MyResource objetos o ver lo que sucede cuando try-with-resources no lanza una excepción, pero .close() hace.

Insinuación: De repente, las excepciones lanzadas al cerrar los recursos comienzan a ser importantes.

Es importante tener en cuenta que, en caso de que un recurso arroje una excepción cuando intente cerrarlo, cualquier otro recurso abierto dentro del mismo bloque try-with-resources seguirá cerrado.

Otro hecho a tener en cuenta es que en una situación en la que el try block no lanza una excepción, y donde hay múltiples excepciones lanzadas al intentar .close() los recursos utilizados, el primero La excepción se propagará por la pila de llamadas, mientras que las demás se suprimirán.

Como puede ver en el código, puede obtener la lista de todas las excepciones suprimidas accediendo al Throwable matriz devuelta por Throwable.getSuppressed().

Recuerde, solo se puede lanzar una única excepción dentro del bloque try. Tan pronto como se lanza una excepción, se sale del código del bloque try y Java intenta cerrar los recursos.

Conclusión

try-with-resources debe usarse en lugar del try-catch-finalmente habitual siempre que sea posible. Es fácil olvidarse de cerrar uno de sus recursos después de codificar durante horas u olvidarse de cerrar un recurso que acaba de agregar a su programa después de una explosión aleatoria de inspiración.

El código es más legible, más fácil de cambiar y mantener y, por lo general, más corto.

 

About the author

Ramiro de la Vega

Bienvenido a Pharos.sh

Soy Ramiro de la Vega, Estadounidense con raíces Españolas. Empecé a programar hace casi 20 años cuando era muy jovencito.

Espero que en mi web encuentres la inspiración y ayuda que necesitas para adentrarte en el fantástico mundo de la programación y conseguir tus objetivos por difíciles que sean.

Add comment

Sobre mi

Últimos Post

Etiquetas

Esta web utiliza cookies propias para su correcto funcionamiento. Al hacer clic en el botón Aceptar, aceptas el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Más información
Privacidad