Participer au site avec un Tip
Rechercher
 

Améliorations / Corrections

Vous avez des améliorations (ou des corrections) à proposer pour ce document : je vous remerçie par avance de m'en faire part, cela m'aide à améliorer le site.

Emplacement :

Description des améliorations :

Comparaison entre try/finally et le try-with-resources

Mise en oeuvre d'une classe d'exception Aspects complémentaires du traitement d'exceptions



Accès rapide :
La vidéo
La syntaxe du « try-with-resources »
Comparaison entre try/finally et le try-with-resources
Utilisation conjointe d'un « try-with-resources » et de l'inférence de types pour les variables locales

La vidéo

Il est courant en Java de chercher à libérer des ressources à la fin de l'exécution d'un bloc try. Le langage Java permet cela depuis le début de son histoire via le bloc finally. Depuis Java SE 7.0, une nouvelle construction, le try-with-resources, vient compléter nos possibilités à ce sujet. Cette vidéo compare ces deux approches, exemples à l'appui.


Mise en oeuvre d'une classe d'exception

La syntaxe du « try-with-resources »

Le « try-with-resources », syntaxe ajoutée à Java SE à partir de sa version 7.0, complète les possibilités de l'instruction try. Cette variante le l'instruction try est en lien direct avec l'interface java.lang.AutoCloseable, elle aussi ajoutée à partir de la version 7.0 du langage.

Cette interface ne définit qu'une seule méthode abstraite : la méthode close. Ainsi, tout type de données concret implémentant cette interface proposera une méthode close. C'est parfait, c'est ce qui garantira à l'instruction « try-with-resources » qu'elle pourra forcer la fermeture de la ressource. Du coup, si vous cherchez à utiliser cette instruction sur un type n'implémentant pas l'interface attendue, une erreur de compilation sera produite. Voici un exemple de code produisant cette erreur de compilation.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
public class BadSyntax {

    public static void main( String[] args ) {
        
        // Cet exemple ne marche pas, car String n'implémente pas l'interface AutoCloseable.
        try ( String demo = "Hello" ) {
            System.out.println( demo );
        }
        
    }
    
}
Exemple de mauvaise utilisation du « try-with-resources », produisant une erreur de compilation

Et voici l'erreur produite par le compilateur.

$> javac BadSyntax.java
BadSyntax.java:7: error: incompatible types: try-with-resources not applicable to variable type
        try ( String demo = "Hello" ) {
                     ^
$>

Pour ce qui est de la syntaxe, comme vous l'avez constaté, on doit positionner les ressources à libérer entre parenthèses et directement à la suite du mot clé try (et donc avant l'accolade ouvrante). Comme le nom de la construction l'indique (« try-with-resources »), on peut avoir plusieurs ressources gérées par le try. Dans ce cas, séparer les différentes ressources via des caractères ;. Notez qu'on peut désormais avoir un bloc « try-with-resources » sans bloc catch et sans bloc finally. Voici trois exemples de syntaxes.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
// Une seule ressource automatiquement gérée
try ( FileInputStream fis = new FileInputStream( "inputFile.txt" ) ) {

    // Ici on manipule de fichier

} // Appel à la méthode close automatique


// Deux ressources automatiquement gérées
try ( FileInputStream fis = new FileInputStream( "inputFile.txt" ) ;
      FileOutputStream fos = new FileOutputStream( "outputFile.txt" ) ) {

    // Ici on manipule nos deux fichiers (une copie de fichier, par exemple)

} // Appels aux méthodes close automatique


// Deux ressources automatiquement gérées, avec un bloc catch pour gérer les erreurs.
try ( FileInputStream fis = new FileInputStream( "inputFile.txt" ) ;
      FileOutputStream fos = new FileOutputStream( "outputFile.txt" ) ) {

    // Ici on manipule nos deux fichiers (une copie de fichier, par exemple)

} catch ( Exception exception ) {
    exception.printStackTrace();
}
Quelques exemples de syntaxe

Avec l'arrivée du Java SE 9.0, il est maintenant possible de déclarer une variable correspondant à une ressource à libérer automatiquement en dehors du bloc try. Voici un exemple d'utilisation.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
import java.io.FileInputStream;

public class BadSyntax {

    public static void main( String[] args ) throws Exception {
        
        FileInputStream fis = new FileInputStream( "inputFile.txt" );
        // Une seule ressource automatiquement gérée
        try ( fis ) {      // Uniquement si Java SE >= 9.0

            // Ici on manipule de fichier

        } // Appel à la méthode close automatique
        
    }
}
Quelques exemples de syntaxe
si vous utilisez un JDK antérieur à la version 9.0, une erreur de compilation sera produite par cet exemple !!!

Comparaison entre try/finally et le try-with-resources

