Bonjour ! Bienvenue dans cette nouvelle leçon qui ouvre un chapitre fondamental de la programmation orientée objet. Dans le module précédent, nous avons appris à construire des classes, à encapsuler leurs données et, avec la méthode toString(), nous avons eu un premier aperçu de la redéfinition de méthodes. Vous avez vu que chaque classe hérite implicitement de java.lang.Object.
Aujourd'hui, nous allons rendre cet héritage explicite. Nous allons explorer l'un des piliers de la POO : l'héritage. Notre objectif est d'apprendre à créer des relations parent-enfant entre vos propres classes en utilisant le mot-clé extends. Cela vous permettra de bâtir des hiérarchies de classes logiques, de réutiliser du code efficacement et de poser les fondations pour le polymorphisme, un concept que nous verrons très bientôt.
1. Qu'est-ce que l'héritage ?
L'héritage est un mécanisme qui permet à une classe (la classe fille ou sous-classe) d'acquérir les attributs et les méthodes d'une autre classe (la classe mère ou super-classe).
L'idée principale est de factoriser le code commun. Si plusieurs classes partagent des caractéristiques et des comportements, on peut extraire ces éléments communs dans une classe mère. Les classes filles héritent alors de cette base et peuvent y ajouter leurs propres spécificités.
On identifie souvent une relation d'héritage par le test " est un " (en anglais, "is-a").
- Une
Voitureest unVehicule. - Un
Managerest unEmploye. - Un
Cercleest uneForme.
Cette relation est souvent représentée en UML (un langage de modélisation que vous connaissez) par une flèche pointant de la classe fille vers la classe mère.

