• 10 heures
  • Facile

Ce cours est visible gratuitement en ligne.

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 29/08/2024

Utilisez les flux en Java

Le projet continue de progresser. Tandis que vous avez eu quelques jours de repos, Marc a eu l’occasion de faire progresser le projet. Vous consultez le code pour le récupérer

Vous n’êtes pas déçu par le résultat, Marc sait y faire pour coder un programme de qualité !

Une fois le programme exécuté le résultat est le suivant :

Résultat du programme exécuté
Résultat du programme exécuté

La modélisation est la suivante :

Diagramme montrant des classes, des interfaces et des énumérations avec leurs relations
Modélisation associée

C’est sympa, mais cela manque un peu d’interaction. Marc prévoit la mise en place d’une fonctionnalité où l’utilisateur doit choisir entre afficher les idées de constructions ou afficher le nombre de blocs pour chaque type de bloc.

De plus, il aimerait qu’on puisse écrire dans un fichier le résultat de certaines opérations de l’utilisateur.

Découvrez les flux

Comment faire pour récupérer la saisie d’un utilisateur ? Et je n’ai jamais manipulé de fichiers non plus !

Récupérer une saisie utilisateur ou écrire dans un fichier revient à traiter du thème de l’envoie et la réception de données par notre programme. Pour mener à bien un échange de données il faut une source et un destinataire, notez quelques exemples :

 

Source

Destinataire

Lecture des données d’un fichier

Fichier

Programme

Écriture des données dans un fichier

Programme

Fichier

Lecture des données d’une console

Console

Programme

Écriture des données dans une console

Programme

Console

Un de nos deux objectifs est de récupérer la saisie d’un utilisateur, dans ce cas la console sera la source des données et le programme en sera le destinataire.

Pour l’autre objectif où l’on veut écrire le résultat de certaines opérations sur les blocs de l’utilisateur dans un fichier alors le programme est la source et le fichier le destinataire.

Techniquement pour réaliser ces opérations d'envoi / réception, nous pouvons utiliser les flux en Java. Vous entendrez principalement parler de Streams qui est la terminologie couramment utilisée.

Comme vu précédemment, un programme peut être un destinataire et une source, il existe donc :

  • des flux d’entrées, le programme reçoit une donnée (destinataire).

  • des flux de sorties, le programme envoie une donnée (source).

Comme à son habitude le langage Java va nous offrir des classes pour manipuler ces flux. Le package java.io contient ses différentes classes.

Il y a toute une nomenclature associée aux flux. Tout d’abord on peut diviser les classes permettant de gérer les flux en deux catégories :

  1. Celle pour gérer des flux d’octets.

  2. Celle pour gérer des flux de caractères. 

Concentrons-nous sur les flux de caractères, ces derniers sont divisés en deux catégories :

  1. Les flux de caractères d’écriture : Writer

  2. Les flux de caractères de lecture : Reader

java.io.Reader et java.io.Writer sont deux classes abstraites qui seront implémentées par d’autres classes à utiliser en fonction du cas d’usage.

Pour remplir notre premier objectif, apprenons maintenant à manipuler la console pour savoir récupérer une saisie de l’utilisateur et ainsi lui permettre d’agir sur le comportement du programme.

Manipulez la console

Récupérer la saisie d’un utilisateur nécessite de savoir manipuler la console. Et sachez que vous savez déjà en partie le faire !

Comment ça ?

Et oui, voilà un moment que vous utilisez l’instruction  System.out.println()  pour écrire dans la console. À chaque fois que vous l’avez fait votre programme était la source d’une donnée et la console le destinataire.

Plus concrètement, la classe System possède un attribut static nommé out de type java.io.PrintStream. Il s’agit de la classe pour écrire dans la console avec un flux d’octet et non de caractères. La classe System étant prête à l’emploi je ne peux que vous conseiller de l’utiliser.

Laissez-moi tout de même vous parlez de la classe java.io.PrintWriter qui peut également être utilisée pour écrire dans une console via un flux de caractères et non d’octet. Tout d’abord elle étend la classe abstraite java.io.Writer et regardez le code suivant :

Main.java

public class Main {
    
    public static void main(String[] args) {
        
        PrintWriter writer = new PrintWriter(System.out);
        writer.println("Et un bloc par ci, un bloc par là !");
        writer.flush();
        writer.close();

    }
}

