Java 7: Statement try-with-resources

DOMANDA:

Che cos'è lo Statement try-with-resources introdotto con il rilascio di Java 7?



RISPOSTA:

Una delle problematiche più comuni ai programmatori è la sistematica chiusura delle risorse e/o connessioni aperte. Capita spesso, infatti, che per dimenticanza o meglio per errori e/o eccezioni impreviste non gestite al meglio, alcune risorse rimangano in attesa di una chiusura che non arriverà mai.

Per prevenire questi memory leaks da Java 7 è stato introdotto lo statement try-with-resources che si occupa di chiudere automaticamente le risorse aperte.

Guardiamo come funziona:

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

La novità è che c'è un blocco try (in questo caso senza catch e finally) con uno o più argomenti tra parentesi. Questi parametri rappresentano un oggetto risorsa che Java garantirà di chiudere al termine del blocco stesso. In questo modo la responsabilità di chiudere la risorsa passa dal programmatore ad una totale gestione automatizzata da parte della Java Virtual Machine.

Nell'esempio sopra abbiamo un BufferedReader ed è possibile specificarlo come parametro del try-with-resources perché da Java 7 in poi questa classe (come tante altre che trovate qui e qui) implementano l'interfaccia java.lang.AutoCloseable e java.io.Closable.


Diamo uno sguardo a come avremmo dovuto scrivere il codice prima di Java 7:

public String leggiRigaDaFileConFinally(String path)
                                                     throws IOException {
    BufferedReader br = new BufferedReader(new FileReader(path));
    try {
        return br.readLine();
    } finally {
        if (br != null) br.close();
    }
}

Notiamo che il codice scritto in Java 7 è più sintetico e pulito.

Dobbiamo però informare i lettori che con il blocco try-with-resources è stato introdotta una nuova gestione delle eccezioni qualora venissero lanciate sia quelle del blocco try, sia quelle di chiusura automatica (normalmente gestibili nel finally con un nuovo blocco try/catch). Per approfondimenti di questa tematica vi rimando al post sugli AutoCloseable Objects e sulle SuppressedExceptions.


RISORSE MULTIPLE

E' utile sapere che è possibile inserire anche più risorse nel try-with-resources separandole con punti e virgola come in questo esempio:

try (
       ZipFile zf = new ZipFile(zipFileName);
       BufferedWriter writer = new BufferedWriter(outputFilePath, charset)
    ) {
 ...
}

Come è logico aspettarsi, il try-with-resources con risorse multiple, chiude le stesse in ordine inverso. Nell'esempio sopra, quindi, chiuderà prima il BufferedWriter e successivamente lo ZipFile.



Il try-with-resources può avere i blocchi catch e finally come il classico try. E' comunque importante sapere che nel try-with-resources sia il catch che il finally è eseguito dopo che la risorsa è stata chiusa.

Guardiamo l'esempio:

public static void viewTable(Connection con) throws SQLException {

    String query = "select id, nome, cognome, indirizzo from anagrafica";

    try (Statement stmt = con.createStatement()) {
        ResultSet rs = stmt.executeQuery(query);

        while (rs.next()) {
            int idAnagrafica = rs.getInt("id");
            String nome = rs.getString("nome");
            String price = rs.getString("cognome");
            String sales = rs.getString("indirizzo");

            System.out.println(id + "," + nome + ", " + cognome + ", " +
                               indirizzo);
        }
    } catch (SQLException e) {
        e.printStackTrace();
    }
}


Concludiamo il post di oggi con un esempio per testare le potenzialità di questo nuovo statement Java 7:

package trywithresources;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class TestTryWithResources {
   public static void main(String[] args) throws IOException {

      try (
           BufferedReader br =
           new BufferedReader(new InputStreamReader(System.in))
      ) {

         System.out.print("INSERISCI UN NUMERO: ");
         System.out.println("HAI INSERITO: " +Integer.parseInt(br.readLine()));

      } catch(NumberFormatException ex) {
         ex.printStackTrace();
         System.out.println("HAI INSERITO DELLE LETTERE AL POSTO DEI NUMERI");
      }
   }
}

Degna di nota è l'innesto di due risorse "autochiudenti" come in questo esempio. Il try-with-resource chiuderà in automatico solo le risorse dichiarate all'interno delle parentesi tonde (quindi nel nostro esempio non si occuperà della chiusura automatica di InputStreamReader ma solo del BufferedReader).

Come già consigliato durante il post, se siete interessati agli approfondimenti, il post sulla creazione di oggetti "autochiudenti" e sulla gestione delle eccezioni soppresse vi saranno di aiuto.


Commenti

Post popolari in questo blog

Arrotondamento e troncamento in Java

Eclipse: Shortcuts (scorciatoie) da tastiera

Strutture dati: List, Set, Map

Creare un eseguibile Java