Java est un langage orienté objet, cela signifie que tout en Java est constitué de classes et de leurs objets et obéit aux principes (paradigmes) de la POO (Programmation Orienté Objet).


Héritage en Java

L’héritage est un mécanisme par lequel la classe est autorisée à hériter des fonctionnalités (champs et méthodes) d’une autre classe. En termes simples, l’héritage signifie créer de nouvelles classes basées sur celles existantes.

On utilise le mot-clé :

class Fille extends Mere {
}

Comment fonctionne l’héritage ?

L’héritage va du plus général vers le plus précis.

  • La classe la plus générale s’appelle superclasse.
  • Les classes plus précises s’appellent des sous-classes.
  • Les sous-classes peuvent récupérer ou hériter des attributs et méthodes de la classe parent.

Info

La classe java.lang.Object est la super-classe de toutes les classes Java, directement ou indirectement.


Exemple

Classe la plus générale : Animal

C’est une classe abstraite (modèle général).

Tous les animaux :

  • ont un nom
  • ont un âge
  • mangent
  • boient
  • font un bruit (mais on ne sait pas lequel)
abstract class Animal {
 
    protected String nom;
    protected int age;
 
    Animal(String nom, int age) {
        this.nom = nom;
        this.age = age;
    }
 
    void manger() {
        System.out.println(nom + " mange.");
    }
 
    void dormir() {
        System.out.println(nom + " dort.");
    }
 
    abstract void faireDuBruit();
}

faireDuBruit() est abstraite car chaque animal fait un bruit différent.


Classe plus précise : Chien

Un chien est un animal → extends Animal

  • hérite de nom, age
  • hérite de manger() et dormir()
  • doit définir faireDuBruit()
  • ajoute une nouvelle propriété
  • peut ajouter de nouvelles méthodes
class Chien extends Animal {
 
    private String race;
 
    Chien(String nom, int age, String race) {
        super(nom, age);  // constructeur parent
        this.race = race; // this = attribut de la classe Chien
    }
 
    @Override
    void faireDuBruit() {
        System.out.println(nom + " aboie.");
    }
 
    void jouer() {
        System.out.println(nom + " joue avec une balle.");
    }
 
    @Override
    void manger() {
        super.manger(); // appelle la version Animal
        System.out.println("Le chien mange des croquettes.");
    }
}

Ce que l’on a

  • super(nom, age) → appelle le constructeur parent
  • this.race → fait référence à l’attribut du chien
  • @Override → redéfinit une méthode
  • super.manger() → utilise le comportement du parent + ajoute du code

Warning

L’héritage doit représenter une relation “est-un” : la classe enfant est un type spécial de la classe parent.

Incorrect : Animal hérite de Chien Correct : Chien hérite de Animal


Pourquoi utiliser l’héritage ?

  • Réutiliser du code
  • Éviter la duplication
  • Créer une hiérarchie logique
  • Permettre le polymorphisme

Exemple :

class Animal {  
void manger() {  
System.out.println("L'animal mange");  
	}  
}  
  
class Chien extends Animal {  
void aboyer() {  
System.out.println("Le chien aboie");  
	}  
}

Le Chien hérite de manger().


Redéfinition de méthode (Override)

La redéfinition de méthode permet à une classe enfant de fournir sa propre version d’une méthode héritée de la classe parent.

L’override permet de modifier le comportement hérité d’une classe parent, et c’est la base du polymorphisme en Java.

On utilise l’annotation @Override.


Comment ça fonctionne ?

Quand une méthode est redéfinie :

  • Même nom
  • Même paramètres
  • Type de retour compatible
  • Visibilité identique ou plus large

La version exécutée dépend de l’objet réel → Polymorphisme dynamique (à l’exécution).

abstract class Animal {
    abstract void faireDuBruit();
}
 
class Chien extends Animal {
 
    @Override
    void faireDuBruit() {
        System.out.println("Le chien aboie");
    }
}

Même si la référence est Animal, c’est la version Chien qui s’exécute.