Mais pourquoi la classe PrintWriter prend en paramètre System.out qui est un objet de type PrintStream ?

Car comme dit précédemment un caractère correspond à plusieurs octets (2 en l'occurrence) donc les flux de caractères sont une sorte de surcouche au flux d’octets, plus facilement compréhensible pour un développeur débutant.

Vis-à-vis du code précédent, nous pouvons noter deux points :

  • Pour que le texte soit écrit dans la console il faut appeler la méthode  flush()  sur le flux.

  • Un flux s’ouvre en instanciant la classe mais se ferme également grâce à la méthode  close()  . 

C’est un peu fastidieux, donc rassurez-vous je ne vais pas vous demander d’abandonner System.out.println qui est plus pratique.

Très bien, mais je ne sais toujours pas récupérer la saisie d’un utilisateur, non?

C’est vrai, passons maintenant au flux d’entrée avec la console ! Pour lire dans la console c’est la classe java.io.BufferedReader, qui de son côté étend la classe abstraite java.io.Reader.

Selon le même principe que pour PrintWriter, BufferedReader aura besoin de s’appuyer sur un flux d’octets. Notons le code suivant :

Main.java

public class Main {
    
    public static void main(String[] args) {
        BufferedReader fluxEntree = new BufferedReader(new InputStreamReader(System.in));
        String saisieUtilisateur = fluxEntree.readLine();
        System.out.println(saisieUtilisateur);
        reader.close();
    }
    
}

A noter :

  • Nous construisons le flux d’octets entrants grâce à la classe java.io.InputStreamReader. Le constructeur de cette dernière prend en paramètre System.in qui est une instance de java.io.InputStream.

  • Ensuite le flux d’octets est passé en paramètre du constructeur de java.io.BufferedReader

  • La méthode  readLine()  permet de récupérer un texte saisi dans la console et le renvoie sous forme d’objet java.lang.String que je décide ensuite d’afficher dans la console.

  • Le flux de caractères entrants doit également être fermé avec la méthodeclose  .

La variable saisieUtilisateur peut maintenant être comparée à n’importe quelle valeur de notre choix et ainsi nous pouvons adapter le comportement de notre programme en fonction de la saisie de l’utilisateur.

Et si on applique ça à notre projet Epicrafter’s Journey, nous irions certainement dans la direction suivante :

public static void main(String[] args) throws IOException {
    logger.info("Lancement du programme Epicrafter's Journey.");
    try {
        // Le programme commence avec un Kit de démarrage.
        KitDemarrage kit = new KitDemarrage();
        System.out.println("Vous possédez un kit de démarrage !"); 
        System.out.println(
        "Que souhaitez-vous afficher ?\n\t1 - Les idées de constructions. \n\t2 - Le nombre de blocs pour chaque type de blocs présent dans le kit");
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        String reponse = reader.readLine();
        if (reponse.equals("1")) {
            // code pour afficher les idées de constructions
        } else if(reponse.equals("2")) {
            // code pour affiche le nombre de blocs pour chaque type de blocs présent dans le kit
        } else {
            System.out.println("La valeur saisie n'est pas valide - tapez 1 ou 2.");
        }
    } catch (IllegalBlocException e) {
        System.out.println("Impossible de construire le Kit de démarrage.");
    }
    logger.info("Arret du programme Epicrafter's Journey.");
}

Finalement ce code ne comporte aucune complexité particulière tout en nous permettant d’adapter le comportement de notre programme selon une saisie réalisée par l’utilisateur.

Avant de conclure cette section, sachez que de nombreux développeurs utilisent la classe java.util.Scanner pour lire à partir de la console grâce à ses nombreuses méthodes destinées à faciliter la lecture de la console.  

On enchaîne avec le deuxième objectif fixé par Marc : écrire dans un fichier le résultat de certaines opérations qui ont été réalisées par l’utilisateur.

Manipulez un fichier

Dans cette démonstration, nous allons ajouter une méthode sauvegarder dans la classe KitDemarrage qui aura pour objectif d’écrire le contenu du kit dans un fichier.

Les points à retenir sont les suivants :

  • Pour écrire dans un fichier nous pouvons utiliser la classe java.io.BufferedWriter. Le constructeur de cette dernière a besoin d’une instance de java.io.FileWriter en paramètre.

  • La méthodewrite  permet d’écrire dans le fichier.

  • Par défaut, le fichier est écrit à la racine du dossier du projet.

  • Il ne faut jamais oublier de .close(). Ce qui est ouvert doit être fermé.

Voici le code de la méthode sauvegarder réalisée durant la démonstration :

public void sauvegarder() {
    StringBuilder builder = new StringBuilder();
    builder.append("Kit de démarrage\n");
    for(String motCle : motsCles) {
        builder.append(motCle + " ");
    }
    try {
        BufferedWriter writer = new BufferedWriter(new FileWriter("kit.txt"));
        writer.write(builder.toString());
        writer.close();
    } catch (IOException e) {
        logger.error("Impossible d'écrire dans le fichier");
    }
}

Notre code est composé de deux groupes d’instructions :

  1. La création du texte à écrire dans le fichier grâce à un objet java.lang.StringBuilder : en l'occurrence c’est la liste des mots-clés que l’on souhaite sauvegarder.

  2. L’écriture dans le fichier grâce à l’objet java.io.BufferedWriter et l’appel de la méthode write.

Il nous aura donc suffi de quelques lignes pour réaliser ce qui était attendu par Marc !

Ne nous arrêtons pas en si bon chemin, même si Marc ne nous l’a pas demandé, je vais vous montrer comment lire le contenu d’un fichier. La lecture d’un fichier ressemble à la lecture de la console, notez ce code :

public void charger() {
    try (BufferedReader reader = new BufferedReader(new FileReader("kit.txt"))) {
        String line = null;
        while((line = reader.readLine()) != null) {
            System.out.println(line);
        }
    } catch (FileNotFoundException e) {
        logger.error("Le fichier kit.txt n'existe pas.");
    } catch (IOException e) {
        logger.error("Impossible de lire le fichier kit.txt");
    }
}

Comme lorsque nous avons récupéré la saisie d’un utilisateur dans la console, nous utilisons la classe java.io.BufferedReader et la méthode readLine mais cette fois-ci la boucle while nous permettra de lire toutes les lignes du fichier. La boucle va s’arrêter lorsque la méthode readLine renverra null, ce qui signifie qu’il n’y a plus de lignes à lire.

De plus, j’attire votre attention sur la syntaxe du try. Cette notation est un try-with-resources ! Intégrée à partir de Java 7, cette syntaxe nous assure que le flux sera fermé même si une exception est déclenchée après l’ouverture du flux. Très pratique du coup ! 

En conclusion, non seulement les objectifs fixés par Marc ont été atteints mais en plus vous êtes en mesure de lire le contenu d’un fichier.

À vous de jouer

Contexte

Vous recevez enfin la tâche avec la nouvelle fonctionnalité prévue par Marc :

ID de la tâche : 9

Nom de la tâche : Choix de l’utilisateur lors de l’utilisation du Kit

Description de la tâche : 

Lorsque le programme démarre, un kit de démarrage est fourni à l’utilisateur. Actuellement il est affiché à l’utilisateur tant les idées de constructions que le nombre de blocs de chaque type présent dans le kit.

L’utilisateur devra avoir le choix entre afficher l’une ou l'autre des informations.

Vous vous lancez dans la réalisation de cette tâche !

Consignes

  1. La classe Main devra être modifiée.

  2. Affichez une question “Que souhaitez-vous afficher ? 1 - Les idées de constructions. 2 - Le nombre de blocs pour chaque type de blocs présent dans le kit”.

  3. Utilisez un BufferedReader comme vu dans le chapitre pour lire la valeur qui sera saisie, en l'occurrence 1 ou 2.

  4. Utilisez une structure conditionnelle pour adapter le code en fonction de la valeur obtenue.

En résumé

  • Java possède de nombreuses classes pour gérer les flux entrants et sortants.

  • java.io.BufferedReader permet de lire la saisie d’un utilisateur dans la console console.

  • java.io.BufferedReader permet également de lire le contenu d’un fichier.

  • java.io.BufferedWriter permet d’écrire dans un fichier.

  • java.io.PrintWriter permet d’écrire dans la console bien que nous puissions également le faire avec  System.out.println()

Continuons à explorer les bonnes pratiques professionnelles en allégeant le code grâce au lambda dans le prochain chapitre !

Exemple de certificat de réussite
Exemple de certificat de réussite