Java Language La declaración de prueba con recursos


Ejemplo

Java SE 7

Como lo ilustra el ejemplo de la sentencia try-catch-final , la limpieza de recursos usando una cláusula finally requiere una cantidad significativa de código de "placa de caldera" para implementar los casos de borde correctamente. Java 7 proporciona una forma mucho más sencilla de resolver este problema en la forma de la declaración try-with-resources .

¿Qué es un recurso?

Java 7 introdujo la interfaz java.lang.AutoCloseable para permitir que las clases se administren usando la declaración try-with-resources . Las instancias de clases que implementan AutoCloseable se conocen como recursos . Por lo general, estos deben eliminarse de manera oportuna en lugar de confiar en el recolector de basura para eliminarlos.

AutoCloseable interfaz AutoCloseable define un solo método:

public void close() throws Exception

Un método close() debe disponer del recurso de una manera apropiada. La especificación establece que debería ser seguro llamar al método en un recurso que ya se ha eliminado. Además, se recomienda encarecidamente a las clases que implementan el Autocloseable que declaren el método close() para lanzar una excepción más específica que la Exception , o ninguna excepción en absoluto.

Una amplia gama de clases e interfaces Java estándar implementan AutoCloseable . Éstos incluyen:

  • InputStream , OutputStream y sus subclases
  • Reader , Writer y sus subclases.
  • Socket y ServerSocket y sus subclases
  • Channel y sus subclases, y
  • Las interfaces JDBC Connection , Statement y ResultSet y sus subclases.

La aplicación y las clases de terceros pueden hacer esto también.

La declaración básica de prueba con recursos.

La sintaxis de un try-with-resources se basa en las formas clásicas try-catch , try-finally y try-catch-finally . Aquí hay un ejemplo de una forma "básica"; Es decir, la forma sin catch o finally .

try (PrintStream stream = new PrintStream("hello.txt")) {
    stream.println("Hello world!");
}

Los recursos a administrar se declaran como variables en la sección (...) después de la cláusula try . En el ejemplo anterior, declaramos un stream variable de recurso y lo inicializamos a un PrintStream recién creado.

Una vez que las variables de recursos se han inicializado, se ejecuta el bloque try . Cuando esto se complete, se stream.close() automáticamente para garantizar que el recurso no se escape. Tenga en cuenta que la llamada close() ocurre sin importar cómo se complete el bloque.

Las declaraciones mejoradas de prueba con recursos

La instrucción try-with-resources puede mejorarse con catch bloqueos catch y finally , como con la sintaxis try-catch-finally pre-Java 7. El siguiente fragmento de código agrega un bloque catch a nuestro anterior para tratar con la FileNotFoundException que el constructor PrintStream puede lanzar:

try (PrintStream stream = new PrintStream("hello.txt")) {
    stream.println("Hello world!");
} catch (FileNotFoundException ex) {
    System.err.println("Cannot open the file");
} finally {
    System.err.println("All done");
}

Si la inicialización del recurso o el bloque try lanzan la excepción, entonces se ejecutará el bloque catch . El bloque finally siempre se ejecutará, como con una sentencia convencional try-catch-finally .

Hay un par de cosas a tener en cuenta, sin embargo:

  • La variable de recurso está fuera de alcance en los bloqueos de catch y finally .
  • La limpieza de recursos se realizará antes de que la declaración intente coincidir con el bloque catch .
  • Si la limpieza automática de recursos arrojó una excepción, entonces podría quedar atrapado en uno de los bloques de catch .

Gestionando múltiples recursos

Los fragmentos de código anteriores muestran un solo recurso que se está administrando. De hecho, try-with-resources puede administrar múltiples recursos en una sola declaración. Por ejemplo:

try (InputStream is = new FileInputStream(file1);
     OutputStream os = new FileOutputStream(file2)) {
    // Copy 'is' to 'os'
}

Esto se comporta como cabría esperar. Ambos is y os se cierran automáticamente al final del bloque try . Hay un par de puntos a tener en cuenta:

  • Las inicializaciones se producen en el orden del código, y los inicializadores de variables de recursos posteriores pueden utilizar los valores de los anteriores.
  • Todas las variables de recursos que se inicializaron correctamente se limpiarán.
  • Las variables de recursos se limpian en orden inverso a sus declaraciones.

Por lo tanto, en el ejemplo anterior, is inicializa antes del sistema os y se limpia después de él, y is limpiará si hay una excepción al inicializar el sistema os .

Equivalencia de try-with-resource y clásico try-catch-finally

La especificación del lenguaje Java especifica el comportamiento de los formularios de prueba con recursos en términos de la declaración clásica de prueba-captura- final. (Consulte los detalles completos en el JLS.)

Por ejemplo, este intento básico con el recurso :

try (PrintStream stream = new PrintStream("hello.txt")) {
    stream.println("Hello world!");
}

se define como equivalente a este try-catch-finally :

// Note that the constructor is not part of the try-catch statement
PrintStream stream = new PrintStream("hello.txt");

// This variable is used to keep track of the primary exception thrown
// in the try statement. If an exception is thrown in the try block,
// any exception thrown by AutoCloseable.close() will be suppressed.
Throwable primaryException = null;

// The actual try block
try {
    stream.println("Hello world!");
} catch (Throwable t) {
    // If an exception is thrown, remember it for the finally block
    primaryException = t;
    throw t;
} finally {
    if (primaryException == null) {
        // If no exception was thrown so far, exceptions thrown in close() will
        // not be caught and therefore be passed on to the enclosing code.
        stream.close();
    } else {
        // If an exception has already been thrown, any exception thrown in
        // close() will be suppressed as it is likely to be related to the
        // previous exception. The suppressed exception can be retrieved
        // using primaryException.getSuppressed().
        try {
            stream.close();
        } catch (Throwable suppressedException) {
            primaryException.addSuppressed(suppressedException);
        }
    }
}

(El JLS especifica que las variables t y primaryException reales serán invisibles para el código Java normal).

La forma mejorada de try-with-resources se especifica como una equivalencia con la forma básica. Por ejemplo:

try (PrintStream stream = new PrintStream(fileName)) {
    stream.println("Hello world!");
} catch (NullPointerException ex) {
    System.err.println("Null filename");
} finally {
    System.err.println("All done");    
}

es equivalente a:

try {
    try (PrintStream stream = new PrintStream(fileName)) {
        stream.println("Hello world!");
    }
} catch (NullPointerException ex) {
    System.err.println("Null filename");
} finally {
    System.err.println("All done");    
}