Vous devriez commencer à vous dire que l'instruction « try-with-resources » est plus simple à utiliser qu'une instruction try / catch / finally traditionnelle. Si tel est le cas, sachez que vous avez effectivement raison. Afin de bien comprendre ce point, je vous propose de comparer deux programmes fonctionnellement identiques : à savoir la copie de fichier déjà présentée dans le chapitre précédent. Voici la version utilisant une instruction try traditionnelle.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
 28 
 29 
 30 
 31 
 32 
 33 
 34 
 35 
 36 
 37 
 38 
 39 
 40 
 41 
 42 
 43 
 44 
 45 
 46 
 47 
 48 
 49 
 50 
 51 
 52 
 53 
 54 
 55 
 56 
 57 
 58 
 59 
 60 
 61 
 62 
 63 
 64 
 65 
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Copy {
    
    public static void main( String [] args ) {
        
        // Si vous ne fournissez pas deux paramètres au main,
        // on affiche l'usage de notre commande.
        if ( args.length == 0 ) {
            System.out.println( "Usage: java Copy sourceFile destFile" );
            System.exit( 0 );
        }
        
        // On calcule le nombre total d'octets du fichier source.
        long length = new File( args[0] ).length();

        // On prépare trois variables qui correspondent au fichier source,
        // au fichier de destination ainsi qu'un buffer d'octets.
        FileInputStream inputStream = null;
        FileOutputStream outputStream = null;
        byte [] buffer = new byte[ 1024 * 1024 ];
        
        try {
        
            // On ouvre les deux fichiers (en lecture et en écriture)
            inputStream = new FileInputStream( args[0] );
            outputStream = new FileOutputStream( args[1] );
        
            // On recopie les octets du fichier, tant qu'il y en a.
            while ( length > 0 ) {
                int readedBytes = inputStream.read( buffer );
                outputStream.write( buffer, 0, readedBytes );
                length -= readedBytes;
            }
        
            System.out.println( "Copie du fichier terminée" );
            
        } catch( IOException exception ) { 
            
            // Il y a donc eu une erreur durant la copie des fichiers
            System.err.println( "Impossible de réaliser la copie du fichier" );
            
        } catch( SecurityException exception ) { 
            
            // On a un problème de droits sur les fichiers (java.lang.SecurityException)
            System.err.println( "Vous n'avez pas les droits pour réaliser la copie du fichier" );
        
        } finally {
            
            // On ferme les fichiers.
            if ( inputStream != null ) {
                try { inputStream.close(); } catch( Exception e ) { /* Tant pis */ }
            }
            if ( outputStream != null ) {
                try { outputStream.close(); } catch( Exception e ) { /* Tant pis */ }
            }
            
        }
        
    }
    
}
Une copie de fichier utilisant une instruction try/catch/finally

Et maintenant voici le même programme avec une instruction « try-with-resources ».

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
 28 
 29 
 30 
 31 
 32 
 33 
 34 
 35 
 36 
 37 
 38 
 39 
 40 
 41 
 42 
 43 
 44 
 45 
 46 
 47 
 48 
 49 
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Copy {
    
    public static void main( String [] args ) {
        
        // Si vous ne fournissez pas deux paramètres au main,
        // on affiche l'usage de notre commande.
        if ( args.length == 0 ) {
            System.out.println( "Usage: java Copy sourceFile destFile" );
            System.exit( 0 );
        }
        
        // On calcule le nombre total d'octets du fichier source.
        long length = new File( args[0] ).length();

        byte [] buffer = new byte[ 1024 * 1024 ];

        // On ouvre les deux fichiers (en lecture et en écriture)
        try ( FileInputStream inputStream = new FileInputStream( args[0] ) ;
              FileOutputStream outputStream = new FileOutputStream( args[1] ) ) {
        
            // On recopie les octets du fichier, tant qu'il y en a.
            while ( length > 0 ) {
                int readedBytes = inputStream.read( buffer );
                outputStream.write( buffer, 0, readedBytes );
                length -= readedBytes;
            }
        
            System.out.println( "Copie du fichier terminée" );
            
        } catch( IOException exception ) { 
            
            // Il y a donc eu une erreur durant la copie des fichiers
            System.err.println( "Impossible de réaliser la copie du fichier" );
            
        } catch( SecurityException exception ) { 
            
            // On a un problème de droits sur les fichiers (java.lang.SecurityException)
            System.err.println( "Vous n'avez pas les droits pour réaliser la copie du fichier" );
        
        }
        
    }
    
}
Une copie de fichier utilisant une instruction try-with-ressources

Regardez déjà les nombres de lignes de codes utilisés par les deux exemples. Le second code est presque 1/3 plus court que le premier. Cela est notamment dû au fait qu'avec la seconde approche les blocs catch de l'instruction try servent aussi à l'exécution des deux appels aux méthodes close, contrairement à la première version. Notez aussi qu'il n'est plus nécessaire de déclarer les deux variables de flux (fis et fos) en dehors du bloc try, ce qui permet de mieux contôler leurs portées.

Utilisation conjointe d'un « try-with-resources » et de l'inférence de types pour les variables locales

Nous en avons déjà parlé dans le chapitre relatif à l'inférence de types pour les variables locales, mais Java SE 10 propose une manière de simplifier la déclaration de vos variables locales. Les ressources gérées par le « try-with-resources » étant aussi gérées par des variables locales, il est possible d'y utiliser le mot clé var. Voici un exemple de code appliqué à une exécution de code en base de données.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
try ( var connection = DriverManager.getConnection() ) {
    String strSql = "SELECT * FROM Table WHERE pk=?";
    try ( var statement = connection.prepareStatement( strSql ) {
        statement.setInt( 1, 55 );
        try ( var resultSet = statement.executeQuery() ) {
            // TODO: finir le code
        }
    }
}
Exemple d'utilisation de l'inférence de type avec la gestion de ressources du bloc try
cet extrait de code s'appuie sur l'API JDBC (Java DataBase Connectivity) permettant de se connecter à une base de données de type SQL. Si vous ne connaissez pas encore cette librairie, rien de grave, nous y reviendrons ultérieurement. Pour l'heure comprenez juste que les types des trois variables (connection, statement et resultSet) sont déduits des appels de méthodes associées.


Mise en oeuvre d'une classe d'exception Aspects complémentaires du traitement d'exceptions