Pour une explication plus approfondie et un exemple concret, la vidéo suivante est excellente.
Cette vidéo de la chaîne EvoluNoob explique en détail le concept de l'héritage avec un exemple concret.
Regardez la première partie de la vidéo (l'explication du concept). L'auteur part d'un exemple avec deux classes distinctes (Cheval et Pegase) qui partagent du code, et montre comment l'héritage permet de créer une hiérarchie logique où Pegase devient une spécialisation de Cheval. Concentrez-vous sur la relation "est un" et la structure hiérarchique qui en découle.
2. Mettre en œuvre l'héritage avec extends
En Java, on utilise le mot-clé extends pour déclarer qu'une classe hérite d'une autre.
Voyons un exemple simple. Imaginons que nous avons une application qui gère des véhicules.
Classe mère : Vehicule.java
Elle contient les attributs et méthodes communs à tous les véhicules.
public class Vehicule {
protected String marque;
protected int annee;
public Vehicule(String marque, int annee) {
this.marque = marque;
this.annee = annee;
}
public void demarrer() {
System.out.println("Le véhicule démarre.");
}
}
Classe fille : Voiture.java
Elle hérite de Vehicule et ajoute des éléments spécifiques aux voitures.
public class Voiture extends Vehicule {
private int nombreDePortes;
public Voiture(String marque, int annee, int nombreDePortes) {
// On verra comment initialiser 'marque' et 'annee' juste après !
// Pour l'instant, imaginons que c'est fait.
super(marque, annee); // Appel au constructeur de la classe mère
this.nombreDePortes = nombreDePortes;
}
public void klaxonner() {
System.out.println("Tut tut !");
}
}
Note : nous reviendrons sur la ligne super(...) dans la section suivante.
Grâce à extends Vehicule, un objet de type Voiture possède non seulement nombreDePortes et klaxonner(), mais aussi marque, annee et demarrer().
Utilisation : Main.java
public class Main {
public static void main(String[] args) {
Voiture maVoiture = new Voiture("Renault", 2022, 5);
// Méthode propre à la classe Voiture
maVoiture.klaxonner();
// Méthode héritée de la classe Vehicule
maVoiture.demarrer();
// Attribut hérité de la classe Vehicule
System.out.println("Marque de la voiture : " + maVoiture.marque);
}
}
3. Constructeurs et le mot-clé super
Un aspect crucial de l'héritage est la construction des objets. Quand vous créez un objet de la classe fille (ex: Voiture), un objet de la classe mère (Vehicule) doit d'abord être construit en coulisses.
C'est pourquoi la première instruction d'un constructeur de classe fille doit toujours être un appel au constructeur de sa classe mère.
On utilise le mot-clé super pour cela.
super(): appelle le constructeur sans argument de la classe mère.super(param1, param2, ...): appelle le constructeur de la classe mère qui correspond à ces paramètres.
Si vous n'écrivez pas d'appel à super() explicitement, Java en insère un pour vous (super();), mais cela ne fonctionne que si la classe mère a un constructeur sans argument. Si, comme dans notre cas, Vehicule n'a qu'un constructeur avec paramètres, vous devez l'appeler explicitement.
4. La programmation orientée objet - Jean-Michel Doudoux
Ce document de J.M. Doudoux formalise les règles de l'héritage en Java.
Lisez la section "La mise en oeuvre de l'héritage". Portez une attention particulière à la phrase : "En Java, il est obligatoire dans un constructeur d'une classe fille de faire appel explicitement ou implicitement au constructeur de la classe mère." C'est une règle fondamentale. La section explique aussi comment utiliser super() pour cet appel.
4. Visibilité des membres : le modificateur protected
Dans notre exemple Vehicule.java, les attributs marque et annee sont déclarés avec protected. Pourquoi pas private ?
- Si un membre est
private, il n'est accessible que depuis sa propre classe. Une classe fille ne peut pas y accéder directement. - Si un membre est
public, il est accessible de partout, ce qui brise l'encapsulation.
Le modificateur protected offre le compromis parfait pour l'héritage :
Un membre
protectedest accessible par les classes du même package et par toutes les classes filles, où qu'elles soient.
C'est le modificateur d'accès à privilégier pour les attributs et méthodes de la classe mère que vous souhaitez rendre disponibles à ses descendantes, sans pour autant les exposer au reste du monde.
La vidéo suivante illustre parfaitement la mise en place de extends, l'utilisation de super() dans le constructeur, et l'intérêt de protected.
Revenons à la vidéo d'EvoluNoob pour voir la mise en pratique.
Regardez les deux segments suivants : extends et protected : L'auteur met en place le mot-clé extends et explique la nécessité du modificateur protected pour que la classe fille puisse accéder aux attributs de la classe mère. le constructeur et super : Voyez comment le constructeur de la classe Pegase appelle le constructeur de la classe Cheval avec super().
5. Exercice pratique : Personne et Employe
Il est temps de mettre tout cela en pratique. Vous allez modéliser une hiérarchie simple entre une personne et un employé.
Énoncé :
-
Créez la classe mère
Personneavec les attributsprotectedsuivants :String nomString prenom
-
Ajoutez un constructeur à
Personnequi initialise ces deux attributs. -
Redéfinissez la méthode
toString()dansPersonnepour qu'elle affiche le nom et le prénom. -
Créez la classe fille
Employequi hérite dePersonne(extends Personne). -
Ajoutez les attributs
privatesuivants àEmploye:String postedouble salaire
-
Créez un constructeur pour
Employequi initialise tous ses attributs (nom,prenom,poste,salaire).- N'oubliez pas d'appeler le constructeur de
Personneavecsuper()!
- N'oubliez pas d'appeler le constructeur de
-
Dans une classe
Main, créez une instance deEmployeet affichez-la dans la console (System.out.println). Grâce à l'héritage detoString(), vous devriez déjà voir le nom et le prénom !
Cliquez ici pour voir la solution
Classe Personne.java
public class Personne {
protected String nom;
protected String prenom;
public Personne(String nom, String prenom) {
this.nom = nom;
this.prenom = prenom;
}
@Override
public String toString() {
return "Personne{" +
"nom='" + nom + '\'' +
", prenom='" + prenom + '\'' +
'}';
}
}
Classe Employe.java
public class Employe extends Personne {
private String poste;
private double salaire;
public Employe(String nom, String prenom, String poste, double salaire) {
// Appel obligatoire au constructeur de la classe mère Personne
super(nom, prenom);
this.poste = poste;
this.salaire = salaire;
}
// Pour un affichage plus complet, on pourrait aussi redéfinir toString() ici.
// Nous verrons cela dans la prochaine leçon sur le polymorphisme.
}
Classe Main.java
public class Main {
public static void main(String[] args) {
Employe employe1 = new Employe("Durand", "Alice", "Développeuse Backend", 50000.0);
System.out.println(employe1);
// Affiche : Personne{nom='Durand', prenom='Alice'}
// car la méthode toString() de Personne est héritée.
// On peut aussi accéder aux attributs de la classe mère
System.out.println("Nom de l'employé : " + employe1.nom);
}
}
Conclusion
Vous maîtrisez maintenant le premier grand pilier de la POO après l'encapsulation : l'héritage. Cette compétence est essentielle pour construire des applications Java bien structurées et évolutives.
Points clés à retenir :
- L'héritage crée une relation "est un" entre une classe mère et une classe fille.
- Il favorise la réutilisation du code en plaçant les éléments communs dans la classe mère.
- Le mot-clé
extendsest utilisé pour déclarer l'héritage. - Le constructeur de la classe fille doit appeler un constructeur de la classe mère via
super(), et ce doit être sa première instruction. - Le modificateur
protectedpermet aux classes filles d'accéder aux membres de leur mère tout en préservant une bonne encapsulation vis-à-vis de l'extérieur.
Dans la prochaine leçon, nous allons nous appuyer directement sur l'héritage pour explorer un concept tout aussi puissant : le polymorphisme. Nous verrons comment des objets de types différents (mais liés par héritage) peuvent réagir différemment au même appel de méthode, notamment en redéfinissant des méthodes avec l'annotation @Override que vous connaissez déjà.