Programmation orientée objet et applications

Formation pratique sur Java : notions, méthodes, ateliers et livrables vérifiables.

  1. Public cible

    Débutants, étudiants et professionnels qui souhaitent acquérir une pratique structurée dans ce domaine.

  2. Objectifs

    Comprendre les notions essentielles et les appliquer dans un projet vérifiable.

  3. Prérequis

    Aucune connaissance préalable n’est strictement obligatoire ; les prérequis détaillés sont précisés dans les ateliers.

  4. Niveau

    Tous niveaux

  5. Ressources

    • Documentation officielle et références citées dans les ateliers.
    • Exercices, examens et projet de synthèse du catalogue.
  6. Validation des compétences

    La validation repose sur les évaluations actives et les livrables pratiques du cours.

  7. Besoins techniques

    Un ordinateur, un navigateur et les outils indiqués dans le premier atelier sont suffisants pour commencer.

Exercices, examens et projets

Programmation concurrente

Exercice

Exercice Java

Écrire et tester une classe Java.

Obligatoire Consulter
Examen

Examen Java

Évaluer POO, collections et exceptions.

Obligatoire Consulter
Projet

Projet Java

Construire une application Java maintenable.

Livrables :

  • Code
  • Tests
Obligatoire Consulter

Persistance, Web et Jakarta EE

Exercice

Exercice Java

Écrire et tester une classe Java.

Obligatoire Consulter
Examen

Examen Java

Évaluer POO, collections et exceptions.

Obligatoire Consulter
Projet

Projet Java

Construire une application Java maintenable.

Livrables :

  • Code
  • Tests
Obligatoire Consulter

Projet final et bonnes pratiques

Exercice

Exercice Java

Écrire et tester une classe Java.

Obligatoire Consulter
Examen

Examen Java

Évaluer POO, collections et exceptions.

Obligatoire Consulter
Projet

Projet Java

Construire une application Java maintenable.

Livrables :

  • Code
  • Tests
Obligatoire Consulter

Découvrir Java

Exercice

Exercice Java

Écrire et tester une classe Java.

Obligatoire Consulter
Examen

Examen Java

Évaluer POO, collections et exceptions.

Obligatoire Consulter
Projet

Projet Java

Construire une application Java maintenable.

Livrables :

  • Code
  • Tests
Obligatoire Consulter

Environnement, syntaxe et structures de contrôle

Exercice

Exercice Java

Écrire et tester une classe Java.

Obligatoire Consulter
Examen

Examen Java

Évaluer POO, collections et exceptions.

Obligatoire Consulter
Projet

Projet Java

Construire une application Java maintenable.

Livrables :

  • Code
  • Tests
Obligatoire Consulter

Programmation orientée objet, collections et I/O

Exercice

Exercice Java

Écrire et tester une classe Java.

Obligatoire Consulter
Examen

Examen Java

Évaluer POO, collections et exceptions.

Obligatoire Consulter
Projet

Projet Java

Construire une application Java maintenable.

Livrables :

  • Code
  • Tests
