Essayer de coder ← Retour aux sujets

Classe, objet, constructeur

class

Une classe décrit un type d'objets (ses attributs et ses méthodes). Un objet est un exemplaire de cette classe, créé avec new.

class Eleve { String nom; int age; }
Déclare la classe et ses attributs
Eleve e = new Eleve("Lina", 17);
Crée un objet : appelle le constructeur
e.sePresenter()
Appelle une méthode sur l'objet
this
L'objet courant, à l'intérieur de ses méthodes
private int age;
Attribut inaccessible de l'extérieur : encapsulation
Classe.java
class Eleve {
    private String nom;
    private int age;

    Eleve(String nom, int age) {
        this.nom = nom;
        this.age = age;
    }

    String sePresenter() {
        return nom + " a " + age + " ans";
    }
}

public class Classe {
    public static void main(String[] args) {
        Eleve e = new Eleve("Lina", 17);
        System.out.println(e.sePresenter());
    }
}
Affichage
Lina a 17 ans

Héritage et polymorphisme

extends

Une classe peut hériter d'une autre : elle reprend ses attributs et ses méthodes et peut les redéfinir. Une variable du type parent peut désigner un objet d'un type enfant.

class Chien extends Animal { … }
Chien hérite d'Animal
super(nom);
Appelle le constructeur du parent
@Override
Annonce qu'on redéfinit une méthode du parent
Animal a = new Chien("Rex");
Polymorphisme : le type réel décide de la méthode appelée
interface Forme { double aire(); }
Contrat que les classes doivent respecter
Zoo.java
class Animal {
    protected String nom;

    Animal(String nom) {
        this.nom = nom;
    }

    String crier() {
        return "...";
    }
}

class Chien extends Animal {
    Chien(String nom) {
        super(nom);
    }

    @Override
    String crier() {
        return nom + " : Ouaf !";
    }
}

class Chat extends Animal {
    Chat(String nom) {
        super(nom);
    }

    @Override
    String crier() {
        return nom + " : Miaou !";
    }
}

public class Zoo {
    public static void main(String[] args) {
        Animal[] animaux = {new Chien("Rex"), new Chat("Mimi"), new Animal("Inconnu")};

        for (Animal a : animaux) {
            System.out.println(a.crier());
        }
    }
}
Affichage
Rex : Ouaf !
Mimi : Miaou !
...

🏗️ Le cours : la programmation orientée objet

La programmation orientée objet (POO) organise un programme autour d'objets : chacun regroupe des données (ses attributs) et les actions possibles sur ces données (ses méthodes). Java est construit autour de cette idée : tout le code vit dans des classes.

Classe et objet

