MAÎTRISER
JAVA
COMME UN
PRO
DEVENEZ 10X PLUS
EFFICACE EN JAVA
PHILÉMON GLOBLÉHI
Java L'Essentiel:
Maîtriser la POO et la
programmation
fonctionnelle
Philémon Globléhi
Développeur Java
Version 1.0.0
Dernière mise à jour : 31/10/2025
Maîtriser Java : Du code à l'objet
Java, ce n'est pas juste un langage - c'est une manière de penser la programmation. Dans cet
ebook, on va démystifier ensemble les concepts qui font de Java un incontournable, de la simple
variable jusqu'aux architectures les plus élégantes.
Que tu débutes ou que tu veuilles consolider tes bases, on va explorer Java avec un objectif
simple : comprendre le pourquoi avant le comment.
Ce qu'on va explorer ensemble
Les fondations
● Pourquoi Java ? (et comment il s'est imposé)...........................................................................03
● Préparer ton environnement de travail…………………………………………………………………………..09
● Du code source au programme : comprendre la compilation………………………………………..11
● La grammaire de Java : ce qui rend ton code lisible………………………………………………………17
Construire avec les objets
● Ta première classe (et pourquoi c'est révolutionnaire).......................................................25
● Les types primitifs : les briques de base………………………………………………………………………33
● Manipuler les données : opérateurs et expressions…………………………………………………..39
● Diriger le flux : conditions et boucles…………………………………………………………………………..45
● Les tableaux : stocker et organiser………………………………………………………………………………52
La programmation orientée objet
● Attributs et méthodes : donner vie aux objets…………………………………………………………..58
● Naissance et mort d'un objet : le cycle de vie……………………………………………………………68
● Ranger ton code : les packages………………………………………………………………………………….76
● Héritage vs Composition : quand utiliser quoi ?.................................................................83
● Le polymorphisme démystifié……………………………………………………………………………………..91
● Classes abstraites : l'art de l'incomplétude……………………………………………………………….99
● Object : la mère de toutes les classes……………………………………………………………………….106
● String : bien plus qu'un simple texte…………………………………………………………………………..111
● Gérer l'imprévu : les exceptions…………………………………………………………………………………118
● Les énumérations : quand les constantes deviennent intelligentes……………………….127
Philémon GLOBLEHI, Développeur Java 1
● Les interfaces : des contrats en code……………………………………………………………….135
Aller plus loin
● Génériques : écrire du code réutilisable……………………………………………………………144
● Collections : gérer tes données comme un pro………………………………………………..152
● Lire et écrire : les entrées/sorties………………………………………………………………………160
● Manipuler le temps : les dates en Java……………………………………………………………..167
● Penser fonctionnel : lambdas…………………………………………………………………………….173
● Les streams : traiter les données en flux………………………………………………………….182
● Les classes internes : déclarer des classes dans des classes……………………….192
● Les nouveautés de Java 11 à Java 25………………………………………………………………202
Philémon GLOBLEHI, Développeur Java 2
Pourquoi Java ? (et comment il s’est imposé)
D'où vient Java ?
Java a vu le jour chez Sun Microsystems avant qu'Oracle ne reprenne les rênes en 2010. L'idée ?
Créer un langage qui marche partout, peu importe l'ordinateur sur lequel il tourne, tout en
profitant des avantages de la programmation orientée objet.
Comment ça fonctionne concrètement ?
Imaginez que vous écrivez une lettre, mais au lieu de l'envoyer directement, vous la traduisez
d'abord dans une langue intermédiaire que tout le monde peut comprendre. C'est exactement
ça, Java.
Quand vous écrivez du code Java, le compilateur le transforme en bytecode - pensez-y comme
un langage universel. Ensuite, la JVM (Java Virtual Machine) joue le rôle d'interprète : elle lit ce
bytecode et le traduit dans la langue que votre ordinateur comprend, au moment où vous lancez
le programme.
Ce qu'il faut retenir : Pour faire tourner un programme Java, vous avez besoin de deux choses :
● Le code compilé (fait par le développeur)
● La JVM installée sur votre machine
Quel environnement choisir ?
Oracle propose deux kits :
JRE - Pour simplement utiliser des applications Java. C'est comme avoir un lecteur DVD : vous
pouvez regarder des films, mais pas en créer.
JDK - Pour créer des applications Java. Là, vous avez la caméra ET le lecteur : vous pouvez
développer et tester vos programmes.
Philémon GLOBLEHI, Développeur Java 3
Open source ou propriétaire ?
Depuis 2006, Java est progressivement devenu open source (licence GNU GPL). Vous pouvez
donc choisir entre Open JDK (entièrement libre) ou la version Oracle qui, depuis Java 11,
nécessite une licence payante pour un usage professionnel en production.
L'évolution du langage Java
Java a connu une transformation remarquable depuis sa création. Voici les étapes clés de son
évolution, des débuts jusqu'aux versions les plus récentes.
Les débuts (1996-2004) : Poser les fondations
Java 1.0 (1996) marque la naissance du langage avec sa promesse "Write Once, Run Anywhere".
Les bases sont posées : classes, héritage, interfaces, exceptions.
Java 1.1 (1997) introduit les classes internes, la réflexion (introspection du code à l'exécution) et
JDBC pour accéder aux bases de données.
Java 1.2 (1998), rebaptisé Java 2, apporte le Java Collections Framework (listes, ensembles,
maps) qui remplace les anciennes structures Vector et Hashtable. Swing arrive pour créer des
interfaces graphiques modernes.
Java 1.4 (2002) ajoute les assertions, les expressions régulières intégrées, et l'API NIO (New I/O)
pour des entrées/sorties plus performantes.
La grande révolution (2004) : Java 5
Java 5 (2004) est un tournant majeur avec des fonctionnalités qui transforment l'écriture du code
:
● Génériques : enfin du code type-safe (List<String> au lieu de List)
● Autoboxing/Unboxing : conversion automatique entre int et Integer
● Énumérations : types énumérés riches et sûrs
● Annotations : métadonnées pour le code (@Override, @Deprecated)
● Varargs : nombre variable de paramètres (void method(String... args))
Philémon GLOBLEHI, Développeur Java 4
● For amélioré : boucle foreach simplifiée
Ces ajouts rendent Java beaucoup plus agréable et moins verbeux.
Maturation (2006-2011)
Java 6 (2006) se concentre sur les performances et l'outillage, sans grande nouveauté
syntaxique.
Java 7 (2011) apporte des améliorations pratiques :
● Try-with-resources : gestion automatique des ressources
● Notation diamant : List<String> liste = new ArrayList<>() (moins de
répétition)
● Switch sur les String : enfin possible !
● Littéraux numériques : 1_000_000 pour la lisibilité
● Catch multiple : catch (IOException | SQLException e)
L'ère moderne (2014) : Java 8
Java 8 (2014) est une révolution comparable à Java 5 :
● Lambdas et interfaces fonctionnelles : [Link](e ->
[Link](e))
● Streams API : manipulation déclarative des collections
● Méthodes par défaut dans les interfaces : évolution sans casser le code existant
● API Date/Time : [Link] remplace enfin Date et Calendar
● Optional : gérer l'absence de valeur sans null
La programmation fonctionnelle fait son entrée dans Java, rendant le code plus concis et
expressif.
Accélération du rythme (2017-aujourd'hui)
À partir de Java 9, Oracle adopte un rythme de release semestriel (mars et septembre).
Java 9 (2017) :
● Modules (Project Jigsaw) : modularisation du JDK
Philémon GLOBLEHI, Développeur Java 5
● JShell : REPL interactif pour tester du code
● Méthodes factory pour les collections : [Link](), [Link](), [Link]()
Java 10 (2018) :
● var : inférence de type local (var liste = new ArrayList<String>())
Java 11 (2018, LTS) :
● String enrichi : strip(), lines(), repeat(), isBlank()
● Simplification des fichiers source uniques (run direct sans compilation)
● Client HTTP modernisé
Java 12-13 (2019) :
● Switch expressions (preview) : int result = switch(day) { case MON, TUE
-> 1; default -> 0; }
● Blocs de texte (preview) : textes multilignes avec """
Java 14 (2020) :
● Records (preview) : classes de données immuables ultra-concises
● Pattern matching pour instanceof (preview) : if (obj instanceof String s)
● Switch expressions stabilisé
Java 15 (2020) :
● Blocs de texte stabilisés
● Sealed classes (preview) : contrôle fin de l'héritage
Java 16 (2021) :
● Records stabilisés
● Pattern matching pour instanceof stabilisé
Java 17 (2021, LTS) :
● Sealed classes stabilisées
● Suppression définitive du JIT expérimental AOT
Java 18-19 (2022) :
● Pattern matching pour switch (preview) : déconstruction dans les switch
Philémon GLOBLEHI, Développeur Java 6
● Serveur web simple intégré
● Virtual Threads (preview, Project Loom) : threads légers pour la concurrence massive
Java 20 (2023) :
● Continuation des previews (virtual threads, pattern matching)
● Scoped Values (preview) : alternative thread-safe aux ThreadLocal
Java 21 (2023, LTS) :
● Virtual Threads stabilisés : révolution pour la programmation concurrente
● Sequenced Collections : interfaces pour collections ordonnées
● Pattern Matching pour switch stabilisé
● Record Patterns : déconstruction des records dans les patterns
Java 22 (2024) :
● Unnamed Variables & Patterns : _ pour ignorer des valeurs
● String Templates (preview) : interpolation de chaînes sécurisée
● Statements avant super() : plus de flexibilité dans les constructeurs
Java 23 (2024) :
● Primitive Types in Patterns : pattern matching avec types primitifs (preview)
● Module Import Declarations : simplification des imports
● Markdown en Javadoc : documentation plus moderne
Java 24 (2025, mars) :
● Flexible Constructor Bodies : assouplissement des constructeurs (2nd preview)
● Stream Gatherers : nouvelles opérations intermédiaires pour les streams
● Améliorations des performances du garbage collector
Java 25 (2025, septembre, LTS) :
● Flexible Constructor Bodies stabilisé
● Améliorations continues du pattern matching
● Optimisations pour les virtual threads
● Raffinements du Project Valhalla (value types en preview attendu)
Philémon GLOBLEHI, Développeur Java 7
Les projets d'avenir
Plusieurs projets majeurs façonnent l'avenir de Java :
Project Loom : Virtual threads (désormais en production depuis Java 21) révolutionnent la
programmation concurrente avec des millions de threads légers.
Project Valhalla : Value types et generics spécialisés pour améliorer les performances et réduire
l'empreinte mémoire. Les premières previews sont attendues dans les prochaines versions.
Project Panama : Meilleure interopérabilité avec le code natif (C/C++), avec la Foreign Function &
Memory API en cours de finalisation.
Project Amber : Simplification continue de la syntaxe (pattern matching, records, switch
expressions...). Mission largement accomplie avec les versions récentes.
Les versions LTS
Certaines versions sont Long Term Support (LTS), supportées pendant des années :
● Java 8 (2014) : support jusqu'en 2030+
● Java 11 (2018) : support jusqu'en 2026+
● Java 17 (2021) : support jusqu'en 2029+
● Java 21 (2023) : support jusqu'en 2031+
● Java 25 (2025) : support jusqu'en 2033+
Les versions LTS sont désormais publiées tous les deux ans (au lieu de trois), facilitant l'adoption
des nouvelles fonctionnalités en production.
Ce qu'il faut retenir
Java a évolué d'un langage verbeux vers un langage moderne et expressif. Les lambdas et
streams (Java 8) ont introduit la programmation fonctionnelle. Le rythme semestriel accélère
l'innovation. Les records et le pattern matching réduisent considérablement le boilerplate. Les
virtual threads révolutionnent la concurrence. Java reste à la fois stable (LTS tous les deux ans) et
innovant (releases fréquentes), permettant à chacun de choisir son rythme d'adoption.
Philémon GLOBLEHI, Développeur Java 8
Préparer votre environnement de travail
Ce dont vous avez vraiment besoin
Pour démarrer en Java, deux outils sont indispensables :
Le JDK - Votre boîte à outils complète pour créer des programmes. Même si votre ordinateur
peut déjà exécuter des applications Java (grâce au JRE), ça ne suffit pas pour en développer. Le
JDK contient tout : le compilateur, la JVM, et plein d'utilitaires pratiques.
Un IDE - Votre atelier de développement. Techniquement, vous pourriez coder dans le
Bloc-notes, mais ce serait comme construire une maison avec juste un marteau. Un IDE vous
apporte l'assistance dont vous avez besoin : coloration du code, détection d'erreurs en temps
réel, suggestions intelligentes...
Installer le JDK
Rendez-vous sur le site d'Oracle pour télécharger le JDK 11 (ou une version plus récente selon
vos besoins). L'installation varie selon votre système :
● Windows/MacOS : un installeur classique, suivez simplement les étapes
● Linux : généralement via votre gestionnaire de paquets ou une archive à décompresser
Gardez en tête l'emplacement où vous installez le JDK, vous en aurez besoin après.
Choisir et configurer IntelliJ IDEA
IntelliJ IDEA est devenu l'IDE de référence pour beaucoup de développeurs Java. Il existe en
deux versions :
Community Edition (gratuite) - Parfaite pour débuter et créer des applications Java standard.
Largement suffisante pour apprendre.
Ultimate Edition (payante, mais gratuite pour les étudiants) - Ajoute le support avancé pour le
développement web, les bases de données, et d'autres frameworks professionnels.
Philémon GLOBLEHI, Développeur Java 9
Téléchargez simplement l'installeur depuis le site de JetBrains et laissez-vous guider.
Vérifier que tout fonctionne
Au premier lancement d'IntelliJ, l'IDE détecte normalement votre JDK automatiquement. Pour
vérifier :
1. Allez dans File → Project Structure (ou Ctrl+Alt+Shift+S)
2. Regardez la section SDKs dans la colonne de gauche
3. Votre JDK devrait apparaître ici
Pas de JDK détecté ? Pas de panique :
● Cliquez sur le + en haut
● Choisissez Add JDK...
● Naviguez jusqu'au dossier où vous l'avez installé
● Validez
Prêt à coder
Une fois ces étapes franchies, vous êtes paré ! IntelliJ vous proposera de créer votre premier
projet. Choisissez un projet Java simple, et vous verrez que l'IDE fait déjà beaucoup de travail
pour vous : structure de projet, suggestions, documentation intégrée...
Le vrai apprentissage commence maintenant : écrire votre premier programme Java.
Philémon GLOBLEHI, Développeur Java 10
Du code source au programme : comprendre
la compilation
Votre premier programme
Avant de plonger dans les subtilités du langage, voyons comment Java transforme votre code en
programme fonctionnel. Prenons cet exemple simple :
Ne vous inquiétez pas de la syntaxe pour le moment. Ce bout de code affiche juste "Bonjour le
monde !" quand vous le lancez.
Du code source au programme : la compilation
Votre fichier .java contient du texte lisible par un humain. Mais l'ordinateur, lui, ne comprend
que le bytecode. C'est là qu'intervient javac, le compilateur Java.
Ouvrez un terminal, placez-vous dans le dossier de votre fichier et tapez :
Philémon GLOBLEHI, Développeur Java 11
Si tout va bien, un nouveau fichier apparaît : [Link]. C'est votre bytecode,
prêt à être exécuté par la JVM. Impossible à lire pour vous, parfaitement clair pour la machine
virtuelle.
Astuce : Si votre terminal ne reconnaît pas javac, c'est que le chemin vers le JDK n'est pas
configuré. Donnez le chemin complet, par exemple :
Lancer votre programme
Maintenant que vous avez votre fichier .class, la JVM peut prendre le relais. Utilisez la
commande java suivie du nom de la classe sans l'extension :
Et voilà ! Votre message s'affiche. Notez qu'à ce stade, le fichier source .java n'est plus
nécessaire. Seul le .class compte pour l'exécution.
Philémon GLOBLEHI, Développeur Java 12
Les fichiers JAR : regrouper intelligemment
Imaginez un projet avec 500 fichiers .class. Distribuer tout ça séparément serait l'enfer ! Les
fichiers JAR (Java ARchive) résolvent ce problème : c'est simplement un fichier ZIP qui contient
tous vos .class, avec l'extension .jar.
Pour créer une archive :
Ensuite, ajoutez-la au classpath comme n'importe quel dossier :
Toutes les bibliothèques Java se distribuent sous forme de JAR. C'est pratique, compact, et ça
évite d'éparpiller des centaines de fichiers.
Travailler avec IntelliJ IDEA
Compiler à la main, c'est formateur, mais dans la vraie vie, vous utiliserez un IDE. IntelliJ fait tout
le sale boulot pour vous :
● Compilation automatique à chaque sauvegarde
● Détection des erreurs en temps réel avec des suggestions de correction
● Refactoring intelligent quand vous renommez une classe
● Navigation rapide dans votre code
Créer un projet
Philémon GLOBLEHI, Développeur Java 13
Dans IntelliJ : File → New → Project, choisissez Java, donnez un nom à votre projet, et c'est parti.
L'IDE crée automatiquement un dossier src pour vos sources.
Ajoutez-y votre fichier [Link], et IntelliJ le compile instantanément dès que
vous sauvegardez. Plus besoin de taper javac manuellement.
Quelques raccourcis à connaître
● Ctrl + Espace : autocomplétion (votre meilleur ami)
● Alt + Entrée : suggestions de correction
● Shift + F10 : lancer votre programme
● Ctrl + Alt + L : reformater le code proprement
● Shift + Shift : chercher n'importe quoi dans le projet
Ajouter une bibliothèque externe
Disons que vous voulez manipuler du JSON avec la bibliothèque Gson de Google. Téléchargez
le fichier JAR, créez un dossier lib dans votre projet, et copiez-y le JAR.
Ensuite : clic droit sur le JAR → Add as Library. IntelliJ s'occupe du reste.
Vous pouvez maintenant utiliser Gson dans votre code :
Philémon GLOBLEHI, Développeur Java 14
Lancez ce code, et vous obtenez une conversion automatique entre objets Java et JSON.
Magique, non ?
Exporter votre projet
Pour partager votre travail : Build → Build Artifacts → JAR. IntelliJ crée un fichier JAR contenant
tout votre code compilé, prêt à être distribué.
Ce qu'il faut retenir
Le cycle de vie d'un programme Java tient en trois étapes : écrire (fichiers .java), compiler
(fichiers .class), exécuter (via la JVM). Les fichiers JAR simplifient la distribution, et un bon IDE
Philémon GLOBLEHI, Développeur Java 15
comme IntelliJ automatise l'essentiel pour que vous puissiez vous concentrer sur votre code
plutôt que sur la plomberie.
Philémon GLOBLEHI, Développeur Java 16
La grammaire de Java : ce qui rend ton code
lisible
Les instructions : donnez vos ordres
Java est un langage impératif : vous écrivez une série d'ordres que la machine exécute dans
l'ordre. Chaque instruction se termine par un point-virgule. Simple, efficace.
Trois instructions, trois points-virgules. Le compilateur sait exactement où chacune commence et
finit.
Le typage : être explicite pour éviter les erreurs
Java ne tolère pas l'à-peu-près. Quand vous créez une variable, vous devez dire ce qu'elle va
contenir. C'est le typage fort.
Ici, age ne pourra contenir qu'un nombre entier. Si vous tentez d'y mettre du texte, le compilateur
vous arrête net :
Philémon GLOBLEHI, Développeur Java 17
Pourquoi c'est utile ? Parce que ça évite des bugs vicieux. Le compilateur joue le rôle de
garde-fou : il détecte les incohérences avant même que votre programme ne tourne.
Java vérifie les types deux fois : à la compilation (typage statique) et à l'exécution (typage
dynamique). Double sécurité.
Le raccourci var (depuis Java 10)
Parfois, le type est évident. Plutôt que de répéter l'information, utilisez var :
Le compilateur devine le type grâce à la valeur que vous assignez. Ça ne marche que si vous
initialisez immédiatement la variable, sinon Java ne peut pas deviner.
Les blocs : organiser son code
Les accolades {} délimitent des blocs de code. Elles regroupent plusieurs instructions
ensemble, souvent après un if, un while, ou une fonction.
Vous pouvez même créer des blocs anonymes pour isoler temporairement des variables :
Philémon GLOBLEHI, Développeur Java 18
Pas besoin de point-virgule après une accolade fermante.
Exemple complet :
Philémon GLOBLEHI, Développeur Java 19
Les commentaires : expliquez votre intention
Commentaires courts avec // :
Philémon GLOBLEHI, Développeur Java 20
Commentaires longs avec /* */ :
Commentaires javadoc pour générer de la documentation automatique, avec /** :
Le formatage : rendre le code lisible
Techniquement, Java se fiche complètement de la mise en forme. Vous pourriez tout écrire sur
une seule ligne. Mais pour les humains qui reliront votre code (vous y compris dans 6 mois), c'est
l'enfer.
Code illisible mais valide :
Philémon GLOBLEHI, Développeur Java 21
Même code, propre :
Les règles d'or
● Accolade ouvrante sur la même ligne, accolade fermante seule sur sa ligne
● Un espace autour des opérateurs : x = y + 2 plutôt que x=y+2
● Indentation (tabulation ou espaces) pour chaque niveau de bloc
● Retour à la ligne après chaque instruction
IntelliJ reformate automatiquement votre code avec Ctrl + Alt + L. Profitez-en
Les conventions de nommage : parler la même langue
Les développeurs Java sont pointilleux sur les noms. Respecter ces conventions rend votre code
immédiatement compréhensible.
Classes et interfaces : première lettre en majuscule, style chameau (PascalCase)
Méthodes et variables : première lettre en minuscule, style dromadaire (camelCase)
Philémon GLOBLEHI, Développeur Java 22
Constantes : tout en majuscules, mots séparés par des underscores
Packages : tout en minuscules
Soyez explicites. Préférez nombreInscriptions à nb ou n. Votre futur vous dira merci. Pour
les variables jetables dans une boucle, i, j, k restent acceptables.
Les mots réservés : terrain interdit
Certains mots appartiennent à Java et vous ne pouvez pas les utiliser comme noms de variables
ou de classes : if, while, class, int, public, return, new, this, etc.
À éviter aussi :
● true, false, null : valeurs spéciales
● _ (underscore seul) : réservé depuis Java 9
Si vous tentez d'utiliser int class = 5;, le compilateur refuse. Choisissez des noms qui ont
du sens, de toute façon.
Ce qu'il faut retenir
Philémon GLOBLEHI, Développeur Java 23
Java impose une structure claire : instructions terminées par ;, types explicites, blocs délimités
par {}. Le compilateur est strict, mais c'est pour votre bien : il attrape les erreurs tôt. Respectez
les conventions de nommage et de formatage, et votre code sera lisible par n'importe quel
développeur Java. C'est la base pour écrire du code maintenable.
Philémon GLOBLEHI, Développeur Java 24
Ta première classe (et pourquoi c’est
révolutionnaire)
Le principe de base
Java fonctionne sur un principe simple : tout est objet, ou presque. Un objet, c'est une
représentation informatique d'une entité concrète ou abstraite. Pour créer ces objets, on utilise
des classes, qui servent de moules ou de plans de construction.
Programmer en Java, c'est donc jongler entre trois activités : définir ces plans (les classes),
fabriquer des objets à partir de ces plans (l'instanciation), et faire travailler ces objets ensemble.
Créer sa première classe
Prenons un exemple différent : imaginons qu'on veuille modéliser un Compte bancaire pour une
application de gestion financière.
En Java, chaque classe vit dans son propre fichier. Le nom du fichier doit correspondre
exactement au nom de la classe, avec l'extension .java. Pour notre compte, on crée donc
[Link] :
Philémon GLOBLEHI, Développeur Java 25
Le mot-clé class lance la déclaration, précédé de public (on reviendra sur ce détail). Les
accolades délimitent le territoire de notre classe.
Ce qu'on peut mettre dans une classe
Une classe, c'est comme une boîte à outils qui peut contenir :
Des attributs - Ce sont les caractéristiques de l'objet, son ADN en quelque sorte. Notre compte
bancaire pourrait avoir un solde, un numéro de compte, etc.
Des méthodes - Les actions qu'on peut faire avec l'objet. Pour un compte : déposer de l'argent,
retirer, consulter le solde.
Des constantes - Des valeurs fixes qui donnent du sens au code.
Des énumérations et classes internes - Des structures plus avancées qu'on abordera plus tard.
L'ordre d'apparition de ces éléments est libre, mais par convention, on place généralement :
constantes, énumérations, attributs, puis méthodes.
Ajouter des comportements avec les méthodes
Enrichissons notre compte bancaire avec quelques méthodes. Commençons par consulter le
solde :
Philémon GLOBLEHI, Développeur Java 26
Une méthode suit toujours ce schéma :
[visibilité] [ce qu'elle renvoie] [son nom] ([ce qu'elle reçoit]) {
[les instructions]
Pour getSolde() :
● Visibilité : public (accessible de partout)
● Type de retour : double (un nombre décimal)
● Nom : getSolde
● Paramètres : aucun
L'attribut solde est marqué private - il reste invisible de l'extérieur. Par défaut, un double vaut
0.
Ajoutons maintenant des opérations bancaires :
Philémon GLOBLEHI, Développeur Java 27
Notez le mot-clé void pour les méthodes qui ne renvoient rien. La méthode toString() est
spéciale : elle génère une version texte de l'objet, utilisée notamment par
[Link](). L'opérateur + colle les textes ensemble et convertit
automatiquement les nombres en texte.
Philémon GLOBLEHI, Développeur Java 28
Le point d'entrée : la méthode main
Pour exécuter un programme, Java cherche une méthode particulière qui sert de porte d'entrée :
Toute classe peut avoir sa propre méthode main, ce qui signifie qu'une application peut avoir
plusieurs points de démarrage possibles. C'est pour ça qu'on doit préciser à la commande java
quelle classe lancer.
Ajoutons un petit programme de démonstration :
Philémon GLOBLEHI, Développeur Java 29
La ligne CompteBancaire compte = new CompteBancaire(); est cruciale. Une classe,
c'est juste un plan. Pour vraiment travailler avec un compte bancaire, on doit fabriquer une
instance concrète - un objet. Le mot-clé new déclenche cette fabrication et alloue de la mémoire
pour stocker l'état de l'objet.
Philémon GLOBLEHI, Développeur Java 30
Les parenthèses après new CompteBancaire() sont obligatoires.
Le point (.) sert à atteindre les méthodes de l'objet :
La classe System et son attribut out permettent d'afficher du texte. Quand on passe un objet à
println(), celui-ci appelle automatiquement la méthode toString() de l'objet.
Faire tourner le programme
En ligne de commande, placez-vous dans le dossier contenant votre fichier et tapez :
Résultat affiché :
Philémon GLOBLEHI, Développeur Java 31
Ce qu'il faut retenir
Une classe définit un modèle, une instance est un objet concret créé avec new. Les attributs
stockent l'état (souvent private), les méthodes définissent les actions (souvent public). Le
main est le point d'entrée du programme. L'opérateur . accède aux méthodes et attributs d'un
objet. Vous venez de créer vos premiers objets Java !
Philémon GLOBLEHI, Développeur Java 32
Les types primitifs : les briques de base
Pas tout à fait orienté objet
Java prétend être un langage orienté objet, mais il triche un peu. Il existe huit types primitifs qui
ne sont pas des objets, juste des valeurs stockées directement en mémoire. Pourquoi ? Pour des
raisons de performance : manipuler un simple nombre entier comme un objet serait lourd et
inefficace.
Voici les huit types primitifs :
Nombres entiers : byte, short, int, long
Nombres décimaux : float, double
Caractère : char
Booléen : boolean
Les booléens : vrai ou faux
Le type boolean ne connaît que deux valeurs : true ou false. C'est tout. Par défaut, un
attribut booléen vaut false.
Impossible d'utiliser un booléen dans un calcul mathématique. C'est une question de logique, pas
de nombre.
Les caractères : char
Philémon GLOBLEHI, Développeur Java 33
Un char stocke un seul caractère Unicode (UTF-16) sur 2 octets. On l'écrit entre apostrophes :
Les entiers : byte, short, int, long
Ces quatre types diffèrent uniquement par leur capacité de stockage :
● byte : 1 octet (-128 à 127)
● short : 2 octets (-32 768 à 32 767)
● int : 4 octets (-2 milliards à +2 milliards environ)
● long : 8 octets (vraiment très grand)
Par défaut, ils valent tous 0.
Règle simple : Vous pouvez affecter un type "petit" à un type "grand" sans problème. L'inverse
nécessite un cast avec risque de perte.
Pour un long, ajoutez le suffixe L :
Attention au dépassement : Si un calcul dépasse les limites du type, le nombre "boucle" sans
avertissement. Pour des calculs très grands, utilisez la classe BigInteger.
Philémon GLOBLEHI, Développeur Java 34
Les décimaux : float, double
Ces types représentent des nombres à virgule selon la norme IEEE 754.
● float : 4 octets (simple précision)
● double : 8 octets (double précision)
Par défaut, ils valent 0.0.
Vous pouvez mélanger entiers et décimaux :
Danger critique : Les nombres à virgule flottante sont approximatifs. Ne les utilisez jamais pour
de l'argent ou des calculs nécessitant une précision absolue. Pour ça, utilisez BigDecimal ou
stockez tout en centimes avec des int.
Les classes enveloppes : quand les primitifs ont besoin
d'être des objets
Philémon GLOBLEHI, Développeur Java 35
Parfois, vous devez traiter un nombre comme un objet (pour le mettre dans une collection, par
exemple). Java fournit des classes enveloppes :
● boolean → Boolean
● char → Character
● int → Integer
● byte → Byte
● short → Short
● long → Long
● float → Float
● double → Double
Ces classes offrent des méthodes utiles, comme convertir une chaîne en nombre :
Contrairement aux primitifs, ces objets peuvent être null (absence de valeur) :
Elles contiennent aussi des constantes utiles :
L'autoboxing : conversion automatique
Philémon GLOBLEHI, Développeur Java 36
Java convertit automatiquement entre primitifs et enveloppes quand nécessaire :
Le compilateur transforme ça en :
C'est pratique, mais attention aux pièges :
L'autoboxing fonctionne aussi avec les paramètres de méthode :
Quand l'utiliser ? L'autoboxing simplifie le code, mais il a un coût en performance. Si vous faites
des calculs intensifs, préférez les primitifs purs.
Ce qu'il faut retenir
Les types primitifs sont rapides et légers. Utilisez int pour les nombres entiers courants,
double pour les décimaux (sauf argent), boolean pour la logique, char pour les caractères
Philémon GLOBLEHI, Développeur Java 37
uniques. Les classes enveloppes permettent de traiter ces valeurs comme des objets quand
nécessaire. L'autoboxing facilite la conversion, mais peut cacher des bugs si vous oubliez le
risque de null. Choisissez toujours le type adapté à votre besoin : ni trop petit (risque de
dépassement), ni inutilement grand.
Philémon GLOBLEHI, Développeur Java 38
Manipuler les données : opérateurs et
expressions
L'affectation : copier des valeurs
L'opérateur = copie la valeur de droite dans la variable de gauche. Java fonctionne par copie.
Pour les objets, c'est différent : la variable stocke une référence (l'adresse mémoire de l'objet),
pas l'objet lui-même.
Si vous créez un nouvel objet, la référence change :
La valeur null signifie "ne pointe vers rien" :
Philémon GLOBLEHI, Développeur Java 39
Cette logique de copie par référence s'applique aussi aux paramètres de méthodes.
Les opérateurs arithmétiques
Opérations binaires (deux opérandes) :
Ordre de priorité : * et / avant + et -. Le reste de division : %
Opérations unaires (un seul opérande) :
Attention à la position de ++ et -- :
Philémon GLOBLEHI, Développeur Java 40
Préfixé : modifie d'abord, affecte ensuite. Postfixé : affecte d'abord, modifie après.
Concaténation de texte
L'opérateur + assemble des chaînes de caractères :
Java convertit automatiquement les nombres en texte. Même null devient le texte "null" :
En coulisses, Java utilise StringBuilder pour optimiser ces concaténations.
Les opérateurs de comparaison
Ces opérateurs retournent true ou false :
Piège avec les objets : == compare les références, pas le contenu !
Philémon GLOBLEHI, Développeur Java 41
Retenez bien : pour comparer des objets (surtout des String), utilisez toujours .equals(),
jamais ==.
Les opérateurs logiques
Pour combiner des conditions booléennes :
Optimisation automatique : Java n'évalue la partie droite que si nécessaire.
Pour && : si la gauche est false, inutile de vérifier la droite. Pour || : si la gauche est true,
inutile de vérifier la droite.
Si vous voulez forcer l'évaluation complète, utilisez & ou | (rare).
Philémon GLOBLEHI, Développeur Java 42
L'opérateur ternaire : condition en une ligne
Raccourci pour un if/else simple :
Format : condition ? valeur_si_vrai : valeur_si_faux
Pratique pour les affectations conditionnelles courtes.
Le transtypage (cast)
Forcer une conversion de type quand Java ne l'autorise pas automatiquement :
Si Java vous demande un cast, c'est souvent un signal : vérifiez que vous ne perdez pas de
données ou que votre logique est correcte.
Opérateurs combinés avec assignation
Raccourcis pour modifier et réaffecter :
Fonctionne aussi avec les opérateurs binaires : &=, |=, ^=, <<=, >>=.
Philémon GLOBLEHI, Développeur Java 43
L'opérateur point .
Accéder aux méthodes et attributs d'un objet :
On peut même l'utiliser directement sur une valeur littérale :
Ce qu'il faut retenir
L'affectation copie les valeurs (ou les références pour les objets). Les opérateurs arithmétiques
respectent les priorités classiques. Utilisez .equals() pour comparer des objets, jamais ==. Les
opérateurs logiques optimisent automatiquement l'évaluation. Le transtypage force une
conversion risquée. Les raccourcis comme += et ++ rendent le code plus concis. Maîtriser ces
opérateurs, c'est maîtriser les bases de toute logique Java.
Philémon GLOBLEHI, Développeur Java 44
Diriger le flux : conditions et boucles
Prendre des décisions avec if-else
Le if exécute un bloc de code uniquement si une condition est vraie :
Ajoutez else pour gérer le cas contraire :
Enchaînez plusieurs conditions avec else if :
Les accolades sont optionnelles pour une seule instruction, mais utilisez-les systématiquement
par clarté.
Sortir d'une méthode avec return
Philémon GLOBLEHI, Développeur Java 45
Le mot-clé return stoppe immédiatement l'exécution d'une méthode et renvoie une valeur :
Pour une méthode void, utilisez return seul pour sortir prématurément :
Le code après un return ne sera jamais exécuté (erreur "unreachable code").
Répéter avec while
Le while répète un bloc tant qu'une condition reste vraie :
La condition est testée avant chaque itération. Si elle est fausse dès le départ, le bloc ne
s'exécute jamais.
Philémon GLOBLEHI, Développeur Java 46
Répéter au moins une fois avec do-while
Variante du while qui garantit au moins une exécution :
La condition est testée après chaque itération.
La boucle for classique
Idéale pour itérer un nombre défini de fois :
Structure : initialisation ; condition ; incrément
Chaque partie peut être vide (mais gardez les points-virgules) :
Boucle infinie (à utiliser avec prudence) :
Philémon GLOBLEHI, Développeur Java 47
Le for amélioré (for-each)
Pour parcourir facilement des collections ou tableaux :
Format : for (type variable : collection). Plus lisible qu'un for classique quand vous
n'avez pas besoin de l'indice.
Contrôler les boucles avec break et continue
break sort complètement de la boucle :
continue passe directement à l'itération suivante :
Philémon GLOBLEHI, Développeur Java 48
Les libellés : pour les boucles imbriquées
Quand vous avez des boucles dans des boucles, un libellé permet de sortir de plusieurs niveaux
d'un coup :
Sans libellé, break ne sortirait que de la boucle interne.
Le switch : sélectionner parmi plusieurs valeurs
Alternative au if-else quand vous comparez une variable à plusieurs valeurs précises :
Philémon GLOBLEHI, Développeur Java 49
Important : Sans break, l'exécution continue dans les cas suivants (effet "cascade") :
Le switch fonctionne avec : entiers, caractères, chaînes de caractères, énumérations.
Ce qu'il faut retenir
Les structures de contrôle dirigent le flux d'exécution de votre programme. Le if-else prend
des décisions, les boucles (while, for) répètent des actions, break et continue affinent le
comportement des itérations, et switch simplifie les sélections multiples. Maîtriser ces outils,
Philémon GLOBLEHI, Développeur Java 50
c'est contrôler précisément la logique de vos programmes. Utilisez toujours des accolades pour
la lisibilité, même quand elles sont optionnelles.
Philémon GLOBLEHI, Développeur Java 51
Les tableaux : stocker et organiser
Qu'est-ce qu'un tableau ?
Un tableau stocke plusieurs valeurs du même type dans une seule variable. En Java, les tableaux
sont eux-mêmes des objets, ce qui signifie qu'une variable tableau peut valoir null.
Déclaration :
Créer et initialiser un tableau
Initialisation directe avec des valeurs :
Création avec new :
Quand vous créez un tableau sans valeurs initiales, Java remplit automatiquement avec des
valeurs par défaut :
● Nombres : 0 (ou 0.0)
● Booléens : false
● Objets : null
La taille peut être une variable :
Philémon GLOBLEHI, Développeur Java 52
Important : Une fois créé, un tableau a une taille fixe. Impossible d'ajouter ou retirer des
éléments. Pour changer la taille, il faut créer un nouveau tableau.
L'attribut length donne la taille (en lecture seule) :
Vous pouvez créer un tableau vide (new int[0]), mais une taille négative provoquera une
erreur à l'exécution.
Accéder aux éléments
Les indices commencent à 0. Le dernier élément est à l'indice length - 1.
Parcourir avec un for classique :
Parcourir avec un for amélioré (plus simple quand l'indice n'est pas nécessaire) :
Philémon GLOBLEHI, Développeur Java 53
Accéder à un indice hors limites provoque une erreur ArrayIndexOutOfBoundsException :
Tableaux multi-dimensionnels
Un tableau de tableaux, utile pour des matrices :
Les dimensions peuvent être irrégulières :
Afficher un tableau
Si vous affichez directement un tableau, vous obtenez quelque chose comme [I@ee7d9f1 (le
type et le code de hachage).
Pour voir le contenu, utilisez [Link]() :
Philémon GLOBLEHI, Développeur Java 54
Pour les tableaux multi-dimensionnels, utilisez [Link]().
Comparer des tableaux
L'opérateur == compare les références, pas le contenu :
Même equals() ne fonctionne pas comme attendu sur les tableaux. Utilisez
[Link]() :
Pour les tableaux multi-dimensionnels : [Link]().
Trier et rechercher
Trier un tableau :
Fonctionne aussi avec les chaînes et les objets comparables.
Recherche binaire (nécessite un tableau trié) :
Philémon GLOBLEHI, Développeur Java 55
Si l'élément n'existe pas, retourne un nombre négatif indiquant où il devrait être.
Copier un tableau
Créer une copie avec [Link]() :
Copier une portion avec [Link]() :
Copier entre tableaux existants avec [Link]() :
Le typage des tableaux
Un tableau conserve son type d'origine même si affecté à une variable de type parent :
Philémon GLOBLEHI, Développeur Java 56
Convertir un tableau en liste
Pour utiliser les collections Java, convertissez avec [Link]() :
Attention : la liste obtenue a une taille fixe (pas d'ajout/suppression possible), mais les éléments
sont modifiables.
Ce qu'il faut retenir
Les tableaux stockent des collections de taille fixe. Les indices commencent à 0. Utilisez
[Link]() pour afficher le contenu, [Link]() pour comparer,
[Link]() pour trier. Pour une copie, préférez [Link](). Le for amélioré
simplifie le parcours. Les tableaux ont des limites (taille fixe, pas de méthodes pratiques), c'est
pourquoi on préfère souvent les List et autres collections dans le code moderne. Mais
comprendre les tableaux reste essentiel, car ils restent utilisés dans de nombreuses API.
Philémon GLOBLEHI, Développeur Java 57
Attributs et méthodes : donner vie aux objets
Les attributs : l'état interne
Les attributs stockent les données d'un objet. Chaque instance a ses propres valeurs d'attributs.
Vous pouvez ensuite accéder à ces attributs :
Portée : public ou private
public : accessible partout dans le programme
private : accessible uniquement depuis la classe elle-même
La bonne pratique consiste à rendre les attributs private pour respecter l'encapsulation (on y
revient plus loin).
Valeurs par défaut
Même sans initialisation explicite, les attributs ont des valeurs par défaut :
● Nombres : 0 (ou 0.0)
● Booléens : false
Philémon GLOBLEHI, Développeur Java 58
● Objets : null
Vous pouvez initialiser directement :
Attributs finaux : figer une valeur
Le mot-clé final empêche toute modification après initialisation :
Une fois créé, impossible de changer nombreChapitres.
Attention : final interdit de changer la référence, pas l'état de l'objet référencé :
Philémon GLOBLEHI, Développeur Java 59
Attributs de classe (static)
Un attribut static est partagé par toutes les instances. Il appartient à la classe, pas aux objets
individuels.
Accessible directement depuis la classe :
Si vous modifiez un attribut static depuis une instance, tous les objets voient le changement :
Même si possible, évitez d'accéder aux attributs static via une instance, ça prête à confusion.
Passez toujours par le nom de la classe.
Constantes : static + final
Pour déclarer une vraie constante, combinez static et final :
Philémon GLOBLEHI, Développeur Java 60
Convention : nom en majuscules, mots séparés par _.
Les méthodes : les actions
Les méthodes définissent ce que vos objets peuvent faire.
Portée des méthodes
public : appelable partout (définit le contrat de la classe)
private : appelable uniquement depuis la classe (méthodes utilitaires internes)
Valeur de retour
Une méthode retourne au plus une valeur. Utilisez void si elle ne retourne rien.
Philémon GLOBLEHI, Développeur Java 61
Pour sortir prématurément d'une méthode void, utilisez return seul.
Paramètres
Les méthodes peuvent recevoir des paramètres :
Nombre variable de paramètres (varargs) avec ... :
Appel flexible :
Philémon GLOBLEHI, Développeur Java 62
Règles :
● Le paramètre variable doit être le dernier
● Vous pouvez aussi passer un tableau directement
Paramètres finaux empêchent toute réaffectation dans la méthode :
Variables locales
Déclarez des variables où vous voulez dans une méthode, mais initialisez-les avant usage
(contrairement aux attributs, elles n'ont pas de valeur par défaut) :
La portée d'une variable est limitée au bloc où elle est déclarée :
Philémon GLOBLEHI, Développeur Java 63
Méthodes de classe (static)
Une méthode static n'a pas accès aux attributs d'instance, seulement aux attributs static.
Elle fonctionne indépendamment de tout objet.
Appel direct depuis la classe :
La méthode main est static, c'est pourquoi elle peut démarrer sans créer d'objet.
Surcharge (overloading)
Plusieurs méthodes peuvent avoir le même nom si leurs paramètres diffèrent :
Philémon GLOBLEHI, Développeur Java 64
Le compilateur choisit automatiquement la bonne méthode selon les arguments.
Utilisez la surcharge pour des comportements similaires, pas pour des opérations totalement
différentes.
Le mot-clé this
this désigne l'instance courante. Utile quand un paramètre masque un attribut :
Vous pouvez aussi retourner this pour chaîner des appels :
this n'existe pas dans les méthodes static (elles n'ont pas d'instance).
Encapsulation : cacher l'état interne
Philémon GLOBLEHI, Développeur Java 65
Principe fondamental : rendez vos attributs private et offrez un accès contrôlé via des
méthodes publiques.
Pourquoi ? Pour découpler l'implémentation interne de l'interface publique. Les clients utilisent
votre objet sans connaître ses détails internes. Si vous changez l'implémentation plus tard, tant
que les méthodes publiques fonctionnent pareil, aucun impact.
Getters (accesseurs) et setters (mutateurs)
Au lieu d'exposer directement un attribut, créez des méthodes :
Getter (lecture) :
Pour un booléen, préférez is :
Setter (écriture) :
Classe complète avec encapsulation :
Philémon GLOBLEHI, Développeur Java 66
Les clients manipulent des doubles pages, mais l'état interne est en pages simples. Ils n'en
savent rien, et c'est parfait.
Vous contrôlez aussi quelles propriétés sont consultables ou modifiables :
● Pas de getter = propriété non consultable
● Pas de setter = propriété en lecture seule
IntelliJ peut générer automatiquement ces méthodes : clic droit → Generate → Getters and
Setters.
Ce qu'il faut retenir
Les attributs stockent l'état, les méthodes définissent le comportement. Utilisez private pour
les attributs (encapsulation), public pour les méthodes accessibles. Les attributs et méthodes
static appartiennent à la classe, pas aux instances. Le mot-clé this référence l'objet courant.
Les getters/setters offrent un accès contrôlé aux propriétés. La surcharge permet plusieurs
méthodes de même nom avec des paramètres différents. L'encapsulation cache les détails
d'implémentation et facilite l'évolution du code.
Philémon GLOBLEHI, Développeur Java 67
Naissance et mort d’un objet : le cycle de vie
Les constructeurs : initialiser correctement
Un constructeur est une méthode spéciale appelée automatiquement à la création d'un objet.
Son rôle : garantir que l'objet démarre dans un état cohérent.
Caractéristiques :
● Même nom que la classe
● Pas de type de retour (pas même void)
● Appelé avec new
Quand vous écrivez new CompteBancaire(), la JVM alloue la mémoire, appelle le
constructeur, puis retourne la référence de l'objet créé.
Constructeurs avec paramètres
Les constructeurs peuvent recevoir des paramètres pour initialiser l'objet :
Philémon GLOBLEHI, Développeur Java 68
Comme les méthodes, les constructeurs supportent la surcharge : plusieurs constructeurs avec
des paramètres différents.
Ordre d'initialisation
Java garantit cet ordre :
1. Initialisation des attributs (valeurs par défaut ou explicites)
2. Exécution du constructeur
Attributs final dans les constructeurs
Philémon GLOBLEHI, Développeur Java 69
Un attribut final doit être initialisé avant la fin du constructeur :
Le compilateur refuse :
● D'accéder à un final non initialisé
● De finir un constructeur sans initialiser tous les final
● De réassigner un final déjà initialisé
Le constructeur par défaut
Si vous ne déclarez aucun constructeur, le compilateur en génère un automatiquement
(constructeur par défaut, vide et public).
Attention : Dès que vous créez un constructeur (quel qu'il soit), le constructeur par défaut
disparaît.
Philémon GLOBLEHI, Développeur Java 70
Constructeur privé
Déclarez un constructeur private pour empêcher l'instantiation :
Utile pour les classes utilitaires qui ne contiennent que des méthodes static.
Chaîner les constructeurs
Un constructeur peut appeler un autre constructeur avec this(...) comme première
instruction :
Philémon GLOBLEHI, Développeur Java 71
Évite la duplication de code.
Appeler des méthodes dans un constructeur
Possible, mais prudence : tant que le constructeur n'est pas terminé, l'objet n'est pas
complètement initialisé.
Assurez-vous que l'état nécessaire est initialisé avant d'appeler une méthode.
Philémon GLOBLEHI, Développeur Java 72
Injection de dépendances
Plutôt que de créer les objets dont vous avez besoin dans le constructeur, recevez-les en
paramètre :
Mauvaise approche (couplage fort) :
Meilleure approche (injection) :
Utilisation :
Découple la création de l'utilisation. C'est la base de l'inversion de dépendances, un principe
fondamental en programmation objet.
Blocs d'initialisation (rarement utilisés)
Philémon GLOBLEHI, Développeur Java 73
Bloc d'instance : s'exécute avant chaque constructeur
Bloc static : s'exécute une seule fois au chargement de la classe
Rarement nécessaires, souvent remplaçables par des constructeurs ou des initialisations
directes.
Le ramasse-miettes (garbage collector)
Le ramasse-miettes libère automatiquement la mémoire des objets non référencés. Vous n'avez
pas à détruire manuellement les objets.
Un objet est considéré comme référencé si :
● Une variable active pointe vers lui
Philémon GLOBLEHI, Développeur Java 74
● Un autre objet référencé a un attribut qui pointe vers lui
Pour qu'un objet soit collecté :
● Aucune référence ne doit pointer vers lui
Le ramasse-miettes gère même les références circulaires (un objet qui se référence lui-même via
d'autres objets).
Vous pouvez suggérer un passage du ramasse-miettes avec [Link](), mais sans garantie
d'exécution immédiate. Le ramasse-miettes décide de son timing.
Attention : Le ramasse-miettes ne libère que la mémoire. Les autres ressources (fichiers,
connexions réseau) nécessitent une gestion explicite.
Ce qu'il faut retenir
Les constructeurs initialisent les objets à la création. Ils peuvent recevoir des paramètres et être
surchargés. L'injection de dépendances découple création et utilisation. Java gère
automatiquement la mémoire via le ramasse-miettes qui libère les objets non référencés. La
stack stocke les variables locales, le heap stocke les objets. Les constructeurs privés empêchent
l'instantiation. Chaîner les constructeurs avec this() évite la duplication. Le ramasse-miettes
fonctionne automatiquement, mais ne libère pas les ressources non-mémoire.
Philémon GLOBLEHI, Développeur Java 75
Ranger ton code : les packages
Le problème des collisions de noms
Imaginez deux développeurs qui créent chacun une classe GestionnaireDocument. Si ces
deux classes se retrouvent dans la même application, laquelle la JVM choisira-t-elle ? La
première trouvée, en ignorant l'autre. Catastrophe garantie.
Les packages résolvent ce problème en créant des espaces de noms distincts. Votre
GestionnaireDocument et celui d'un autre développeur peuvent coexister pacifiquement
dans des packages différents.
Déclarer un package
La première ligne d'un fichier Java (avant toute déclaration de classe) indique son package :
Règle importante : La structure des packages doit correspondre à l'arborescence des dossiers.
Si votre classe est dans package [Link], le fichier doit être dans :
Philémon GLOBLEHI, Développeur Java 76
Le compilateur respecte automatiquement cette structure pour les fichiers .class.
Le package par défaut (à éviter)
Sans déclaration package, votre classe appartient au package par défaut (sans nom). C'est
pratique pour des tests rapides, mais ne faites jamais ça en production. IntelliJ vous alertera
d'ailleurs. Tous vos fichiers doivent appartenir à un package nommé.
Sous-packages : organiser l'arborescence
Les packages s'organisent de manière hiérarchique, comme des dossiers :
Sur le disque :
Convention de nommage
Pour éviter toute collision, utilisez votre nom de domaine inversé comme base :
● Domaine : [Link]
● Package : [Link]
Puis ajoutez le nom de l'application ou de la bibliothèque.
Philémon GLOBLEHI, Développeur Java 77
Noms réservés : Les packages commençant par java ou javax sont réservés à l'API standard.
N'y touchez pas.
Le nom complet d'une classe
Une classe est désignée par son nom complet : package + nom de classe.
La classe String s'appelle en réalité [Link]. Vous pourriez créer votre propre
classe String dans un autre package sans conflit :
Pour utiliser une classe depuis un autre package, utilisez son nom complet :
Exception : Vous avez automatiquement accès aux classes de votre propre package et du
package [Link] (d'où l'usage direct de String, Math, System...).
Import : simplifier l'accès
Philémon GLOBLEHI, Développeur Java 78
Pour éviter de répéter les noms complets, importez les classes nécessaires :
Types d'imports
Importer une classe :
Importer une méthode ou attribut static :
Importer toutes les classes d'un package :
Philémon GLOBLEHI, Développeur Java 79
Déconseillé ! Même si pratique, ça rend le code moins clair. IntelliJ gère les imports
automatiquement (Ctrl + Alt + O pour les réorganiser). Autant être explicite.
Exemple complet :
Si un nom importé entre en conflit avec un nom local, l'import est ignoré. Utilisez alors le nom
complet pour lever l'ambiguïté.
Portée package : cacher l'implémentation
Philémon GLOBLEHI, Développeur Java 80
Jusqu'ici : public (accessible partout) et private (accessible uniquement dans la classe).
Il existe une troisième portée, sans mot-clé : la portée package. Accessible uniquement depuis
le même package.
La classe CryptoAlgo est invisible hors du package [Link]. Seule
CryptoUtils est exposée publiquement. Ça cache la complexité interne et simplifie l'API.
Le fichier [Link]
Fichier spécial (optionnel) dans chaque package pour documenter le package lui-même :
Philémon GLOBLEHI, Développeur Java 81
Utile pour générer de la documentation Javadoc complète. Peut aussi contenir des annotations
de package.
Ce qu'il faut retenir
Les packages créent des espaces de noms pour éviter les collisions. Organisez-les selon votre
nom de domaine inversé. La structure des packages doit refléter l'arborescence des dossiers.
Importez les classes nécessaires pour éviter les noms complets répétitifs. La portée package
(sans mot-clé) cache l'implémentation aux classes externes. IntelliJ gère automatiquement les
imports, profitez-en. Ne laissez jamais une classe dans le package par défaut. Les packages
structurent votre code et facilitent sa maintenance à long terme.
Philémon GLOBLEHI, Développeur Java 82
Héritage vs Composition : quand utiliser
quoi
Deux types de relations fondamentales
En programmation objet, les classes entretiennent deux relations principales :
"Est un" (is-a) : héritage - une classe est un cas particulier d'une autre "A un" (has-a) :
composition - une classe utilise les services d'une autre
L'héritage : factoriser le code commun
Imaginez une bibliothèque numérique avec des livres papier et des livres audio. Ces deux
classes partagent des caractéristiques (titre, auteur) mais ont aussi leurs spécificités.
Plutôt que dupliquer le code, créez une classe parente générale :
Philémon GLOBLEHI, Développeur Java 83
Les classes enfants héritent avec extends :
LivreAudio et LivrePoche héritent automatiquement de afficher() et modifier(), tout
en ajoutant leurs propres méthodes.
Appeler le constructeur parent
Un constructeur enfant doit initialiser la partie héritée. Utilisez super() comme première
instruction :
Philémon GLOBLEHI, Développeur Java 84
Si vous n'appelez pas explicitement super(), le compilateur ajoute automatiquement super()
(sans paramètre). Si le parent n'a pas de constructeur vide, erreur de compilation.
La racine universelle : Object
Java n'autorise pas l'héritage multiple (une seule classe parente). Toute classe sans extends
hérite automatiquement de Object.
Toutes les classes Java descendent donc d'Object, d'où les méthodes universelles
toString(), equals(), hashCode().
Substituabilité : utiliser les types parents
Une classe enfant peut être affectée à une variable de type parent (upcasting) :
Puisque LivreAudio est un Livre, vous pouvez traiter un livre audio comme un livre générique.
Utile pour écrire du code qui fonctionne avec n'importe quel type de livre.
Toute instance peut même être affectée à Object :
Philémon GLOBLEHI, Développeur Java 85
Downcasting : revenir au type enfant
L'inverse (downcasting) nécessite un cast explicite et peut échouer :
Vérifier le type avec instanceof
Sécurisez vos downcasts avec instanceof :
Le downcasting devrait rester exceptionnel. Si vous l'utilisez partout, repensez votre architecture.
Les portées : contrôler l'accès
Philémon GLOBLEHI, Développeur Java 86
Quatre niveaux de visibilité, du plus ouvert au plus fermé :
public : accessible partout protected : accessible dans le package ET par les classes enfants
(même dans d'autres packages) package (pas de mot-clé) : accessible uniquement dans le
package private : accessible uniquement dans la classe
Pourquoi protected ?
Si un attribut est private, les classes enfants n'y ont pas accès. Utilisez protected quand les
enfants doivent pouvoir lire/modifier l'attribut.
Règle générale : Déclarez tout private par défaut. Passez à protected uniquement si
nécessaire. Ça limite les risques de modification involontaire de l'état interne.
Héritage des membres static
Les méthodes et attributs static sont partagés dans toute la hiérarchie :
Philémon GLOBLEHI, Développeur Java 87
Le compteur est unique pour toute la hiérarchie. Invoquez de préférence via la classe où c'est
déclaré pour la clarté.
Classes final : bloquer l'héritage
Une classe final ne peut pas être étendue :
Cas d'usage rare. Exemple célèbre : String est final, impossible d'en hériter.
La composition : utiliser d'autres objets
La composition signifie qu'une classe contient des instances d'autres classes comme attributs.
Philémon GLOBLEHI, Développeur Java 88
Un livre a des pages. C'est la composition. Plus courante que l'héritage en pratique.
Héritage vs Composition : quand utiliser quoi ?
Héritage : quand une classe est vraiment un type spécialisé d'une autre
● Une voiture est un véhicule
● Un carré est un rectangle (attention aux pièges !)
Composition : quand une classe utilise une autre
● Une voiture a un moteur
● Une voiture a des pneus
● Un garage contient des véhicules
Philémon GLOBLEHI, Développeur Java 89
Préférez la composition quand possible. L'héritage crée un couplage fort. La composition reste
flexible.
Ce qu'il faut retenir
L'héritage modélise une relation "est un" avec extends. Une classe enfant hérite des attributs et
méthodes de son parent. Appelez le constructeur parent avec super(). Toutes les classes
héritent d'Object. Le polymorphisme permet d'affecter un enfant à une variable parent. Utilisez
instanceof avant un downcast. La portée protected rend accessible aux enfants. Les
membres static sont partagés dans toute la hiérarchie. La composition (relation "a un") se fait
via des attributs. Privilégiez la composition sur l'héritage quand les deux sont possibles.
Philémon GLOBLEHI, Développeur Java 90
Le polymorphisme démystifié
Principe fondamental
Le polymorphisme permet aux classes enfants de redéfinir le comportement des méthodes
héritées. Chaque type d'objet peut réagir différemment au même appel de méthode.
Le comportement dépend du type réel de l'objet, pas du type de la variable :
Philémon GLOBLEHI, Développeur Java 91
Même si les deux variables sont de type Employe, chacune exécute la méthode correspondant
à son objet réel.
Redéfinir une méthode : les règles
Pour redéfinir correctement :
Signature identique : même nom, mêmes paramètres Portée égale ou plus large : protected
→ public autorisé, l'inverse interdit Type de retour compatible : peut être un sous-type du
retour original
Élargir la portée
Vous pouvez rendre une méthode plus accessible dans l'enfant :
Utile pour cacher une implémentation au niveau parent tout en laissant l'enfant l'exposer
publiquement si nécessaire.
Philémon GLOBLEHI, Développeur Java 92
Type de retour spécialisé
Le type de retour peut être un sous-type :
Le mot-clé super : appeler le parent
super permet d'accéder à l'implémentation parente :
Limitation : super n'accède qu'au parent direct. Impossible de sauter plusieurs niveaux dans la
hiérarchie.
L'annotation @Override : sécuriser vos redéfinitions
Ajoutez @Override avant toute méthode redéfinie. Le compilateur vérifie qu'elle correspond
bien à une méthode parente :
Philémon GLOBLEHI, Développeur Java 93
Sans @Override, une erreur de nom crée silencieusement une nouvelle méthode au lieu de
redéfinir l'existante. Avec, le compilateur vous alerte immédiatement.
Ce qui ne supporte PAS le polymorphisme
Méthodes private
Les méthodes private ne sont pas héritées, donc pas redéfinissables. Deux méthodes
private de même nom dans parent et enfant sont totalement distinctes.
Méthodes static
Les méthodes static appartiennent à la classe, pas aux instances. Pas de redéfinition possible :
Philémon GLOBLEHI, Développeur Java 94
Appelez toujours les méthodes static via le nom de classe pour éviter la confusion.
Méthodes final : bloquer la redéfinition
Une méthode final ne peut pas être redéfinie :
Rare en pratique. Utilisez final uniquement si le comportement doit absolument rester
identique dans toute la hiérarchie.
Piège : polymorphisme dans les constructeurs
Danger : Appeler une méthode redéfinie dans un constructeur peut causer des bugs difficiles à
tracer.
Philémon GLOBLEHI, Développeur Java 95
Le constructeur parent appelle afficher() avant que l'enfant n'ait initialisé certificat.
Résultat : NullPointerException.
Solution : N'appelez dans les constructeurs que des méthodes private ou final qui ne
peuvent pas être redéfinies.
Masquage des attributs (à éviter)
Vous pouvez déclarer un attribut du même nom qu'un attribut parent, mais ça ne redéfinit rien, ça
masque juste :
Philémon GLOBLEHI, Développeur Java 96
Source de confusion. Évitez absolument cette pratique, utilisez des noms différents.
Le principe du ouvert/fermé
Une classe doit être :
● Ouverte à l'extension : on peut ajouter de nouvelles méthodes par héritage
● Fermée à la modification : on ne doit pas altérer le comportement existant
Comment l'appliquer :
● Attributs en private (inaccessibles aux enfants)
● Méthodes critiques en final (non redéfinissables)
● Redéfinition pour étendre, pas pour casser le contrat parent
Une classe enfant ne devrait jamais redéfinir une méthode d'une façon qui violerait les attentes
des utilisateurs de la classe parent.
Ce qu'il faut retenir
Philémon GLOBLEHI, Développeur Java 97
Le polymorphisme permet aux enfants de modifier le comportement hérité via la redéfinition. Le
comportement exécuté dépend du type réel de l'objet, pas du type de la variable. Utilisez
@Override systématiquement pour sécuriser vos redéfinitions. super accède à
l'implémentation parente. Les méthodes private et static ne supportent pas le
polymorphisme. Les méthodes final bloquent la redéfinition. Évitez d'appeler des méthodes
redéfinissables dans les constructeurs. Ne masquez jamais les attributs parents. Respectez le
principe du ouvert/fermé : étendez sans casser le contrat.
Philémon GLOBLEHI, Développeur Java 98
Classes abstraites : l’art de l’incomplétude
Pourquoi abstraire ?
Parfois, une classe parente représente un concept tellement général qu'il n'a pas de sens de
créer directement ses instances. La classe Document sert à mutualiser le code commun aux PDF
et aux fichiers Word, mais créer un "document générique" sans préciser son type concret n'a pas
vraiment d'utilité.
Les classes abstraites résolvent ce problème : elles définissent un modèle sans pouvoir être
instanciées directement.
Déclarer une classe abstraite
Ajoutez le mot-clé abstract :
Impossible maintenant de créer directement un Document :
Philémon GLOBLEHI, Développeur Java 99
Par contre, les classes enfants concrètes restent instanciables :
Une classe abstraite doit avoir au moins une classe enfant concrète, sinon elle ne sert à rien.
C'est pourquoi abstract et final sont incompatibles (une classe final ne peut pas avoir
d'enfants).
Méthodes abstraites : imposer un contrat
Une méthode abstraite déclare une signature sans corps. Les classes enfants concrètes doivent
obligatoirement l'implémenter.
Les enfants doivent fournir l'implémentation :
Philémon GLOBLEHI, Développeur Java 100
Si une classe concrète hérite d'une méthode abstraite sans la redéfinir : erreur de compilation.
Pourquoi des méthodes abstraites ?
Définir des propriétés variables
Au lieu de stocker nombrePages comme attribut, on le définit comme propriété abstraite.
Chaque type de véhicule retourne sa propre valeur.
Personnaliser un algorithme
Une classe abstraite peut implémenter un algorithme général qui appelle des méthodes
abstraites à des points précis. Les enfants adaptent le comportement en implémentant ces
méthodes.
Philémon GLOBLEHI, Développeur Java 101
Les classes enfants héritent de tout l'algorithme traiter() mais contrôlent ce qui s'affiche via
getContenu() :
Philémon GLOBLEHI, Développeur Java 102
C'est le patron de conception Template Method : l'algorithme est fixe dans le parent, les détails
sont personnalisables par les enfants.
Règles à retenir
Une classe abstraite peut avoir :
● Des méthodes abstraites (sans corps)
● Des méthodes concrètes (avec corps)
● Des attributs
● Des constructeurs (appelés par les enfants via super())
Une classe abstraite ne peut pas :
● Être instanciée directement
● Être final (incompatible avec abstract)
Une méthode abstraite :
● N'a pas de corps ( juste signature + ;)
● Force les enfants concrets à l'implémenter
● Peut avoir n'importe quelle portée sauf private
Une classe enfant abstraite :
● Peut hériter de méthodes abstraites sans les implémenter
Philémon GLOBLEHI, Développeur Java 103
● Reporte l'obligation d'implémentation à ses propres enfants concrets
Exemple complet
Utilisation :
Philémon GLOBLEHI, Développeur Java 104
Ce qu'il faut retenir
Les classes abstraites modélisent des concepts généraux non instanciables directement. Le
mot-clé abstract empêche l'instanciation. Les méthodes abstraites imposent un contrat que les
enfants concrets doivent respecter. Utilisez l'abstraction pour factoriser le code commun tout en
laissant les détails variables aux enfants. Une classe abstraite mêle code réutilisable (méthodes
concrètes) et contrats à respecter (méthodes abstraites). C'est un outil puissant pour créer des
hiérarchies de classes cohérentes et extensibles.
Philémon GLOBLEHI, Développeur Java 105
Object : la mère de toutes les classes
La racine de tout
Java n'autorise que l'héritage simple. Toute classe sans extends hérite automatiquement
d'Object. Résultat : toutes les classes Java descendent d'Object.
Object fournit des méthodes communes à toutes les classes. Certaines méritent d'être
redéfinies pour fonctionner correctement.
Rien ne vous empêche de créer une instance d'Object directement, même si ça n'a pas grand
intérêt :
La méthode equals() : comparer correctement
Règle d'or : N'utilisez jamais == pour comparer des objets. == compare les références, pas le
contenu.
Utilisez toujours equals() :
Philémon GLOBLEHI, Développeur Java 106
Redéfinir equals()
L'implémentation par défaut d'Object compare simplement les références (this == obj).
Souvent insuffisant.
Si votre classe a une notion d'identité (un ID, une désignation...), redéfinissez equals() :
Deux produits sont égaux s'ils ont le même code barre.
La méthode hashCode() : pour les tables de hachage
Philémon GLOBLEHI, Développeur Java 107
Si vous redéfinissez equals(), redéfinissez toujours hashCode() aussi. Sinon, vos objets ne
fonctionneront pas correctement dans les HashMap, HashSet, etc.
Règle : Deux objets égaux (selon equals()) doivent avoir le même hashCode().
Basez-vous sur les mêmes attributs que dans equals().
IntelliJ peut générer ces deux méthodes automatiquement : Generate → equals() and
hashCode().
La méthode toString() : déboguer facilement
toString() retourne une représentation textuelle de votre objet. Utilisée implicitement lors de
la concaténation :
Par défaut, Object retourne quelque chose comme Produit@1a2b3c4d (nom de classe +
hashcode). Peu utile.
Redéfinissez pour plus de clarté :
Résultat :
Philémon GLOBLEHI, Développeur Java 108
Très pratique pour le débogage et les logs.
La méthode clone() : copier des objets
clone() crée une copie d'un objet. Par défaut, elle est protected et lance une exception sauf
si votre classe implémente l'interface Cloneable.
Attention : Le clonage par défaut est superficiel. Si vos attributs sont des objets, seules les
références sont copiées, pas les objets eux-mêmes.
Les tableaux implémentent automatiquement Cloneable :
La méthode getClass() : introspection
getClass() retourne l'objet représentant la classe de l'instance. Utile pour la réflexion :
Philémon GLOBLEHI, Développeur Java 109
Permet d'accéder dynamiquement aux informations de la classe au moment de l'exécution.
La méthode finalize() : avant la destruction
Appelée par le ramasse-miettes avant destruction, mais sans aucune garantie de quand ni
même si elle sera exécutée. Peu fiable, évitez de vous en servir.
Méthodes de concurrence
Object fournit aussi wait(), notify(), notifyAll() pour la programmation concurrente
(threads). Sujet avancé, hors de portée pour débuter.
Ce qu'il faut retenir
Toutes les classes héritent d'Object. N'utilisez jamais == pour comparer des objets, utilisez
equals(). Si vous redéfinissez equals(), redéfinissez aussi hashCode(). Redéfinissez
toString() pour faciliter le débogage. clone() nécessite Cloneable et fait une copie
superficielle par défaut. getClass() permet l'introspection. finalize() est peu fiable,
évitez-la. Ces méthodes forment le socle commun à tous les objets Java.
Philémon GLOBLEHI, Développeur Java 110
String : bien plus qu’un simple texte
Les chaînes de caractères : des objets immuables
En Java, les chaînes de caractères sont des instances de String. Elles s'écrivent entre
guillemets :
Différence importante : Une String n'est pas un tableau. Pour accéder aux caractères, utilisez
charAt() :
Ou convertissez en tableau avec toCharArray() :
Méthodes essentielles
Comparaison
Ne jamais utiliser == pour comparer des String ! Utilisez equals() :
Philémon GLOBLEHI, Développeur Java 111
Ignorer la casse :
Comparaison lexicographique (ordre alphabétique) :
Recherche
Modification (création d'une nouvelle chaîne)
Extraction
Philémon GLOBLEHI, Développeur Java 112
Concaténation
Immutabilité : les String ne changent jamais
Crucial : Une fois créée, une String ne peut pas être modifiée. Toutes les méthodes créent de
nouvelles chaînes :
Cette immutabilité permet :
● De déclarer des constantes fiables (static final String)
● Des optimisations mémoire par la JVM
La JVM réutilise les littéraux identiques :
Philémon GLOBLEHI, Développeur Java 113
StringBuilder : construire efficacement
Pour construire des chaînes par concaténations multiples, utilisez StringBuilder (mutable,
donc plus performant) :
Autres opérations :
D'ailleurs, le compilateur transforme automatiquement les concaténations + en StringBuilder
:
Formatage de texte
[Link]() permet de créer des chaînes formatées :
Philémon GLOBLEHI, Développeur Java 114
Principaux codes de conversion
● %s : chaîne de caractères
● %d : entier décimal
● %f : nombre à virgule
● %c : caractère
● %b : booléen
● %% : le caractère %
● %n : retour à la ligne
Options de formatage
Avec une locale :
Expressions régulières
Les regex permettent de vérifier, chercher et remplacer des motifs complexes.
Vérifier un motif
Philémon GLOBLEHI, Développeur Java 115
Classes de caractères
● . : n'importe quel caractère
● \d : chiffre (0-9)
● \w : caractère de mot (lettres, chiffres, _)
● \s : espace blanc
● [abc] : a, b ou c
● [a-z] : lettre minuscule
● [^0-9] : tout sauf chiffre
Quantificateurs
● ? : 0 ou 1 fois
● * : 0 ou plusieurs fois
● + : 1 ou plusieurs fois
● {n} : exactement n fois
● {n,m} : entre n et m fois
Remplacer avec regex
Découper une chaîne
Philémon GLOBLEHI, Développeur Java 116
Ce qu'il faut retenir
Les String sont immuables, toute modification crée une nouvelle chaîne. Utilisez toujours
equals() pour comparer, jamais ==. Pour des constructions multiples, préférez
StringBuilder. [Link]() simplifie le formatage. Les expressions régulières offrent
une puissance énorme pour la manipulation de texte. La classe String est finale, impossible
d'en hériter. Maîtriser les chaînes de caractères est essentiel, elles sont omniprésentes dans tout
programme Java.
Philémon GLOBLEHI, Développeur Java 117
Gérer l’imprévu : les exceptions
Pourquoi des exceptions ?
Dans un programme, les erreurs sont inévitables : fichier introuvable, division par zéro, connexion
réseau coupée, valeur invalide... Plutôt que de tester manuellement chaque valeur de retour de
chaque méthode, Java utilise les exceptions pour isoler le code de gestion d'erreur du code
métier normal.
Qu'est-ce qu'une exception ?
Une exception est une classe héritant d'Exception qui représente un état anormal. Son nom
doit se terminer par Exception.
Exemples fournis par Java :
● NullPointerException : tentative d'utiliser une référence null
● NumberFormatException : impossible de convertir une chaîne en nombre
● IndexOutOfBoundsException : accès hors limites d'un tableau
Créer votre propre exception
Une exception peut stocker des informations utiles :
Philémon GLOBLEHI, Développeur Java 118
Lancer une exception : throw
Quand vous détectez un problème, jetez une exception avec throw :
Jeter une exception interrompt immédiatement l'exécution de la méthode.
Attraper une exception : try-catch
Pour gérer une exception, utilisez try-catch :
Philémon GLOBLEHI, Développeur Java 119
Le bloc try contient le code susceptible de lancer une exception. Le bloc catch définit
comment réagir si cette exception survient.
Plusieurs catch
Vous pouvez attraper différents types d'exceptions :
Ordre important : Les exceptions les plus spécifiques d'abord, les plus générales ensuite.
Attraper plusieurs types identiquement
Si le traitement est identique pour plusieurs exceptions, utilisez | :
Philémon GLOBLEHI, Développeur Java 120
Propager une exception : throws
Si une méthode peut lancer une exception mais ne la gère pas, elle doit le déclarer avec throws
:
L'exception remonte alors automatiquement à l'appelant, qui devra lui-même la gérer ou la
propager.
Le bloc finally : toujours exécuté
Un bloc finally s'exécute toujours, qu'il y ait eu exception ou non :
Philémon GLOBLEHI, Développeur Java 121
Même si le try contient un return, le finally s'exécute avant la sortie de la méthode.
Try-with-resources : fermer automatiquement
Pour les ressources (fichiers, connexions...), Java offre une syntaxe plus simple :
Le compilateur génère automatiquement un finally qui ferme la ressource. Marche avec tout
type implémentant AutoCloseable ou Closeable.
Plusieurs ressources :
Hiérarchie d'exceptions
Philémon GLOBLEHI, Développeur Java 122
Vous pouvez créer des hiérarchies pour grouper les exceptions :
Ensuite, attraper au niveau souhaité :
Encapsuler une exception : la cause
Une exception peut en encapsuler une autre :
Récupérer la cause :
Philémon GLOBLEHI, Développeur Java 123
Checked vs Unchecked
Checked exceptions (Exception)
Héritent d'Exception (sauf RuntimeException). Le compilateur force leur gestion ou
déclaration.
Utilisez-les pour les erreurs métier que l'application peut gérer (solde insuffisant, fichier
manquant...).
Unchecked exceptions (RuntimeException)
Héritent de RuntimeException ou Error. Pas besoin de les déclarer.
Utilisez-les pour les erreurs techniques ou les bugs (paramètre invalide, état incohérent...).
Exemples fournis par Java :
● NullPointerException : référence null
● IllegalArgumentException : paramètre invalide
Philémon GLOBLEHI, Développeur Java 124
● ArithmeticException : division par zéro
● ClassCastException : cast invalide
Error : n'y touchez pas
Error et ses enfants (OutOfMemoryError, StackOverflowError...) signalent des
problèmes graves de la JVM. Ne les créez jamais, ne les attrapez généralement pas.
Polymorphisme et exceptions
Une méthode redéfinie peut :
● Ne pas déclarer d'exception (OK)
● Déclarer des exceptions plus spécifiques (OK)
● Jamais déclarer d'exceptions nouvelles ou plus larges (interdit)
Respecte le principe de substitution de Liskov.
Afficher la pile d'appel
Pour déboguer, affichez la trace complète :
Philémon GLOBLEHI, Développeur Java 125
Ce qu'il faut retenir
Les exceptions séparent le code métier de la gestion d'erreur. Lancez avec throw, attrapez avec
try-catch. Déclarez les checked exceptions avec throws. Utilisez finally ou
try-with-resources pour libérer les ressources. Les checked exceptions (Exception) forcent la
gestion, les unchecked (RuntimeException) signalent des bugs ou erreurs techniques. Ne
créez jamais d'Error. Respectez la hiérarchie dans les catch : du plus spécifique au plus
général. Les exceptions rendent le code plus robuste et lisible qu'un simple retour de code
d'erreur.
Philémon GLOBLEHI, Développeur Java 126
Les énumérations : quand les constantes
deviennent intelligentes
Le problème des constantes
Avant les énumérations, on utilisait des constantes entières pour représenter des ensembles finis
de valeurs :
Problèmes :
● Aucun typage réel (n'importe quel int est accepté)
● Pas de liste énumérable des valeurs
● Comparaisons peu sûres
Les énumérations : la solution
Une énumération définit un type avec un ensemble fini de valeurs possibles :
Convention : noms en majuscules, mots séparés par _.
Philémon GLOBLEHI, Développeur Java 127
Fichier : [Link] (comme une classe).
Utiliser une énumération
Une variable d'énumération peut valoir null.
Énumérations internes
Vous pouvez définir une énumération dans une classe :
Méthodes fournies
Philémon GLOBLEHI, Développeur Java 128
Méthodes de classe
valueOf(String) : convertit une chaîne en énumération
values() : retourne un tableau de tous les éléments
Méthodes d'instance
name() : retourne le nom en chaîne
ordinal() : retourne la position (commence à 0)
compareTo() : compare selon l'ordre de déclaration
Comparer des énumérations
Philémon GLOBLEHI, Développeur Java 129
Utilisez == directement (recommandé) :
Chaque élément d'énumération n'existe qu'une seule fois en mémoire, donc == fonctionne
parfaitement.
Énumérations dans un switch
Pas besoin de préfixer par Priorite., le compilateur comprend.
Ce qui se passe en coulisses
Une énumération est transformée en classe finale par le compilateur :
Philémon GLOBLEHI, Développeur Java 130
Devient (conceptuellement) :
Conséquences :
● Les valeurs sont des instances de l'énumération elle-même
● Le constructeur est privé (impossible de créer de nouvelles instances)
● La classe est finale (impossible d'hériter d'une énumération)
● Hérite d'Enum
Ajouter méthodes et attributs
Une énumération peut contenir des méthodes et attributs comme une classe normale :
Philémon GLOBLEHI, Développeur Java 131
Utilisation :
Constructeurs personnalisés
Ajoutez un constructeur (obligatoirement private) pour enrichir chaque élément :
Philémon GLOBLEHI, Développeur Java 132
Utilisation :
Exemple concret
Philémon GLOBLEHI, Développeur Java 133
Ce qu'il faut retenir
Les énumérations créent des types avec un ensemble fini de valeurs. Elles sont plus sûres que
des constantes entières car fortement typées. Utilisez == pour comparer. Les méthodes
values() et valueOf() facilitent la manipulation. Les énumérations peuvent contenir attributs,
méthodes et constructeurs. Elles sont finales et ne peuvent pas être étendues. Chaque valeur
n'existe qu'une fois en mémoire. Utilisez-les pour modéliser des états, des niveaux, des
catégories... tout ensemble fermé et connu à l'avance.
Philémon GLOBLEHI, Développeur Java 134
Les interfaces : des contrats en code
Qu'est-ce qu'une interface ?
Une interface définit un contrat : un ensemble de méthodes qu'une classe s'engage à
implémenter. Elle crée une abstraction pure, sans aucun détail d'implémentation (ou presque).
Les interfaces permettent un découplage maximal : le code client utilise l'interface sans connaître
l'implémentation concrète.
Déclarer une interface
Comme une classe, une interface a :
● Une portée (public généralement)
● Un nom (dans son propre fichier [Link])
● Un bloc de déclaration
Important : Les méthodes d'interface sont public et abstract par défaut. Pas besoin de le
préciser.
Implémenter une interface
Une classe utilise implements pour indiquer qu'elle respecte le contrat :
Philémon GLOBLEHI, Développeur Java 135
Une classe concrète doit implémenter toutes les méthodes de l'interface (sauf si elle est
abstraite).
Plusieurs interfaces
Une classe peut implémenter autant d'interfaces que nécessaire :
Philémon GLOBLEHI, Développeur Java 136
C'est une forme d'héritage multiple, mais sans les problèmes de l'héritage classique.
Utiliser le type interface
L'intérêt principal : manipuler des objets via leur interface, pas leur classe concrète :
Ça permet de changer d'implémentation sans toucher au code client.
Constantes dans les interfaces
Les attributs d'interface sont automatiquement public static final :
Philémon GLOBLEHI, Développeur Java 137
Méthodes static
Les interfaces peuvent avoir des méthodes static avec implémentation :
Appel direct via l'interface :
Héritage d'interfaces
Philémon GLOBLEHI, Développeur Java 138
Une interface peut hériter d'autres interfaces (même plusieurs) :
Une classe implémentant Transactionnel doit implémenter les méthodes de Creditable ET
Debitable.
Interfaces marqueurs
Une interface vide qui sert juste à marquer un type :
Utilisée avec instanceof pour adapter le comportement :
Exemple célèbre : Cloneable en Java.
Philémon GLOBLEHI, Développeur Java 139
Implémentations par défaut (depuis Java 8)
Une interface peut fournir une implémentation par défaut avec default :
Les classes n'ont pas besoin de redéfinir transferer(), mais peuvent le faire si nécessaire.
Utile pour faire évoluer une interface sans casser le code existant.
Attention : À utiliser avec parcimonie. Les interfaces doivent rester principalement des contrats,
pas des classes déguisées.
Ségrégation d'interface
Principe : ne forcez pas un client à dépendre de méthodes qu'il n'utilise pas. Préférez plusieurs
petites interfaces spécialisées qu'une grosse interface fourre-tout.
Philémon GLOBLEHI, Développeur Java 140
Un système de consultation n'a besoin que de Consultation, pas de tout le reste.
Inversion de dépendance
Principe : dépendez d'abstractions (interfaces), pas d'implémentations concrètes.
Philémon GLOBLEHI, Développeur Java 141
Maintenant, Commande fonctionne avec n'importe quelle implémentation de Database (MySQL,
PostgreSQL, fichiers, mémoire...).
Interface vs Classe abstraite
Quand utiliser quoi ?
Interface :
● Définir un contrat de comportement
● Implémenter plusieurs "rôles" différents
● Découpler totalement l'implémentation
● Relation "peut faire" ou "se comporte comme"
Classe abstraite :
● Mutualiser du code commun
● Créer une hiérarchie de types liés
● Partager un état (attributs) commun
● Relation "est un type de"
Exemple : ArrayList est une AbstractList (héritage) mais se comporte comme une List
(interface).
Ce qu'il faut retenir
Les interfaces définissent des contrats sans imposer d'implémentation. Une classe peut
implémenter plusieurs interfaces (héritage multiple partiel). Les méthodes sont public
Philémon GLOBLEHI, Développeur Java 142
abstract par défaut. Les attributs sont public static final (constantes). Les méthodes
default permettent d'évoluer sans casser le code existant. Préférez plusieurs petites interfaces
spécialisées. L'inversion de dépendance découple le code via les interfaces. Utilisez les
interfaces pour définir "ce qu'un objet peut faire", les classes abstraites pour "ce qu'un objet est".
Les interfaces sont au cœur de la conception d'applications flexibles et évolutives.
Philémon GLOBLEHI, Développeur Java 143
Génériques : écrire du code réutilisable
Le problème sans génériques
Avant les génériques, les collections pouvaient contenir n'importe quel type d'objet, ce qui
causait des erreurs à l'exécution :
Impossible de détecter les erreurs à la compilation. Nécessite des casts partout.
Les génériques : typage à la compilation
Les génériques permettent de spécifier le type contenu dans une collection :
Le compilateur vérifie la cohérence des types. Plus sûr, plus lisible.
Notation en diamant (depuis Java 7)
Évitez la répétition avec <> :
Philémon GLOBLEHI, Développeur Java 144
Le compilateur devine le type à partir de la gauche.
Plusieurs types paramétrés
Certaines classes acceptent plusieurs types génériques :
Le premier type est la clé (String), le second est la valeur (Integer).
Substitution et génériques
Attention piège : même si String hérite d'Object, List<String> n'hérite pas de
List<Object> !
Pourquoi ? Sinon, on pourrait ajouter n'importe quoi :
Types bornés : contrôler la substitution
Borne supérieure : extends
Philémon GLOBLEHI, Développeur Java 145
Pour une liste en lecture seule (consultation) :
? extends CompteBancaire signifie "n'importe quel type qui hérite de CompteBancaire". On
peut lire, mais pas écrire (le compilateur ne connaît pas le type exact).
Borne inférieure : super
Pour une liste en écriture seule (ajout) :
? super CompteEpargne signifie "n'importe quel type parent de CompteEpargne". On peut
écrire, mais pas lire avec le type précis.
Joker sans borne : ?
Aucune information de type :
Très limité, rarement utilisé seul.
Méthodes génériques
Philémon GLOBLEHI, Développeur Java 146
Une méthode peut définir ses propres types paramétrés :
Le <T extends CompteBancaire> avant le type de retour déclare le type générique. La
méthode fonctionne avec n'importe quelle liste de comptes bancaires :
Le compilateur infère T à partir du paramètre passé.
Classes génériques
Une classe entière peut être générique :
Philémon GLOBLEHI, Développeur Java 147
Utilisation :
Avec bornes :
Conventions de nommage
● T : type générique général
Philémon GLOBLEHI, Développeur Java 148
● E : élément (dans les collections)
● K : clé (Key)
● V : valeur (Value)
● U, V, W : plusieurs types génériques
Limitations importantes
Pas de types primitifs : uniquement des classes
Pas d'instanceof avec type paramétré :
Pas d'instanciation du type générique :
Pas d'attributs static génériques :
Pas de tableaux de types paramétrés :
Philémon GLOBLEHI, Développeur Java 149
Pas de catch avec type générique :
Pas de surcharge juste sur le type paramétré :
Effacement de type (type erasure)
À la compilation, les types génériques sont effacés et remplacés par leur borne (ou Object si
aucune borne) :
Devient en bytecode :
C'est pourquoi les génériques ont ces limitations : le type exact n'existe plus à l'exécution.
Philémon GLOBLEHI, Développeur Java 150
Ce qu'il faut retenir
Les génériques apportent la sécurité du typage à la compilation. Utilisez <Type> pour
paramétrer les collections. ? extends T pour lire, ? super T pour écrire. Les méthodes et
classes peuvent être génériques. Conventions : T, E, K, V. Les types primitifs ne fonctionnent pas,
utilisez les classes enveloppes. L'effacement de type explique les limitations. Les génériques
rendent le code plus sûr et plus expressif sans sacrifier la flexibilité.
Philémon GLOBLEHI, Développeur Java 151
Collections : gérer tes données comme un
pro
Au-delà des tableaux
Les tableaux ont une limite majeure : taille fixe. Impossible d'ajouter ou retirer des éléments. Les
collections du Java Collections Framework (package [Link]) offrent des structures de
données flexibles et puissantes.
Trois grandes familles :
● Listes (List) : collections ordonnées, doublons autorisés
● Ensembles (Set) : pas de doublons
● Tableaux associatifs (Map) : paires clé-valeur
Toutes les collections sont génériques et ne fonctionnent qu'avec des objets (utilisez Integer
au lieu d'int, par exemple).
Les listes (List)
ArrayList : accès rapide
Optimisée pour la lecture par index, moins pour l'insertion/suppression.
Philémon GLOBLEHI, Développeur Java 152
Réserver de l'espace pour optimiser :
LinkedList : insertion/suppression rapides
Liste doublement chaînée, lente pour l'accès aléatoire.
Utilisable aussi comme file ou pile :
Philémon GLOBLEHI, Développeur Java 153
ArrayDeque : files et piles performantes
Plus rapide que LinkedList pour les opérations FIFO/LIFO, mais n'implémente pas List.
PriorityQueue : file avec priorité
Les éléments sortent dans l'ordre naturel (tri automatique).
Interfaces des listes
● Iterable : parcours avec for amélioré
● Collection : méthodes de base (add, remove, size, toArray...)
● List : accès par index, tri
● Queue : file (FIFO)
Philémon GLOBLEHI, Développeur Java 154
● Deque : file double (FIFO/LIFO)
Principe : Utilisez le type d'interface le plus restrictif adapté à vos besoins.
Les ensembles (Set)
Pas de doublons. Deux éléments sont identiques si [Link](e2) retourne true.
HashSet : rapide, sans ordre
Basé sur le code de hachage (hashCode()). Très performant.
Ordre de parcours imprévisible.
TreeSet : trié automatiquement
Maintient un ordre naturel (les éléments doivent implémenter Comparable ou fournir un
Comparator).
Philémon GLOBLEHI, Développeur Java 155
Moins performant que HashSet pour ajout/suppression.
LinkedHashSet : ordre d'insertion préservé
Compromis entre HashSet (performance) et TreeSet (ordre). Conserve l'ordre d'insertion.
Interfaces des ensembles
● Set : ensemble sans doublon
● SortedSet : ordre naturel maintenu
● NavigableSet : navigation avancée
Les tableaux associatifs (Map)
Associent une clé unique à une valeur.
HashMap : rapide, sans ordre
Basé sur le code de hachage. Très performant.
Philémon GLOBLEHI, Développeur Java 156
Ordre de parcours imprévisible.
TreeMap : clés triées
Maintient les clés dans l'ordre naturel.
LinkedHashMap : ordre d'insertion
Conserve l'ordre d'ajout des clés.
Philémon GLOBLEHI, Développeur Java 157
Interfaces des maps
● Map : tableau associatif de base
● SortedMap : clés en ordre naturel
● NavigableMap : navigation avancée
Convertir collection → tableau
La classe utilitaire Collections
Méthodes statiques pratiques :
Collections vides immutables :
Philémon GLOBLEHI, Développeur Java 158
Ce qu'il faut retenir
Les collections remplacent avantageusement les tableaux pour les besoins courants.
ArrayList pour l'accès rapide, LinkedList pour l'insertion/suppression. HashSet pour des
ensembles performants, TreeSet pour un ordre naturel. HashMap pour des associations
clé-valeur rapides, TreeMap pour des clés triées. Utilisez toujours le type d'interface approprié
(List, Set, Map) plutôt que les classes concrètes. Les collections ne fonctionnent qu'avec des
objets, utilisez les classes enveloppes pour les primitifs. Maîtriser les collections est essentiel en
Java moderne.
Philémon GLOBLEHI, Développeur Java 159
Lire et écrire : les entrées/sorties
Flux de données : le concept de base
En Java, toute opération d'entrée/sortie passe par des flux (streams). Un flux est un canal qui
transporte des données. Quatre classes abstraites fondamentales :
● InputStream : lecture de données binaires
● OutputStream : écriture de données binaires
● Reader : lecture de caractères (texte)
● Writer : écriture de caractères (texte)
Flux binaires en lecture : InputStream
ByteArrayInputStream : lire depuis la mémoire
FileInputStream : lire un fichier
Philémon GLOBLEHI, Développeur Java 160
Important : Utilisez try-with-resources pour fermer automatiquement les flux et libérer les
ressources système.
Flux binaires en écriture : OutputStream
FileOutputStream : écrire dans un fichier
Flux de caractères : Reader et Writer
FileReader et FileWriter : fichiers texte
Limitation : encodage par défaut du système, pas configurable.
Décorateurs : ajouter des fonctionnalités
Les décorateurs enrichissent les flux avec des capacités supplémentaires.
BufferedReader/BufferedWriter : zone tampon
Philémon GLOBLEHI, Développeur Java 161
Améliore les performances en regroupant les lectures/écritures :
InputStreamReader/OutputStreamWriter : encodage
Convertit entre flux binaires et caractères avec un encodage spécifié :
Chaîner les décorateurs
Un seul close() sur le décorateur final ferme toute la chaîne.
Philémon GLOBLEHI, Développeur Java 162
La classe Scanner : lecture facilitée
Pour lire et valider des données depuis la console ou un fichier :
Avec validation :
Gestion moderne des fichiers : Path et Files
Path : représenter un chemin
Philémon GLOBLEHI, Développeur Java 163
Files : opérations simplifiées
Files est pratique pour les petits fichiers. Pour les gros fichiers, préférez les flux pour
économiser la mémoire.
Accès réseau : URL et URLConnection
Philémon GLOBLEHI, Développeur Java 164
Même abstraction que pour les fichiers !
Sérialisation : sauvegarder des objets
Convertir des objets en flux binaires et vice-versa.
Rendre une classe sérialisable
Sérialiser (sauvegarder)
Désérialiser (charger)
Philémon GLOBLEHI, Développeur Java 165
Données transient : exclure de la sérialisation
Limites de la sérialisation
● Format propre à Java (pas pour échanger avec d'autres langages)
● Très dépendant de la structure des classes
● Classes doivent être disponibles à la désérialisation
● Modifications de classe risquent de casser la compatibilité
Le serialVersionUID permet de gérer les versions de classes. Changez-le quand les
modifications cassent la compatibilité.
Console : [Link], [Link], [Link]
Flux spéciaux ouverts au démarrage de l'application :
● [Link] : InputStream de la console (entrée standard)
● [Link] : PrintStream de la console (sortie standard)
● [Link] : PrintStream pour les erreurs
Ces flux n'ont pas besoin d'être fermés explicitement.
Ce qu'il faut retenir
Les flux (InputStream, OutputStream, Reader, Writer) sont l'abstraction centrale des I/O
en Java. Utilisez try-with-resources pour fermer automatiquement les flux. Les décorateurs
enrichissent les flux (buffer, encodage, comptage...). Path et Files simplifient les opérations sur
fichiers. La sérialisation permet de sauvegarder des objets, mais a des limites. Même API pour
fichiers, mémoire, réseau : c'est la force de l'abstraction Java. Préférez les flux pour les gros
volumes, Files pour les petits fichiers.
Philémon GLOBLEHI, Développeur Java 166
Manipuler le temps : les dates en Java
Historique complexe
Java a connu plusieurs générations d'API pour les dates, créant une situation confuse avec trois
approches coexistantes.
Date : l'ancêtre obsolète
La classe [Link] est la première API, aujourd'hui largement dépréciée.
Limitations majeures :
● Pas de dates avant 1900
● Pas de fuseaux horaires
● Calendrier grégorien uniquement
● Pas d'opérations (ajouter des jours, etc.)
Pourtant, elle reste utilisée pour des raisons de compatibilité (notamment avec SQL et certaines
bibliothèques).
Construire une Date à partir de millisecondes depuis l'epoch (1er janvier 1970) :
Philémon GLOBLEHI, Développeur Java 167
Calendar : l'amélioration partielle
Introduit en Java 1.1 pour remplacer Date, mais restant fastidieux à utiliser.
Avantages :
● Toutes les dates possibles (même avant notre ère)
● Support des fuseaux horaires
● Opérations sur les dates
Limitations :
● API lourde et complexe
● Classe abstraite (pas de new Calendar())
● Pas de notion de durée
L'API Date/Time moderne (Java 8+)
Philémon GLOBLEHI, Développeur Java 168
Depuis Java 8, l'API [Link] est la solution recommandée. Toutes les classes sont
immutables et thread-safe.
Dates locales (sans fuseau horaire)
LocalDate : uniquement une date
LocalTime : uniquement une heure
LocalDateTime : date et heure
Conversion entre types :
Avec fuseaux horaires :
Philémon GLOBLEHI, Développeur Java 169
Pour des dates avec fuseau horaire : utilisez ZonedDateTime.
Années et mois
Year et YearMonth pour manipulations avancées :
Instant : point dans le temps
Pour les traitements informatiques (horodatage, mesures) :
Périodes et durées
Period : différence en années/mois/jours
Philémon GLOBLEHI, Développeur Java 170
Duration : différence en heures/minutes/secondes
Formatage des dates
DateTimeFormatter pour afficher les dates :
Lecture (parsing) de dates
Convertir une chaîne en date :
Philémon GLOBLEHI, Développeur Java 171
Codes de format courants
● dd : jour sur 2 chiffres
● MM : mois sur 2 chiffres
● yyyy : année sur 4 chiffres
● HH : heure (24h) sur 2 chiffres
● mm : minutes sur 2 chiffres
● ss : secondes sur 2 chiffres
● MMMM : nom du mois en lettres
Exemple : calculer un âge
Ce qu'il faut retenir
Utilisez toujours l'API [Link] (Java 8+) pour du nouveau code. LocalDate, LocalTime,
LocalDateTime pour les dates sans fuseau. Instant pour les horodatages techniques.
Period pour durées en jours/mois/ans, Duration pour heures/minutes/secondes.
DateTimeFormatter pour formater et parser. Toutes les classes sont immutables et
thread-safe. Évitez Date et Calendar sauf obligation de compatibilité. L'API moderne est plus
simple, plus puissante et plus sûre.
Philémon GLOBLEHI, Développeur Java 172
Penser fonctionnel : lambas et streams
La programmation fonctionnelle en Java
Depuis Java 8, le langage intègre des éléments de programmation fonctionnelle, un paradigme
basé sur l'appel et la composition de fonctions.
Avantages :
● Fonctions comme entités de première classe (passables en paramètre)
● Code plus concis et lisible
● Moins de structures de contrôle imbriquées (if, for)
● Traitement découpé en processus simples
Limitation : Java ne supporte pas nativement les fonctions. Les lambdas sont un "sucre
syntaxique" qui cache en réalité des interfaces.
Les lambdas : fonctions anonymes
Une lambda est une fonction sans nom, déclarée à la volée.
Syntaxe
(paramètres) -> { corps }
Simplifications possibles :
● Parenthèses optionnelles si un seul paramètre
● Types inférés par le compilateur
● Accolades optionnelles si une seule instruction
● return implicite si une seule expression
Exemples
Somme de deux nombres :
Philémon GLOBLEHI, Développeur Java 173
Affichage :
Constante :
Utilisation pratique
Trier une liste avec un critère personnalisé :
Appliquer une action sur chaque élément :
Comparé au style impératif classique :
Philémon GLOBLEHI, Développeur Java 174
Closures : capturer l'environnement
Une lambda capture les variables de son contexte (closure).
Restriction : Les variables capturées sont implicitement final (non modifiables).
Interfaces fonctionnelles
Une interface fonctionnelle ne déclare qu'une seule méthode abstraite. Elle peut être
implémentée par une lambda.
Créer une interface fonctionnelle
Philémon GLOBLEHI, Développeur Java 175
L'annotation @FunctionalInterface force la vérification par le compilateur.
Utiliser avec une lambda
Passer en paramètre
Interfaces fonctionnelles standards : [Link]
Le JDK fournit des interfaces prêtes à l'emploi :
Function : transformation
Prend un paramètre, retourne un résultat.
Philémon GLOBLEHI, Développeur Java 176
Operator : opération sur même type
Prend un ou plusieurs paramètres du même type, retourne le même type.
Consumer : consommateur
Prend un paramètre, ne retourne rien.
Supplier : fournisseur
Aucun paramètre, retourne un résultat.
Predicate : test booléen
Prend un paramètre, retourne un booléen.
Philémon GLOBLEHI, Développeur Java 177
Références de méthodes : l'opérateur ::
Alternative plus lisible aux lambdas quand une méthode existe déjà.
Référence de méthode statique
Équivalent à : s -> [Link]()
Référence de méthode d'instance
Équivalent à : e -> [Link](e)
Référence de constructeur
Avec paramètres :
La classe Optional : gérer l'absence de valeur
Philémon GLOBLEHI, Développeur Java 178
Alternative à null pour représenter une valeur potentiellement absente.
Création
Style impératif (à éviter)
Style fonctionnel (recommandé)
Chaînage :
Philémon GLOBLEHI, Développeur Java 179
Exemple complet : régulateur de vitesse
Utilisation :
Ce qu'il faut retenir
Les lambdas sont des fonctions anonymes : (params) -> expression. Une interface
fonctionnelle n'a qu'une seule méthode abstraite et peut être implémentée par une lambda.
[Link] fournit les interfaces courantes (Function, Consumer, Supplier,
Philémon GLOBLEHI, Développeur Java 180
Predicate). L'opérateur :: référence des méthodes existantes pour plus de lisibilité. Les
lambdas capturent leur environnement (closure), mais ne peuvent modifier les variables
capturées. Optional remplace null dans un style fonctionnel. La programmation fonctionnelle
favorise la composition, réduit les structures de contrôle et améliore la lisibilité. C'est un
changement de paradigme qui rend Java plus expressif et concis.
Philémon GLOBLEHI, Développeur Java 181
Les Streams : traiter les données en flux
Qu'est-ce qu'un stream ?
Un stream (flux) est une séquence d'éléments sur laquelle on applique des opérations de
manière fonctionnelle, introduit en Java 8.
Avantages majeurs :
● Traiter des séquences sans boucles explicites
● Code plus lisible et concis
● Traitement en flux : faible empreinte mémoire
● Parallélisation facile pour exploiter les multicœurs
Important : Un stream ne stocke pas les données, il les traite à la volée.
Créer des streams
Depuis un builder
Streams de primitifs
Les valeurs ne créent pas tous les nombres en mémoire, c'est un flux !
Philémon GLOBLEHI, Développeur Java 182
Stream infini
Depuis un tableau
Depuis une collection
Depuis un fichier
Opérations de base : forEach
Exécuter une action sur chaque élément :
Réduction : obtenir un résultat unique
Opérations mathématiques
Philémon GLOBLEHI, Développeur Java 183
Réduction personnalisée
Collecte : créer une nouvelle collection
Vers une liste
Philémon GLOBLEHI, Développeur Java 184
Vers un ensemble (Set)
Grouper par critère (Map)
Joindre en chaîne
Filtrage : éliminer des éléments
Philémon GLOBLEHI, Développeur Java 185
Chaîner les filtres :
Avec limite :
Mapping : transformer les éléments
Changer le type ou la valeur des éléments :
Philémon GLOBLEHI, Développeur Java 186
Vers types primitifs :
Enchaînement d'opérations
La puissance des streams vient du chaînage :
Philémon GLOBLEHI, Développeur Java 187
Parallélisme : exploiter les multicœurs
Exécuter en parallèle sur plusieurs processeurs :
Attention : Les opérations doivent être indépendantes de l'ordre.
Autres opérations utiles
distinct : supprimer les doublons
Philémon GLOBLEHI, Développeur Java 188
sorted : trier
skip et limit : pagination
anyMatch, allMatch, noneMatch
Exemples pratiques
Philémon GLOBLEHI, Développeur Java 189
Compter les lettres dans une chaîne
Lire et traiter un fichier CSV
Philémon GLOBLEHI, Développeur Java 190
Ce qu'il faut retenir
Les streams traitent des séquences sans boucles explicites. Créez-les depuis collections,
tableaux, plages, fichiers. Les opérations intermédiaires (filter, map, sorted) se chaînent. Les
opérations terminales (collect, forEach, reduce) produisent le résultat. Les streams sont
paresseux : rien ne s'exécute tant qu'il n'y a pas d'opération terminale. parallelStream()
active le traitement parallèle. Les streams limitent l'empreinte mémoire en traitant à la volée.
Combinez filter, map, collect pour des traitements puissants et lisibles. Les streams
incarnent la programmation fonctionnelle en Java.
Philémon GLOBLEHI, Développeur Java 191
Les classes internes : déclarer des classes
dans des classes
Qu'est-ce qu'une classe interne ?
Une classe interne (inner class) est déclarée à l'intérieur d'une autre classe, plutôt que dans son
propre fichier.
Trois types principaux : classes internes statiques, classes internes, classes anonymes.
Classes internes statiques
Déclarées avec static, elles appartiennent à l'espace de noms de la classe englobante.
Caractéristiques
● Nom complet inclut la classe englobante : [Link]
● Partage l'espace privé avec la classe englobante (accès mutuel aux membres privés)
● N'a accès qu'aux membres statiques de la classe englobante
● Sert à regrouper des petites classes utilitaires
Philémon GLOBLEHI, Développeur Java 192
Exemple pratique : Comparateur
Utilisation :
Notez que Comparateur peut accéder aux champs privés nom et prenom des objets
Personne.
Philémon GLOBLEHI, Développeur Java 193
Classes internes (non statiques)
Sans static, elles maintiennent une référence vers l'instance englobante.
Caractéristiques
● Ne peuvent être créées que depuis une instance de la classe englobante
● Ont accès à tous les membres (même non statiques) de l'instance englobante
● Créent un couplage fort entre les deux objets
● Couramment utilisées pour les interfaces graphiques
Exemple : Interface graphique
Philémon GLOBLEHI, Développeur Java 194
Les classes ActionIncrementer et ActionDecrementer ont accès aux méthodes privées et
attributs de l'instance Compteur qui les a créées.
Classes anonymes
Classe sans nom, déclarée et instanciée en une seule fois.
Philémon GLOBLEHI, Développeur Java 195
Syntaxe
Exemple : Logger
Philémon GLOBLEHI, Développeur Java 196
Utilisation :
Capture de variables
Philémon GLOBLEHI, Développeur Java 197
Les classes anonymes capturent les variables et paramètres locaux, mais ne peuvent pas les
modifier.
Avant Java 8, final était obligatoire. Maintenant c'est implicite ("effectively final").
Limitations
● Pas de constructeur (la classe n'a pas de nom)
● Ne peut étendre qu'une classe avec constructeur sans paramètre
● Pas de variables de type "classe anonyme"
Accès à la classe englobante : [Link]
Quand il y a collision de noms, référencez explicitement la classe englobante :
Philémon GLOBLEHI, Développeur Java 198
Classe locale (dans une méthode)
Déclarer une classe à l'intérieur d'une méthode :
Avantages sur classe anonyme :
● Possibilité de déclarer des constructeurs
● Nom explicite (meilleur pour le débogage)
Portée : uniquement visible dans la méthode.
Interfaces et énumérations internes
Possibles et toujours implicitement static :
Philémon GLOBLEHI, Développeur Java 199
Utilisation :
Quand utiliser quoi ?
Classe interne statique :
● Petite classe utilitaire liée conceptuellement à la classe englobante
● Pas besoin d'accès aux instances de la classe englobante
Classe interne (non statique) :
● Couplage fort avec une instance de la classe englobante
● Besoin d'accéder aux membres non statiques
● Interfaces graphiques, listeners, callbacks
Classe anonyme :
● Implémentation unique et simple d'une interface
● Code court (quelques lignes)
● Depuis Java 8, souvent remplacée par des lambdas
Classe locale :
Philémon GLOBLEHI, Développeur Java 200
● Implémentation plus complexe qu'une lambda
● Besoin d'un nom explicite ou de constructeurs
● Pas réutilisée ailleurs
Ce qu'il faut retenir
Les classes internes organisent le code en regroupant des classes liées. Les classes internes
statiques n'ont pas de lien avec une instance englobante. Les classes internes (non statiques)
maintiennent une référence vers l'instance qui les a créées. Les classes anonymes sont pratiques
pour des implémentations uniques. Les variables capturées sont implicitement final.
[Link] référence l'instance englobante. Depuis Java 8, préférez les
lambdas aux classes anonymes pour les interfaces fonctionnelles. Les classes internes partagent
l'espace privé avec leur classe englobante.
Philémon GLOBLEHI, Développeur Java 201
Les nouveautés de Java 11 à Java 25
Introduction
Java a beaucoup évolué depuis la version 8. Avec un rythme de release semestriel depuis Java
9, le langage s'enrichit régulièrement de nouvelles fonctionnalités qui le rendent plus moderne,
plus concis et plus performant. Dans cette section, on va explorer ensemble les ajouts les plus
marquants de Java 11 à Java 25.
Pas de panique si vous débutez : ces fonctionnalités sont des extensions de ce que vous
connaissez déjà. Vous n'êtes pas obligé de toutes les maîtriser immédiatement. L'objectif est de
vous donner un aperçu de ce qui existe, pour que vous sachiez où chercher quand vous en
aurez besoin.
Java 11 (LTS - 2018)
Java 11 est une version Long Term Support, encore largement utilisée en production aujourd'hui.
var dans les lambdas
Depuis Java 10, var permet l'inférence de type pour les variables locales. Java 11 l'étend aux
paramètres des lambdas :
Pourquoi c'est utile ? Ça permet d'ajouter des annotations aux paramètres lambda sans répéter
le type :
Philémon GLOBLEHI, Développeur Java 202
Méthodes enrichies pour String
Java 11 ajoute des méthodes pratiques à la classe String :
En pratique : Ces méthodes simplifient énormément le traitement de texte et évitent des
bibliothèques externes.
Exécution directe de fichiers source
Vous pouvez maintenant lancer un fichier .java directement sans compilation préalable :
Philémon GLOBLEHI, Développeur Java 203
Limitation : Ne marche que pour un fichier unique. Parfait pour des scripts ou du prototypage
rapide.
Client HTTP moderne
L'ancien HttpURLConnection était verbeux et peu pratique. Java 11 introduit un client HTTP
moderne dans [Link] :
Philémon GLOBLEHI, Développeur Java 204
Version asynchrone :
Avantages : Support HTTP/2, requêtes asynchrones, API fluide et moderne.
Java 12-13 (2019)
Switch expressions (preview en 12, finalisé en 14)
Le switch devient une expression qui peut retourner une valeur :
Avec des blocs de code :
Philémon GLOBLEHI, Développeur Java 205
Bénéfices : Plus concis, pas de break oubliés, exhaustivité vérifiée par le compilateur.
Blocs de texte (preview en 13, finalisé en 15)
Fini les chaînes avec \n partout ! Les blocs de texte utilisent des triples guillemets :
Requêtes SQL :
Philémon GLOBLEHI, Développeur Java 206
Important : L'indentation est automatiquement gérée. Le compilateur retire l'indentation
commune.
Java 14 (2020)
Records (preview en 14, finalisé en 16)
Les records sont des classes de données immuables ultra-concises. Parfait pour les DTOs, les
objets valeur, etc.
Philémon GLOBLEHI, Développeur Java 207
Le compilateur génère automatiquement :
● Constructeur avec tous les paramètres
● Getters (sans le préfixe get)
● equals(), hashCode(), toString()
● Tous les champs sont final
Utilisation :
Philémon GLOBLEHI, Développeur Java 208
Vous pouvez ajouter des méthodes :
Quand utiliser ? Pour toute classe qui sert uniquement à transporter des données immuables.
Pattern matching pour instanceof (preview en 14, finalisé en 16)
Fini les casts redondants après un instanceof :
Philémon GLOBLEHI, Développeur Java 209
La variable s est automatiquement créée et disponible dans le bloc if.
Exemple plus complexe :
Java 17 (LTS - 2021)
Java 17 est la version LTS actuelle, celle recommandée pour la production.
Sealed classes (finalisé)
Les sealed classes contrôlent finement qui peut hériter d'une classe ou implémenter une
interface :
Philémon GLOBLEHI, Développeur Java 210
Pourquoi c'est utile ?
1. Exhaustivité garantie : Le compilateur peut vérifier que tous les cas sont traités
2. Domaine fermé : Parfait pour modéliser des types finis (états, événements, etc.)
3. Sécurité : Empêche l'extension non contrôlée de votre hiérarchie
Philémon GLOBLEHI, Développeur Java 211
Avec le pattern matching (preview) :
Pattern matching pour switch (preview)
Extension du pattern matching au switch :
Avec des gardes (conditions supplémentaires) :
Java 19-21 (2022-2023)
Philémon GLOBLEHI, Développeur Java 212
Virtual Threads (Project Loom - finalisé en Java 21)
La révolution de la concurrence en Java !
Les threads classiques sont lourds (1 Mo de stack par thread). Les virtual threads sont ultra-légers
(quelques Ko), permettant d'en créer des millions.
Exemple avec des milliers de threads :
Philémon GLOBLEHI, Développeur Java 213
Cas d'usage typique : Serveurs web avec beaucoup de requêtes simultanées :
Avantages :
● Simplification du code (style synchrone, pas de callbacks)
● Scalabilité énorme (millions de threads)
● Pas besoin de pools de threads complexes
Record Patterns (finalisé en Java 21)
Déconstruire les records directement dans les patterns :
Philémon GLOBLEHI, Développeur Java 214
Dans un switch :
Patterns imbriqués :
Philémon GLOBLEHI, Développeur Java 215
Sequenced Collections (Java 21)
Nouvelles interfaces pour manipuler les collections ordonnées :
Bénéfice : API cohérente pour manipuler le début et la fin des collections ordonnées.
Java 22-23 (2024)
Unnamed Variables and Patterns (Java 22)
Philémon GLOBLEHI, Développeur Java 216
Utilisez _ pour ignorer des valeurs dont vous n'avez pas besoin :
Avant Java 22, vous deviez inventer des noms pour des variables inutilisées :
String Templates (preview en Java 21-23)
Interpolation de chaînes sécurisée (attention, encore en preview) :
Philémon GLOBLEHI, Développeur Java 217
Note : Cette fonctionnalité est encore en preview et sa syntaxe peut changer.
Markdown dans Javadoc (Java 23)
Vous pouvez maintenant écrire de la documentation en Markdown plutôt qu'en HTML :
Philémon GLOBLEHI, Développeur Java 218
Avantage : Documentation plus lisible dans le code source, génération HTML automatique.
Java 24-25 (2025)
Flexible Constructor Bodies (2nd preview en Java 24)
Possibilité d'exécuter du code avant l'appel à super() ou this() :
Préparation de paramètres :
Stream Gatherers (Java 24)
Philémon GLOBLEHI, Développeur Java 219
Nouvelles opérations intermédiaires pour les streams :
Créer un gatherer personnalisé :
Philémon GLOBLEHI, Développeur Java 220
Primitive Types in Patterns (preview Java 23-24)
Pattern matching avec les types primitifs :
Dans les records :
Module Import Declarations (Java 23)
Philémon GLOBLEHI, Développeur Java 221
Simplification des imports pour les modules :
Attention : Utilisez avec parcimonie, car ça peut rendre moins clair d'où viennent les classes.
Améliorations de performance (Java 11-25)
Garbage Collectors
Java a considérablement amélioré ses ramasse-miettes :
ZGC (depuis Java 15) :
● Pauses < 10ms même avec des heaps de plusieurs téraoctets
● Idéal pour applications nécessitant une faible latence
bash
java -XX:+UseZGC -Xmx16g MonApplication
Shenandoah GC (depuis Java 12) :
● Concurrent, faibles pauses
Philémon GLOBLEHI, Développeur Java 222
● Alternative à ZGC
bash
java -XX:+UseShenandoahGC -Xmx8g MonApplication
G1GC amélioré :
● GC par défaut depuis Java 9
● Constamment optimisé dans chaque version
Optimisations du JIT
Chaque version améliore le compilateur Just-In-Time :
● Meilleures optimisations des lambdas et streams
● Inlining plus agressif
● Vectorisation automatique
Philémon GLOBLEHI, Développeur Java 223
Conclusion
Vous voilà arrivé au terme de ce parcours à travers les fondamentaux de Java. De la
manipulation des chaînes de caractères aux streams en passant par les exceptions, les
collections et la programmation fonctionnelle, vous avez parcouru les mécanismes essentiels qui
font de Java un langage robuste et expressif.
Ce que vous maîtrisez maintenant
Vous savez désormais manipuler les chaînes de caractères avec l'immutabilité et les expressions
régulières, gérer proprement les erreurs avec les exceptions checked et unchecked, créer des
types fiables avec les énumérations, définir des contrats avec les interfaces et pratiquer
l'inversion de dépendance.
Les génériques vous permettent d'écrire du code type-safe et réutilisable, tandis que les
collections (listes, ensembles, maps) vous donnent les structures de données adaptées à chaque
besoin. Vous comprenez l'importance de choisir ArrayList ou LinkedList, HashSet ou
TreeSet selon les performances recherchées.
Les entrées/sorties et la gestion des dates n'ont plus de secrets pour vous. Vous savez
manipuler des flux, lire et écrire des fichiers, sérialiser des objets et travailler avec l'API moderne
[Link].
Enfin, la programmation fonctionnelle avec les lambdas, les références de méthodes et les
streams vous ouvre la voie vers un style de code plus déclaratif, concis et souvent plus lisible.
Les classes internes vous permettent d'organiser votre code de manière élégante.
Au-delà de ce guide
Ce guide couvre les fondamentaux, mais Java est un écosystème vaste. Vous pourrez
approfondir les frameworks (Spring, Jakarta EE), la programmation concurrente, les design
patterns, les tests unitaires, ou encore les outils de build (Maven, Gradle).
Philémon GLOBLEHI, Développeur Java 224
L'essentiel à retenir
Java privilégie la clarté et la robustesse. Écrivez du code simple avant de chercher la
sophistication. Utilisez les interfaces pour découpler, les génériques pour la sécurité des types,
les streams pour la lisibilité. Gérez toujours proprement les ressources avec try-with-resources.
Préférez l'immutabilité quand c'est possible. Et surtout, lisez le code des autres : c'est en
explorant des projets open source que vous progresserez le plus.
La programmation est un artisanat qui s'affine avec la pratique. Chaque ligne de code écrite,
chaque erreur corrigée, chaque refactoring réalisé vous rend meilleur. Ne cherchez pas la
perfection immédiate : cherchez d'abord à comprendre, puis à améliorer progressivement.
Bon code !
Philémon GLOBLEHI, Développeur Java 225