Obligatoire Consulter
  1. Informations Générales

    1. Titre du Cours

      Programmation en Java – De Débutant à Avancé

    2. Durée Totale

      ~120 heures

    3. Format

      Présentiel ou en ligne (selon les besoins)

    4. Public Cible

      • Débutants en Programmation : Personnes souhaitant apprendre les bases de la programmation à partir de zéro.
      • Développeurs Débutants : Ceux qui ont des connaissances de base en programmation mais souhaitent se spécialiser en Java.
      • Étudiants en Informatique : Étudiants souhaitant approfondir leurs compétences en Java.
      • Professionnels en Reconversion : Individus souhaitant changer de carrière et entrer dans le développement logiciel.
    5. Objectifs Pédagogiques

      À l'issue de ce cours, les participants seront capables de :

      • Comprendre les Concepts Fondamentaux de la programmation orientée objet (POO) en Java.
      • Écrire, Compiler et Exécuter des Programmes Java de base et avancés.
      • Utiliser les Structures de Contrôle de Flux (conditions, boucles, gestion des exceptions) efficacement.
      • Maîtriser les Collections et les Génériques pour gérer des données complexes.
      • Développer des Interfaces Graphiques en utilisant Swing et JavaFX.
      • Implémenter la Programmation Concurrente pour créer des applications performantes.
      • Accéder et Manipuler des Bases de Données via JDBC et des frameworks ORM comme Hibernate.
      • Créer des Applications Web en Java en utilisant des servlets, JSP et Spring Framework.
      • Créer des Applications Web en Java en utilisant des servlets, JSP, Spring Framework et Jakarta EE.
      • Utiliser Java EE pour le Développement d'Applications d'Entreprise.
      • Adopter les Bonnes Pratiques de Développement incluant la gestion de projet, les tests unitaires, et le débogage.
      • Réaliser un Projet Final intégrant l’ensemble des compétences acquises durant le cours.
    6. Besoins Techniques

      1. Matériel

        • Ordinateur Personnel :
          • Processeur : Minimum Intel i3 ou équivalent
          • Mémoire RAM : Minimum 8 Go
          • Stockage : Minimum 256 Go SSD
        • Connexion Internet : Stable et rapide (pour les cours en ligne et le téléchargement des outils)
      2. Logiciels

        • Java Development Kit (JDK) : Version la plus récente (recommandée JDK 17 ou supérieure)
        • Environnement de Développement Intégré (IDE) :
          • Eclipse, IntelliJ IDEA, ou NetBeans (choix selon préférence)
      3. Outils Supplémentaires

        • Git (pour la gestion de version)
        • Maven ou Gradle (pour la gestion de projet)
        • Serveur d’applications (Apache Tomcat recommandé pour les modules web)
  2. Modules du cours

    1. Découvrir Java

      1. Historique et Évolution de Java

        1. Objectifs Pédagogiques

          1. Comprendre les origines et les motivations qui ont conduit à la création de Java.
          2. Identifier les étapes clés de l’évolution de Java à travers ses versions.
          3. Apprécier la position actuelle de Java dans l’écosystème technologique.
          4. Reconnaître les concepts fondamentaux qui font de Java un langage polyvalent, sûr et performant.
        2. Origines de Java

          1. Contexte de Création

            1. Lancement du Projet Green (1991) :
              1. Objectif initial : Créer un langage pour les appareils électroniques grand public (télévisions, récepteurs, etc.).
              2. James Gosling, Mike Sheridan, et Patrick Naughton sont les figures clés du projet.
            2. Les Défis de l’Époque :
              1. Besoin d’un langage portable et indépendant des plateformes matérielles.
              2. Complexité croissante des systèmes embarqués nécessitant un langage plus simple et fiable.
        3. Naissance du Langage Java

          1. Prototype original

            Oak : Les origines

            Oak (nommé ainsi en référence à un chêne devant le bureau de James Gosling).

            • Le chêne comme symbole : Lorsque James Gosling et son équipe ont commencé à développer ce qui allait devenir Java, ils ont choisi le nom de code "Oak". Ce choix était une référence à un chêne qui se dressait devant leur bureau. Le chêne, un arbre robuste et à longue durée de vie, représentait bien l'ambition de créer un langage solide et pérenne.
            • Les raisons de ce choix : Le chêne est souvent associé à la force, la stabilité et la croissance. Ces qualités reflétaient les objectifs des développeurs : créer un langage puissant, fiable et évolutif.
          2. De Oak à Java : Un changement de nom

            Renommé Java en 1995, en hommage au café de Java, symbolisant simplicité et puissance.

            • Publication Officielle : Java 1.0 (1995)

              1. Objectif : Fournir une plateforme d’exécution pour le web (avec les applets).
              2. Révolution dans le développement d’applications Internet grâce à sa portabilité via la JVM (Java Virtual Machine).
            • Le café de Java

              En 1995, le projet a été renommé "Java". Ce nouveau nom est un hommage au café de Java, une île indonésienne connue pour la production de café.

            • Pourquoi Java ?

              Le choix du nom "Java" était motivé par plusieurs raisons :

              1. Simplicité et convivialité : Le café est une boisson appréciée pour sa simplicité et son accessibilité. Le nom "Java" évoquait donc un langage facile à apprendre et à utiliser.
              2. Puissance et énergie : Le café est aussi une source d'énergie. Le nom "Java" soulignait ainsi la puissance et les performances du langage.
              3. Originalité : Dans le paysage des langages de programmation de l'époque, le nom "Java" était original et mémorable.
        4. Principes Fondateurs de Java

          1. "Write Once, Run Anywhere" (WORA) :
            • Applications écrites en Java peuvent être exécutées sur n’importe quelle plateforme compatible avec une JVM.
          2. Simplicité et Fiabilité :
            • Suppression des fonctionnalités complexes de C++ comme les pointeurs explicites.
            • Gestion automatique de la mémoire avec le garbage collector.
          3. Sécurité :
            • Protection des applications contre les menaces via la sandbox (bac à sable).
          4. Orienté Objet :
            • Modèle de conception basé sur des objets pour favoriser la réutilisabilité et la modularité du code.
          5. Multi-threading et Réactivité :
            • Conçu dès le départ pour exécuter plusieurs tâches en parallèle.
        5. L’Évolution de Java

          1. Étapes Clés de l’Évolution de Java

            1. Java 1.0 à 1.4 (1995-2002) : Les Fondations

              1. Java 1.0 (1995) : Lancement initial avec des Applets.
              2. Java 2 (1998) : Introduction des éditions SE (Standard Edition), EE (Enterprise Edition), et ME (Micro Edition).
              3. Améliorations majeures : Swing pour l’interface graphique, collections framework.
            2. Java 5 à Java 8 (2004-2014) : Vers la Modernité

              1. Java 5 (2004) : Introduction de fonctionnalités avancées comme les génériques, les énumérations, et les annotations.
              2. Java 6 (2006) : Optimisation des performances et ajout de la prise en charge des web services.
              3. Java 7 (2011) : Ajout des try-with-resources pour une gestion simplifiée des ressources.
              4. Java 8 (2014) : Révolution avec l’introduction des expressions lambda, des Streams, et de l’API java.time.
            3. Java 9 à Java 20 (2017-présent) : Modernisation et Innovation

              1. Java 9 (2017) : Introduction des modules avec le projet Jigsaw.
              2. Java 11 (2018) : Première LTS (Long-Term Support) après Java 8. Introduction des chaînes en var.
              3. Java 17 (2021) : Deuxième LTS post-Java 8, avec des ajouts comme les classes scellées (sealed classes).
              4. Versions Récentes (Java 20) : Introduction de fonctionnalités comme la programmation concurrente structurée, le pattern matching, et l’amélioration des performances de la JVM.
          2. Cycles de Mise à Jour

            1. Depuis 2018, Java adopte un cycle de mise à jour rapide : une nouvelle version tous les 6 mois.
            2. Version LTS : Support à long terme pour les entreprises.
        6. L'Impact de Java Aujourd'hui

          1. Popularité et Utilisation

            1. Large Communauté de Développeurs :
              1. Des millions de développeurs à travers le monde.
              2. Forte adoption dans les grandes entreprises, startups, et projets open-source.
            2. Applications dans Divers Domaines
              1. Applications Web et Entreprises : Utilisation massive avec JEE et frameworks comme Spring, Hibernate.
              2. Développement Mobile : Android repose sur le langage Java.
              3. Applications Desktop : Développement d’applications GUI avec JavaFX et Swing.
              4. IoT (Internet of Things) et systèmes embarqués.
          2. Avantages et Inconvénients

            1. Avantages

              1. Robustesse et fiabilité.
              2. Large écosystème d’outils et de bibliothèques.
              3. Grande portabilité grâce à la JVM.
            2. Inconvénients

              1. Consommation mémoire élevée.
              2. Syntaxe parfois plus lourde comparée à des langages récents comme Kotlin.
          3. Pourquoi Choisir Java ?

            1. Multiplateforme : Fonctionne sur différents systèmes d'exploitation comme Windows, Mac, Linux (y compris les distributions pour Raspberry Pi).
            2. Popularité Mondiale : L'un des langages les plus utilisés dans le monde, avec une forte présence dans l'industrie.
            3. Forte Demande sur le Marché du Travail : Java reste un langage clé recherché par les recruteurs dans de nombreux secteurs.
            4. Facile à Apprendre et Utiliser : Une syntaxe claire qui le rend accessible, même pour les débutants.
            5. Open-Source et Gratuit : Offert librement, ce qui le rend idéal pour les étudiants et les développeurs indépendants.
            6. Sécurisé, Rapide et Puissant : Conçu pour minimiser les vulnérabilités et offrir de bonnes performances.
            7. Support Communautaire Immense : Des millions de développeurs actifs qui partagent des ressources, des projets et des solutions.
            8. Langage Orienté Objet (OOP) :
              1. Fournit une structure claire pour les programmes.
              2. Facilite la réutilisation du code, réduisant ainsi les coûts de développement.
            9. Proche de C++ et C# : Les développeurs familiers avec ces langages peuvent basculer facilement vers Java, et vice versa.
    2. Environnement, syntaxe et structures de contrôle

      1. Présentation des Outils de Développement

        1. Introduction aux outils nécessaires pour développer en Java.
        2. JDK : Installation du Java Development Kit (dernière version stable).
        3. IDE : Présentation et choix parmi IntelliJ IDEA, Eclipse ou Visual Studio Code.
        4. Explication de l'importance d'un IDE dans le processus de développement.
      2. Installation de l'Environnement

        1. Installer le JDK sur différents systèmes d'exploitation (Windows, macOS, Linux).
        2. Télécharger et configurer l’IDE choisi.
      3. Configuration des Variables d'Environnement

        1. Configurer JAVA_HOME et le PATH pour une utilisation correcte des outils Java en ligne de commande.
        2. Vérifier la configuration avec :
          1. java --version
          2. javac --version
      4. Test de l'Installation

        1. Écrire un premier programme Java "Hello World" :
          1. Compilation avec
            javac
            .
          2. Exécution avec
            java
            .
        2. Démonstration des erreurs courantes (ex. : oubli de configuration, erreurs de syntaxe).
    3. Premiers Pas avec Java - Concepts de Base

      1. Objectifs

        1. Comprendre les bases de Java.
        2. Comprendre la structure d'un programme Java simple
        3. Apprendre à écrire, compiler et exécuter un programme Java.
        4. Savoir manipuler des variables et utiliser différents types de données.
        5. Apprendre à gérer les entrées/sorties simples.
        6. Découvrir les opérateurs de base.
      2. Démarrage rapide : Structure d'un programme Java

        1. Anatomie d'un programme Java

          Un programme Java simple suit toujours une structure bien définie.

          Exemple de Code

                                                                  
                                                                      public class HelloWorld 
                                                                      {
                                                                          public static void main(String[] args) 
                                                                          {
                                                                              System.out.println("Bonjour, monde !");
                                                                          }
                                                                      }
                                                                  
                                                              

          Explication des composantes

          1. public class HelloWorld
            1. Chaque fichier Java contient une classe.
            2. Le nom de la classe doit correspondre au nom du fichier (ex. : HelloWorld.java).
          2. public static void main(String[] args)
            1. C'est le point d'entrée du programme. Tout programme Java commence son exécution ici.
            2. public : Accessible depuis d'autres parties du code.
            3. static : Méthode associée à la classe et non à une instance spécifique.
            4. void : Ne renvoie aucune valeur.
            5. String[] args : Un tableau d'arguments passés en ligne de commande (souvent vide pour un programme simple).
          3. System.out.println("Bonjour, monde !") :Affiche du texte dans la console.
        2. Les imports

          Les importations permettent d'utiliser des classes prédéfinies situées dans d'autres packages (bibliothèques).

          Exemple avec la classe Scanner pour la saisie utilisateur

                                                                  
                                                                      import java.util.Scanner; // Importe la classe Scanner
          
                                                                      public class InputExample 
                                                                      {
                                                                          public static void main(String[] args) 
                                                                          {
                                                                              Scanner scanner = new Scanner(System.in); // Crée un Scanner
                                                                              System.out.print("Entrez votre nom : ");
                                                                              String name = scanner.nextLine();
                                                                              System.out.println("Bonjour " + name + " !");
                                                                          }
                                                                      }
                                                                  
                                                              
        3. Cycle de vie d'un programme Java

          1. Écriture : Éditer le code dans un fichier avec l'extension .java.
          2. Compilation : Transformer le code source en bytecode grâce à javac.
            javac HelloWorld.java
          3. Exécution : Exécuter le bytecode avec la machine virtuelle Java (JVM).
            java HelloWorld
      3. Variables et Types de Données

        1. Qu'est-ce qu'une variable ?

          Une variable est une boîte qui stocke des données dans un programme. En Java, chaque variable a un type qui définit la nature des données qu'elle peut contenir.

          1. Types primitifs

            Java propose des types de données prédéfinis :

            1. int : pour les nombres entiers (ex. : 42).
            2. double : pour les nombres décimaux (ex. : 3.14).
            3. char : pour un caractère unique (ex. : 'A').
            4. boolean : pour les valeurs `true` ou `false`.
          2. Exemple de Code

                                                                                
                                                                                    public class VariablesDemo 
                                                                                    {
                                                                                        public static void main(String[] args) 
                                                                                        {
                                                                                            // Déclaration et initialisation de variables
                                                                                            int age = 25;
                                                                                            double pi = 3.14159;
                                                                                            char grade = 'A';
                                                                                            boolean isJavaFun = true;
            
                                                                                            // Affichage des valeurs
                                                                                            System.out.println("Âge : " + age);
                                                                                            System.out.println("Valeur de Pi : " + pi);
                                                                                            System.out.println("Note : " + grade);
                                                                                            System.out.println("Java est amusant : " + isJavaFun);
                                                                                        }
                                                                                    }
                                                                                
                                                                            
      4. Entrées et Sorties Basiques

        1. Sorties : Affichage avec System.out.println

          La méthode System.out.println() est utilisée pour afficher des messages ou des valeurs dans la console.

        2. Entrées : Lecture avec Scanner

          Pour interagir avec l'utilisateur, on utilise la classe Scanner pour lire des données entrées via le clavier.

        3. Exemple de Code

                                                              
                                                                  import java.util.Scanner;
          
                                                                  public class InputOutputDemo 
                                                                  {
                                                                      public static void main(String[] args) 
                                                                      {
                                                                          Scanner scanner = new Scanner(System.in);
          
                                                                          // Lecture d'un nom
                                                                          System.out.print("Entrez votre nom : ");
                                                                          String name = scanner.nextLine();
          
                                                                          // Lecture d'un âge
                                                                          System.out.print("Entrez votre âge : ");
                                                                          int age = scanner.nextInt();
          
                                                                          // Affichage des données
                                                                          System.out.println("Bonjour " + name + ", vous avez " + age + " ans.");
                                                                      }
                                                                  }
          
                                                              
                                                          
      5. Contrôle de Flux

        1. Structures Conditionnelles
        2. Boucles et Itérations
        3. Gestion des Exceptions
        4. Exercices et Projets Miniatures
    4. Programmation orientée objet, collections et I/O

      1. Concepts fondamentaux de la POO

        La programmation orientée objet (POO) repose sur une idée simple : on regroupe des données et les traitements qui les concernent dans une même entité, l'objet. Java est un langage à classes : tout y est un objet, à l'exception des types primitifs.

        Classe, objet, référence

        La classe est le moule (le plan). L'objet est une instance concrète créée à partir de ce plan. On parle de référence pour la variable qui pointe vers l'objet.

        class Voiture {
            private String marque;
            private int vitesse;
        
            public void accelerer(int kmh) {
                this.vitesse += kmh;
            }
        }
        
        Voiture maVoiture = new Voiture();  // création de l'objet
        maVoiture.accelerer(50);             // appel de méthode
        // IMPORTANT : la copie de référence, pas de l'objet
        Voiture copie = maVoiture;           // copie ET ALIAS sur le même objet
        copie.accelerer(50);
        System.out.println(maVoiture.vitesse); // 100 : les deux noms voient le même objet

        L'erreur la plus fréquente des débutants est de croire que copie a créé un second objet. En Java, une variable de type objet contient une référence : affecter une référence en recopie simplement le pointeur. Pour un véritable duplicata, il faut new ou une méthode de copie.

        Les 4 piliers

        1. Abstraction : on ne retient que ce qui est utile (l'interface d'un objet), pas son fonctionnement interne.
        2. Encapsulation : les données sont privées, on y accède par des méthodes qui contrôlent ce qui entre et sort.
        3. Héritage : une classe spécialisée réutilise et spécialise une classe existante.
        4. Polymorphisme : un même appel se comporte différemment selon le type réel de l'objet.

        Encapsulation et bonnes pratiques

        L'encapsulation consiste à rendre les attributs private et à contrôler l'accès par des méthodes. C'est ce qui garantit qu'un objet reste toujours dans un état valide.

        class CompteBancaire {
            // invariant : le solde ne peut pas être négatif
            private double solde;
        
            public CompteBancaire(double soldeInitial) {
                this.solde = soldeInitial;
            }
        
            public boolean deposer(double montant) {
                if (montant <= 0) {
                    return false;   // refus : l'état reste valide
                }
                this.solde += montant;
                return true;
            }
        
            public double getSolde() { return this.solde; }
        }

        Notez que deposer refuse l'opération au lieu de créer un solde négatif : c'est le rôle de l'encapsulation. Un attribut exposé (public double solde) aurait laissé n'importe qui casser cet invariant.

        • this distingue l'attribut de l'instance du paramètre de même nom.
        • Un constructeur initialise, il ne travaille jamais (pas d'affichage, pas d'appel réseau).
        • Getter et setter ne sont pas obligatoires : un objet immuable (final sur les attributs) est souvent préférable.

        Héritage, interfaces et polymorphisme

        extends : réutiliser et spécialiser

        class Animal {
            protected String nom;
            public Animal(String nom) { this.nom = nom; }
            public String presenter() { return "Je suis " + nom; }
        }
        
        class Chien extends Animal {
            public Chien(String nom) { super(nom); }  // appel du constructeur parent
            @Override
            public String presenter() { return super.presenter() + " et j'aboie"; }
        }
        
        Animal a = new Chien("Rex");
        System.out.println(a.presenter());
        // « Je suis Rex et j'aboie » : le type réel (Chien) décide

        La dernière ligne est le polymorphisme : la variable est déclarée Animal mais l'objet est un Chien. C'est ce mécanisme qui rend les programmes extensibles sans modifier le code existant.

        implements : le contrat

        Une interface décrit ce qu'une classe sait faire, sans dire comment. Une classe peut en implémenter plusieurs, l'héritage de classes étant unique en Java.

        interface Deplacable {
            void avancer(int distance);
            default void reculer(int distance) { avancer(-distance); }  // méthode par défaut
        }
        
        class Vo implements Deplacable {
            private int position = 0;
            public void avancer(int distance) { this.position += distance; }
        }
        
        class Robot implements Deplacable {
            private int batterie = 100;
            public void avancer(int distance) { batterie -= Math.abs(distance); }
        }

        Avec cette interface, on peut écrire un code qui manipule les deux sans connaître leur type réel :

        static void deplacerTout(Deplacable[] elements) {
            for (Deplacable e : elements) {
                e.avancer(10);   // chaque objet fait ce qu'il sait faire
            }
        }
        deplacerTout(new Deplacable[]{ new Vo(), new Robot() });

        abstract : le contrat partiel

        Une classe abstraite peut imposer des méthodes sans les implémenter. On ne peut pas l'instancier : elle sert de base commune.

        abstract class Forme {
            public abstract double aire();          // chaque figure calcule la sienne
            public String decrire() {                // méthode commune
                return getClass().getSimpleName() + " : " + aire();
            }
        }
        
        class Cercle extends Forme {
            private final double rayon;
            public Cercle(double rayon) { this.rayon = rayon; }
            public double aire() { return Math.PI * rayon * rayon; }
        }
        
        class Carre extends Forme {
            private final double cote;
            public Carre(double cote) { this.cote = cote; }
            public double aire() { return cote * cote; }
        }

        À retenir : « préferez des interfaces à des classes parentes », principe KISS de Joshua Bloch. Une interface décrit un rôle ; l'héritage de classes décrit « est un ».

        Classes internes et lambdas

        Une classe déclarée dans une autre est une classe interne. Utile pour regrouper une classe utilitaire avec la seule classe qui s'en sert, ou pour créer un objet anonyme sans nommer sa classe.

        class Gestionnaire {
            private int compteur = 0;
        
            // classe interne non statique : accès aux membres de l'instance
            class Compteur {
                void incrementer() { compteur++; }   // accès direct au champ externe
            }
        }

        Depuis Java 8, la forme la plus courante est la classe anonyme, qui n'est qu'une implémentation inline d'une interface ou d'une classe abstraite — typiquement pour un listener :

        bouton.addActionListener(new ActionListener() {   // avant Java 8
            public void actionPerformed(ActionEvent e) {
                System.out.println("clic");
            }
        });
        
        bouton.addActionListener(e -> System.out.println("clic"));  // depuis Java 8

        La seconde forme est une lambda : elle implémente l'interface fonctionnelle ActionListener (une seule méthode abstraite) sans déclarer de classe. C'est le mécanisme utilisé par le module suivant, sur les Stream et la méthode forEach.

        Packages et organisation du code

        Un package regroupe des classes dans un espace de noms. La convention standard est le domaine inversé : com.entreprise.projet.

        // fichier src/com/entreprise/bank/Compte.java
        package com.entreprise.bank;
        
        import java.util.List;
        import java.time.LocalDate;      // import explicite
        
        public class Compte { ... }

        Points de vigilance :

        • le chemin du dossier doit correspondre exactement au package (com/entreprise/bank pour com.entreprise.bank) ;
        • une classe public doit être dans un fichier portant son nom ;
        • on importe les classes, jamais les packages entiers — un import java.util.* masque l'origine des symboles et rend le code illisible.

        Exercices pratiques

        1. Compte bancaire : écrivez une classe Compte avec un solde privé, un dépôt, un retrait qui refuse de passer sous zéro, et une méthode historique() renvoyant la liste des opérations.
        2. Formes géométriques : définissez une interface Deplacable et des classes Cercle, Carre, Rectangle. Écrivez une méthode qui déplace toutes les formes d'un coup, sans connaître leur type.
        3. Correctif : le code suivant perd-il des données ? Corrigez-le.
          class Panier {
              List<Article> articles = new ArrayList<>();
              void copierDans(Panier autre) { autre = this; }   // que fait cette ligne ?
          }
      2. Le Framework de Collections

        Le Collections Framework (java.util) fournit des structures de données prêtes à l'emploi. Avant de l'utiliser, on choisissait entre un tableau ([]) et un ArrayList ; le tableau reste adapté quand la taille est fixe et connue.

        Les trois grandes familles

        1. List — ArrayList (accès par indice, insertion en fin rapide) et LinkedList (insertion rapide en milieu). L'interface expose l'ordre et l'accès par indice.
        2. Set — HashSet sans doublon et sans ordre garanti, TreeSet trié selon l'ordre naturel des éléments.
        3. Map — HashMap associe une clé à une valeur. Une map n'est pas une collection : c'est une structure clé/valeur.
        List<String> villes = new ArrayList<>();
        villes.add("Lyon");
        villes.add("Paris");
        villes.add(1, "Marseille");   // insertion à l'indice 1
        System.out.println(villes.get(0));        // Lyon
        
        Set<String> emails = new HashSet<>();
        emails.add("a@test.fr");
        System.out.println(emails.add("a@test.fr"));  // false : déjà présent
        
        Map<String, Integer> stock = new HashMap<>();
        stock.put("clavier", 12);
        stock.put("souris", 40);
        stock.put("clavier", 8);      // écrase la valeur
        System.out.println(stock.get("clavier"));  // 8
        
        for (Map.Entry<String, Integer> e : stock.entrySet()) {
            System.out.println(e.getKey() + " : " + e.getValue());
        }

        Point d'attention : une HashMap ne garantit pas l'ordre d'itération. Si l'ordre compte, utilisez LinkedHashMap (insertion) ou TreeMap (trié).

        Génériques et sécurité de type

        Les génériques substituent le type à l'intérieur d'une classe ou d'une méthode, et le contrôle est fait à la compilation, pas à l'exécution.

        // AVANT les génériques : on stocke n'importe quoi, plantage à l'exécution
        List liste = new ArrayList();
        liste.add("texte");
        liste.add(42);
        String s = (String) liste.get(1);   // ClassCastException à l'exécution
        
        // AVEC les génériques : erreur détectée par le compilateur
        List<String> liste = new ArrayList<>();
        liste.add(42);        // erreur de compilation

        Le losange <> permet d'omettre le type à droite lorsque le compilateur peut le déduire — il ne faut jamais l'écrire deux fois, ce qui produirait deux types différents.

        On peut aussi paramétrer ses propres méthodes :

        public static <T> T premier(List<T> liste) {
            if (liste.isEmpty()) {
                throw new NoSuchElementException("liste vide");
            }
            return liste.get(0);
        }
        
        String nom = premier(List.of("Alice", "Bob"));

        Itération, foreach et Streams

        La boucle foreach est la façon naturelle de parcourir une collection :

        for (String ville : villes) {
            System.out.println(ville);
        }

        L'itérateur explicite reste utile quand on supprime pendant la boucle :

        Iterator<String> it = villes.iterator();
        while (it.hasNext()) {
            String v = it.next();
            if (v.startsWith("L")) {
                it.remove();   // suppression sûre : pas de ConcurrentModificationException
            }
        }

        Supprimer dans un for (String v : villes) provoque une ConcurrentModificationException : l'itérateur détecte que la collection a changé pendant le parcours.

        Les Streams (Java 8+)

        Un Stream traite une séquence de valeurs sans la modifier. Les opérations se chaînent : source → opérations intermédiaires → opération terminale.

        List<String> villes = List.of("lyon", "paris", "marseille", "lille");
        
        List<String> resultat = villes.stream()
            .filter(v -> v.length() >= 5)          // intermédiaire : garde Marseille
            .map(String::toUpperCase)                   // intermédiaire : transforme
            .sorted()                                   // intermédiaire : trie
            .toList();                                  // terminal : collecte
        
        // équivalent avec Collectors
        String villesLongues = villes.stream()
            .filter(v -> v.length() >= 5)
            .collect(Collectors.joining(", "));
        
        long combien = villes.stream()
            .filter(v -> v.startsWith("l"))
            .count();

        Un Stream est à usage unique : on ne peut pas le parcourir deux fois, et les opérations sont paresseuses — rien n'est calculé avant l'opération terminale (count(), forEach(), toList()…). Les streams parallèles (parallelStream()) ne sont utiles que sur de gros volumes et des traitements coûteux.

        Collections avancées et performance

        Choisir la bonne collection, c'est surtout choisir la bonne complexité :

        • ArrayList : accès par indice en O(1), insertion en fin O(1) amorti, insertion en milieu O(n).
        • LinkedList : insertion/suppression O(1) si la position est connue, mais accès par indice O(n) — à n'utiliser que si l'insertion en milieu est fréquente.
        • HashMap : recherche en O(1) en moyenne, sans ordre.
        • ArrayDeque : file d'attente et pile, plus efficace que Stack ou LinkedList pour cet usage.

        Enfin, Collections.unmodifiableList() renvoie une vue non modifiable : pratique pour protéger une collection exposée par une méthode.

        public List<String> getNoms() {
            return Collections.unmodifiableList(interne);  // l'appelant ne peut plus modifier
        }

        Exercices pratiques

        1. Annuaire : une Map<String, String> associe un nom à un numéro. Ajoutez ajouter, supprimer, chercher, et l'affichage trié par nom.
        2. Déduplication : à partir d'une liste de 200 mots, affichez les 10 plus fréquents, triés par occurrences décroissantes, sans utiliser de HashMap à l'implémentation manuelle.
        3. Refactoriser : réécrivez cette boucle avec un Stream.
          List<String> resultat = new ArrayList<>();
          for (String s : liste) {
              if (s != null && !s.isBlank() && s.length() > 3) {
                  resultat.add(s.toUpperCase().trim());
              }
          }
      3. Les flux d'E/S (Input/Output)

        Toute entrée-sortie passe par un flux (Stream). Un flux est orienté : on lit d'un flux, on écrit dans un flux, mais pas les deux. L'ensemble des hiérarchies est simplifié par deux classes abstraites :

        • InputStream : flux d'octets en entrée (read()).
        • OutputStream : flux d'octets en sortie (write()).

        Pour du texte, on préfère les flux de caractères — ils gèrent l'encodage :

        // flux d'octets : pour une image, un ZIP, tout format binaire
        InputStream octets = Files.newInputStream(Path.of("image.png"));
        
        // flux de caractères : pour du texte
        Reader texte = Files.newBufferedReader(Path.of("data.txt"));

        Les flux doivent toujours être fermés, même en cas d'exception. C'est exactement ce que garantit le try-with-resources :

        // à éviter : si read() lève une exception, le flux reste ouvert
        FileReader fr = new FileReader("config.txt");
        try {
            // ...
        } finally {
            fr.close();
        }
        
        // forme correcte et obligatoire
        try (BufferedReader br = Files.newBufferedReader(Path.of("config.txt"))) {
            String ligne = br.readLine();
            while (ligne != null) {
                System.out.println(ligne);
                ligne = br.readLine();
            }
        }   // fermeture automatique, même en cas d'exception

        Lecture et écriture de fichiers

        Écrire un fichier

        try (BufferedWriter writer = Files.newBufferedWriter(Path.of("notes.txt"))) {
            writer.write("Première ligne");
            writer.newLine();
            writer.write("Deuxième ligne");
        }   // le fichier est créé ou écrasé
        
        // ajouter sans écraser : StandardOpenOption.APPEND
        Files.writeString(Path.of("notes.txt"), "Ajout\n", StandardOpenOption.APPEND);

        Lire un fichier

        // ligne par ligne — la méthode adaptée aux gros fichiers
        try (Stream<String> lignes = Files.lines(Path.of("notes.txt"))) {
            lignes.forEach(System.out::println);
        }
        
        // tout en mémoire — simple, à réserver aux petits fichiers
        String contenu = Files.readString(Path.of("notes.txt"));

        Différence importante : Files.readString() charge tout en mémoire. Sur un fichier de plusieurs gigaoctets, il faut lire ligne par ligne avec Files.lines(), qui reste en flux.

        Les exceptions contrôlées : à comprendre

        Une opération de fichier peut échouer : IOException. Le compilateur oblige donc à la traiter, soit par throws, soit par try/catch. C'est une garantie du langage, pas une nuisance.

        public void copier(Path source, Path cible) throws IOException {
            Files.copy(source, cible, StandardCopyOption.REPLACE_EXISTING);
        }
        
        public void copierSure(Path source, Path cible) {
            try {
                Files.copy(source, cible, StandardCopyOption.REPLACE_EXISTING);
            } catch (IOException e) {
                // on journalise et on ne bloque pas le programme
                System.err.println("Copie impossible : " + e.getMessage());
            }
        }

        Sérialisation d'objets

        La sérialisation transforme un objet en suite d'octets registrable, et l'inverse. Utile pour la persistance ou le transfert entre processus.

        class Employe implements Serializable {
            private final String nom;        // String et les types primitifs sont
            private final double salaire;     // toujours sérialisables
        
            Employe(String nom, double salaire) {
                this.nom = nom;
                this.salaire = salaire;
            }
        }
        
        try (ObjectOutputStream out =
                 new ObjectOutputStream(Files.newOutputStream(Path.of("employe.bin")))) {
            out.writeObject(new Employe("Alice", 4200.0));
        }
        
        try (ObjectInputStream in =
                 new ObjectInputStream(Files.newInputStream(Path.of("employe.bin")))) {
            Employe e = (Employe) in.readObject();
            System.out.println(e.getNom());
        }

        Précaution : un objet lu par désérialisation peut être consécutivement de type Serializable sans garantie de sécurité. Pour des données venues de l'extérieur, on privilégie JSON ou un format explicite plutôt que la sérialisation native.

        L'API NIO.2 et les chemins

        java.nio.file (NIO.2) est l'API moderne de gestion de fichiers : elle manipule des objets Path plutôt que des chaînes, et propose des opérations Enrichies (copier l'arborescence, parcourir un dossier…).

        Path chemin = Path.of("src/main/java");       // construction moderne
        Path absolu = chemin.toAbsolutePath();
        
        // lister un répertoire
        try (Stream<Path> fichiers = Files.list(chemin)) {
            fichiers.filter(p -> p.toString().endsWith(".java")).forEach(System.out::println);
        }
        
        // parcourir une arborescence en profondeur
        try (Stream<Path> arbre = Files.walk(chemin)) {
            long nbJava = arbre.filter(p -> p.toString().endsWith(".java")).count();
            System.out.println(nbJava + " fichiers Java");
        }
        
        // properties : format clé=valeur
        Properties props = new Properties();
        try (Reader r = Files.newBufferedReader(Path.of("config.properties"))) {
            props.load(r);
        }
        System.out.println(props.getProperty("db.url"));

        try-with-resources sur un Stream : un Stream est une ressource comme une autre. Sans le try, un parcours incomplet laisse des descripteurs de fichiers ouverts.

        Exercices pratiques

        1. Annuaire CSV : lisez un fichier contacts.csv (nom, email par ligne) et réécrivez-le trié par nom, en ignorant les lignes mal formées et les doublons.
        2. Compteur de mots : comptez les occurrences de chaque mot d'un fichier texte et écrivez le résultat dans un autre fichier, trié par fréquence décroissante.
        3. Trouver le bug : ce code fuit une ressource. Corrigez-le.
          BufferedReader br = new BufferedReader(new FileReader("data.txt"));
          String l = br.readLine();
          if (l == null) { return; }        // le flux n'est jamais fermé ici
          System.out.println(l);
          br.close();
      4. Introduction aux interfaces graphiques en Java

        Jusqu'ici les programmes affichaient dans la console. Une interface graphique repose sur un modèle simple et stable :

        1. Composants (widgets) : boutons, champs, listes, étiquettes.
        2. Conteneurs : panels et frames qui regroupent et positionnent les composants.
        3. Gestionnaires de mise en page (layout managers) : la position et la taille sont calculées, jamais fixées à la main.
        4. Événements : l'utilisateur agit, un listener exécute le code associé.

        Point essentiel : un composant est affiché uniquement après un appel à setVisible(true), et Swing n'est pas thread-safe — toute modification d'interface doit donc être faite depuis l'Event Dispatch Thread.

        Swing : composants de base et layout

        import javax.swing.*;
        import java.awt.*;
        
        public class Fenetre extends JFrame {
        
            private final JTextField champNom = new JTextField(20);
            private final JLabel resultat = new JLabel(" ");
        
            public Fenetre() {
                super("Mon application");
        
                JButton bouton = new JButton("Valider");
                bouton.addActionListener(e -> valider());   // listener : le clic
        
                JPanel panel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 10));
                panel.add(new JLabel("Nom : "));
                panel.add(champNom);
                panel.add(bouton);
                panel.add(resultat);
        
                setContentPane(panel);            // on remplace le contenu de la fenêtre
                setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                pack();                          // taille ajustée au contenu
                setLocationRelativeTo(null);     // centrée à l'écran
                setVisible(true);
            }
        
            private void valider() {
                String nom = champNom.getText().trim();
                resultat.setText(nom.isEmpty() ? "Veuillez saisir un nom" : "Bonjour, " + nom + " !");
            }
        
            public static void main(String[] args) {
                javax.swing.SwingUtilities.invokeLater(Fenetre::new);
            }
        }

        Trois règles à retenir :

        • pack() plutôt que setSize(...) : la fenêtre s'ajuste à son contenu et suit la police système.
        • SwingUtilities.invokeLater(...) : Swing ne peut être manipulé que depuis l'EDT.
        • on n'écrit jamais la logique métier dans le listener ; on délègue à une méthode testable.

        Les layouts les plus courants

        • FlowLayout : composants côte à côte, passage à la ligne suivante quand la place manque (par défaut).
        • BorderLayout : cinq zones (NORTH, SOUTH, EAST, WEST, CENTER) — idéal pour un menu et un contenu.
        • GridBagLayout : grille avec alignement et étirement fins. Plus verbeux, mais c'est le seul qui permette des interfaces sérieuses.

        JavaFX : approche déclarative

        JavaFX inverse la logique : on décrit l'interface en XML (ou en Java), et la bibliothèque construit la scène. Le code devient plus lisible et l'interface séparée de la logique métier.

        package fr.academy.demo;
        
        import javafx.application.Application;
        import javafx.scene.Scene;
        import javafx.scene.control.*;
        import javafx.scene.layout.VBox;
        import javafx.stage.Stage;
        
        public class ApplicationDemo extends Application {
        
            private final TextField champ = new TextField();
            private final Label resultat = new Label("");
        
            @Override
            public void start(Stage stage) {
                Button bouton = new Button("Valider");
                bouton.setOnAction(e -> {
                    String nom = champ.getText().trim();
                    resultat.setText(nom.isEmpty() ? "Veuillez saisir un nom" : "Bonjour, " + nom + " !");
                });
        
                VBox racine = new VBox(10, new Label("Nom :"), champ, bouton, resultat);
                racine.setPadding(new Insets(20));
        
                stage.setTitle("JavaFX");
                stage.setScene(new Scene(racine, 320, 200));
                stage.show();
            }
        
            public static void main(String[] args) {
                launch(args);
            }
        }

        Quelle technologie choisir ? Swing est intégré au JDK, très répandu et fiable pour des interfaces de bureau classiques ; JavaFX est plus moderne et plus riche, mais demande un module supplémentaire. Pour une interface de bureau simple en formation, Swing reste le choix le plus simple à installer.

        Projet pratique

        Réalisez un convertisseur d'unités (euros ↔ dollars, °C ↔ °F) : un champ de saisie, une liste déroulante, un bouton « Convertir » et un résultat. Contraintes :

        1. la conversion doit être écrite dans une classe séparée Convertisseur, sans aucun code graphique, et être testable ;
        2. une saisie non numérique doit produire un message clair, pas une exception ;
        3. l'interface ne doit pas être redimensionnée de façon cassante.
    5. Programmation concurrente

      1. Introduction à la programmation Concurrente

        1. Définitions et Concepts de Base

          1. Concurrence vs Parallélisme : Comprendre la différence entre exécuter plusieurs tâches simultanément (concurrence) et exécuter plusieurs tâches en même temps sur des processeurs multiples (parallélisme).
          2. Importance de la Concurrence : Amélioration des performances, meilleure utilisation des ressources système, réactivité des applications.
        2. Problèmes Courants en Concurrence

          1. Conditions de Course (Race Conditions)
          2. Interblocages (Deadlocks)
          3. Famine (Starvation)
          4. Incohérence des Données
      2. Les Threads en Java

        1. Concept de Thread

          1. Définition et Rôle des Threads
          2. Lifecycle d’un Thread : États (New, Runnable, Blocked, Waiting, Timed Waiting, Terminated)
        2. Création et Gestion des Threads

          1. Étendre la Classe Thread
            • Exemple pratique : Création d’un thread en héritant de Thread
          2. Implémenter l’Interface Runnable
            • Exemple pratique : Création d’un thread en implémentant Runnable
          3. Utiliser l’Interface Callable et Future
            • Différences entre Runnable et Callable
            • Exemple pratique : Utilisation de Callable pour récupérer des résultats des threads
          4. Méthodes Importantes de la Classe Thread
            • start(), run(), sleep(), join(), interrupt()
      3. Synchronisation et Sécurité des Threads

        1. Problèmes de Synchronisation

          • Incohérence des Données Partagées
          • Conditions de Course
        2. Mécanismes de Synchronisation en Java

          • Le Mot-clé synchronized
          • Synchronisation des méthodes
          • Synchronisation des blocs de code
          • Exemple pratique : Synchronisation d’un compteur partagé
        3. Objets Moniteurs

          • Utilisation des moniteurs pour la synchronisation
        4. Verrous Explicites avec Lock Interface

          1. Introduction à ReentrantLock
          2. Exemple pratique : Utilisation de ReentrantLock pour une synchronisation fine
        5. Éviter les Problèmes de Concurrence

          1. Stratégies pour Prévenir les Deadlocks

            1. Ordonnancement des ressources
            2. Utilisation de timeout
            3. Techniques pour Minimiser la Starvation
              1. Priorités des threads
              2. Utilisation équitable des ressources
      4. API de Concurrence Avancée
      5. Exercices Pratiques
    6. Persistance, Web et Jakarta EE

      1. Introduction à JDBC (Java Database Connectivity)
      2. Gestion des Transactions
      3. ORM avec Hibernate
      4. JPA (Java Persistence API)
      5. Projets de Base de Données
    7. Développement Web avec Java

      1. Introduction aux Applications Web en Java
      2. Servlets
      3. JSP (JavaServer Pages)
      4. Frameworks Web (Spring Framework)
      5. Sécurité des Applications Web
      6. Projet Web
    8. Java EE (Jakarta EE)

      1. Introduction à Java EE (Jakarta EE)

        1. Compréhension de l'architecture Java EE
        2. Différences entre Java SE et Java EE
      2. Composants Java EE

        1. EJB (Enterprise JavaBeans)
          1. Types d'EJB : Session Beans, Message-Driven Beans
          2. Cycle de vie des EJB
        2. JPA (Java Persistence API) Avancé
        3. JAX-RS et JAX-WS (Services Web RESTful et SOAP)
      3. Contextes et Dépendances (CDI)

        1. Injection de dépendances
        2. Gestion des cycles de vie des beans
      4. Gestion des Transactions et Sécurité

        1. Contrôle des transactions
        2. Sécurité déclarative et programmatique
      5. Développement d'Applications d'Entreprise

        1. Création d'applications multi-couches
        2. Utilisation de serveurs d'applications Java EE (WildFly, GlassFish)
      6. Frameworks Java EE

        1. Intégration avec Spring EE
        2. Utilisation de JSF (JavaServer Faces) pour les interfaces utilisateur
      7. Déploiement et Configuration

        1. Déploiement sur un serveur d'applications
        2. Configuration des ressources (datasources, JNDI)
    9. Projet final et bonnes pratiques

      Bonnes pratiques et outils de développement

      1. Gestion de projet avec Maven et Gradle
      2. Tests unitaires avec JUnit
      3. Débogage et profilage
      4. Documentation et standards de code
      5. Exercices et études de cas

      Projet final

      1. Description du projet
        • Choix entre une application desktop, une application web, ou un service REST
      2. Étapes du projet
        1. Analyse des besoins
        2. Conception (UML, diagrammes de classe)
        3. Développement et implémentation
        4. Tests et validation
        5. Présentation finale
      3. Encadrement et support
        1. Séances de mentorat
        2. Feedback continu
      4. Évaluation
        1. Qualité du code
        2. Fonctionnalité de l’application
        3. Documentation et présentation
  3. Évaluation et Méthodes d’Apprentissage

    1. Méthodes d’Évaluation

      • Quizz Hebdomadaires : Pour évaluer la compréhension des concepts
      • Travaux Pratiques : Exercices individuels et en groupe
      • Projets Intermédiaires : Mise en pratique des modules
      • Projet Final : Application intégrative des compétences acquises
      • Participation et Engagement : Contribution aux discussions et activités
    2. Méthodes d’Apprentissage

      1. Cours Théoriques : Présentations interactives
      2. Démonstrations en Direct : Développement de code en temps réel
      3. Travaux Pratiques : Exercices dirigés et libres
      4. Études de Cas : Analyse de projets réels
      5. Sessions de Q&A : Questions et réponses pour clarifier les doutes
  4. Ressources et Matériel Pédagogique

  5. Méthodologie d’Enseignement

    • Approche Interactive : Encouragement des questions et des discussions
    • Apprentissage Actif : Participation aux exercices pratiques et projets
    • Feedback Continu : Retour régulier sur les performances et les progrès
    • Support Personnalisé : Disponibilité pour l’aide individuelle en dehors des sessions
Sources et documentation de reference

Publié le 25/09/2026