Une classe est un plan (comme les plans d'une maison) ; un objet est une réalisation de ce plan. À partir d'une seule classe Eleve, on peut créer autant d'objets que l'on veut, chacun avec ses propres valeurs d'attributs. new Eleve(…) réserve la mémoire, puis appelle le constructeur.

Attributs, méthodes et encapsulation

On déclare les attributs private : seul le code de la classe peut y toucher. L'extérieur passe par des méthodes qui vérifient les règles (un solde ne peut pas devenir négatif, un âge ne peut pas être de −5). C'est l'encapsulation : l'objet protège ses données.

Banque.java
class Compte {
    private double solde;

    Compte(double soldeInitial) {
        solde = soldeInitial;
    }

    void deposer(double montant) {
        solde += montant;
    }

    boolean retirer(double montant) {
        if (montant > solde) {
            return false;               // refusé : solde insuffisant
        }
        solde -= montant;
        return true;
    }

    double getSolde() {
        return solde;
    }
}

public class Banque {
    public static void main(String[] args) {
        Compte c = new Compte(100);

        c.deposer(50);
        System.out.println(c.retirer(500));
        System.out.println(c.retirer(30));
        System.out.println(c.getSolde());
    }
}
Affichage
false
true
120.0

Les méthodes qui lisent un attribut s'appellent des accesseurs (*getters*, getSolde()) ; celles qui le modifient des mutateurs (*setters*). On n'en écrit que pour les attributs qui doivent vraiment être accessibles.

VisibilitéMot-cléAccessible depuis
privéeprivatela classe seulement
paquetage(rien)les classes du même paquetage
protégéeprotectedle paquetage et les classes filles
publiquepublicpartout

Le constructeur et this

Le constructeur porte le nom de la classe et n'a pas de type de retour. Il initialise les attributs. Dans this.nom = nom;, this.nom désigne l'attribut et nom le paramètre : this est l'objet en cours. Si tu n'écris aucun constructeur, Java en fournit un sans paramètre, qui disparaît dès que tu en écris un.

static : ce qui appartient à la classe

Un attribut ou une méthode static n'appartient pas à un objet mais à la classe : il y en a un seul exemplaire, partagé. On l'appelle avec le nom de la classe (Eleve.getTotal(), Math.max(a, b)).

Inscription.java
class Eleve {
    private static int total = 0;      // un seul exemplaire pour toute la classe
    private String nom;

    Eleve(String nom) {
        this.nom = nom;
        total++;
    }

    static int getTotal() {
        return total;
    }
}

public class Inscription {
    public static void main(String[] args) {
        new Eleve("Lina");
        new Eleve("Sami");
        System.out.println(Eleve.getTotal());
    }
}
Affichage
2

toString et equals

println(objet) appelle la méthode toString() de l'objet : sans redéfinition, elle affiche un code peu parlant (Point@1b6d3586). == compare les références ; pour comparer le contenu, on redéfinit equals (en pratique, on redéfinit aussi hashCode pour que les objets fonctionnent dans un HashSet).

Points.java
class Point {
    private final int x;
    private final int y;

    Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public String toString() {
        return "(" + x + ", " + y + ")";
    }

    @Override
    public boolean equals(Object autre) {
        if (!(autre instanceof Point)) {
            return false;
        }
        Point p = (Point) autre;
        return x == p.x && y == p.y;
    }
}

public class Points {
    public static void main(String[] args) {
        Point a = new Point(1, 2);
        Point b = new Point(1, 2);

        System.out.println(a);
        System.out.println(a == b);
        System.out.println(a.equals(b));
    }
}
Affichage
(1, 2)
false
true

L'héritage

class Chien extends Animal signifie « un chien est un animal » : Chien reçoit les attributs et méthodes d'Animal. Le constructeur de la classe fille commence par super(…), qui appelle celui du parent. Un attribut protected reste invisible de l'extérieur mais accessible aux classes filles. @Override demande au compilateur de vérifier que la méthode redéfinit bien une méthode du parent (il refuse en cas de faute de frappe).

Le polymorphisme

Une variable de type Animal peut contenir un Chien, un Chat ou un Animal. Quand on appelle a.crier(), c'est la version de l'objet réellement créé qui s'exécute, pas celle du type de la variable : c'est ce qu'on observe dans l'exemple de la deuxième carte. On peut ainsi écrire un seul traitement pour tous les animaux, présents et futurs.

Interfaces et classes abstraites

Une interface décrit ce qu'une classe sait faire (des méthodes sans corps) sans dire comment. Chaque classe qui l'implements doit fournir ces méthodes. Une classe abstraite (abstract class) est une classe qu'on ne peut pas instancier directement et qui peut contenir des méthodes à compléter par ses filles.

Formes.java
interface Forme {
    double aire();
}

class Rectangle implements Forme {
    private final double largeur;
    private final double hauteur;

    Rectangle(double largeur, double hauteur) {
        this.largeur = largeur;
        this.hauteur = hauteur;
    }

    public double aire() {
        return largeur * hauteur;
    }
}

class Cercle implements Forme {
    private final double rayon;

    Cercle(double rayon) {
        this.rayon = rayon;
    }

    public double aire() {
        return Math.PI * rayon * rayon;
    }
}

public class Formes {
    public static void main(String[] args) {
        Forme[] formes = {new Rectangle(3, 4), new Cercle(1)};

        for (Forme f : formes) {
            System.out.println(f.aire());
        }
    }
}
Affichage
12.0
3.141592653589793

Les méthodes d'une interface sont implicitement public : c'est pourquoi aire() est déclarée public dans les classes qui l'implémentent.

⚠️ Erreurs fréquentes

Rendre les attributs publics

Avec public int age;, n'importe quel code peut écrire e.age = -50. Déclare les attributs private et passe par des méthodes qui contrôlent les valeurs.

Utiliser un objet qui vaut null

À éviter
Faux.java
Eleve e = null;
System.out.println(e.sePresenter());   // NullPointerException
Correct
Correct.java
Eleve e = new Eleve("Lina", 17);       // l'objet existe
System.out.println(e.sePresenter());

Un constructeur avec un type de retour

À éviter
Faux.java
void Eleve(String nom) { … }     // une méthode ordinaire, pas un constructeur !
Correct
Correct.java
Eleve(String nom) { … }          // pas de type de retour

Oublier super(…) dans une classe fille

Si la classe parent n'a pas de constructeur sans paramètre, la fille doit appeler super(…) avec les bons arguments, en première ligne de son constructeur. Sinon : « constructor Animal in class Animal cannot be applied to given types ».

Comparer des objets avec ==

a == b demande « est-ce le même objet ? ». Pour savoir si deux objets ont le même contenu, il faut une méthode equals redéfinie, et l'appeler : a.equals(b).

Appeler une méthode d'objet depuis main sans objet

main est static : elle ne peut pas appeler directement sePresenter(), qui s'applique à un objet. Crée d'abord l'objet (Eleve e = new Eleve(…)) puis écris e.sePresenter().

Confondre surcharge et redéfinition

La surcharge crée une méthode de même nom avec d'autres paramètres ; la redéfinition remplace une méthode du parent avec la même signature. Mets @Override : si la signature diffère, le compilateur signale la faute.

📋 Aide-mémoire

  • Classe = plan ; objet = exemplaire créé avec new ; le constructeur porte le nom de la classe, sans type de retour.
  • Attributs private, méthodes d'accès getX() / setX() : c'est l'encapsulation.
  • this = l'objet courant ; static = appartient à la classe (un seul exemplaire).
  • toString() pour afficher, equals(Object) pour comparer le contenu ; == compare les références.
  • extends + super(…) + @Override : héritage ; une variable du type parent peut contenir un objet fille (polymorphisme).
  • interface / implements : contrat de méthodes ; abstract class : classe incomplète non instanciable.