Pourquoi utiliser l’Override ?

  • Spécialiser un comportement
  • Adapter une méthode à un cas précis
  • Permettre le polymorphisme
  • Rendre le code extensible

Warning

On ne peut pas redéfinir une méthode final Les attributs ne sont jamais concernés par l’override


Objet courant this

Ce mot clé this permet de désigner l’objet dans lequel on se trouve, c’est-à-dire que lorsque l’on désire faire référence dans une fonction membre à l’objet dans lequel elle se trouve, on utilise this.

L’objet courant this est en réalité une variable système qui permet de désigner l’objet courant. Il permet d’éviter les ambiguïtés et d’appeler un autre constructeur.


Accéder aux attributs de l’objet

On utilise surtout quand un paramètre a le même nom qu’un attribut.

class Animal {
 
    String nom;
 
    Animal(String nom) {
        this.nom = nom; // this.nom = attribut / nom = paramètre
    }
}

Sans this, Java ne saurait pas faire la différence.


Appeler un autre constructeur this()

Permet d’appeler un constructeur de la même classe.

class Animal {
 
    String nom;
    int age;
 
    Animal(String nom) {
        this(nom, 0); // appelle l'autre constructeur
    }
 
    Animal(String nom, int age) {
        this.nom = nom;
        this.age = age;
    }
}

this() doit toujours être la première ligne du constructeur.


Classe parent super()

Le mot-clé super en Java est utilisé pour faire référence à l’objet parent immédiat de la classe. Il est généralement utilisé pour accéder aux méthodes et aux constructeurs de la classe mère, ce qui permet à une sous-classe d’hériter et de réutiliser les fonctionnalités de sa superclasse.


Appeler le constructeur parent super()

class Animal {
 
    Animal(String nom) {
        System.out.println("Nom : " + nom);
    }
}
 
class Chien extends Animal {
 
    Chien(String nom) {
        super(nom); // appel du constructeur parent
    }
}

super() doit être la première instruction du constructeur.


Polymorphisme

Le polymorphisme est un principe fondamental de la POO.
Il permet de manipuler différents objets à travers un même type parent, tout en gardant leur comportement spécifique.

| Un même type peut prendre plusieurs formes

Concrètement, une variable de type parent peut contenir un objet d’une classe enfant.


Comment utiliser le polymorphisme ?

En Java, le polymorphisme repose sur :

Il existe deux types de polymorphisme en Java :

Polymorphisme statique (à la compilation)

  • Surcharge de méthode (même nom, paramètres différents)
  • Décidé à la compilation

Polymorphisme dynamique (à l’exécution)

  • Redéfinition de méthode (@Override)
  • Décidé à l’exécution
  • Basé sur l’objet réel

Exemple d’utilisation

public class Main {
    public static void main(String[] args) {
 
        Animal chien = new Chien("Rex", 3, "Labrador");
        Animal chat = new Chat("Mimi", 2, "Maincoon");
 
        chien.faireDuBruit(); // Rex aboie
        chat.faireDuBruit();  // Mimi miaule
 
        chien.manger();
        chat.dormir();
    }
}

Même si chien est déclaré comme Animal, c’est la version de Chien qui peut être exécutée si la méthode est redéfinie.

Dans notre exemple on parle de polymorphisme dynamique.


Pourquoi utiliser le polymorphisme ?

On utilise le polymorphisme pour améliorer la lisibilité du code en favorisant sa réutilisation et en éliminant les redondances.

Grâce au polymorphisme, on peut écrire du code générique qui opère sur des objets de types différents sans avoir besoin de connaître leurs implémentations spécifiques.

Sans polymorphisme :

if(animal instanceof Chien) {
    ((Chien) animal).faireDuBruit();
} else if(animal instanceof Chat) {
    ((Chat) animal).faireDuBruit();
}

Ce code est fragile, et difficile à maintenir.

Avec polymorphisme :

animal.faireDuBruit();

Il en résulte un code plus court, plus concis, plus facile à comprendre et à maintenir.