1. Aperçu des modificateurs d’accès
En Java, il existe quatre niveaux d’accès pour les classes, les champs, les méthodes et les constructeurs :
| Modificateur | Où est-il accessible ? |
|---|---|
|
Partout : à l’intérieur de la classe, dans d’autres classes, dans d’autres packages |
|
À l’intérieur de la classe, dans les sous-classes (héritage), et dans les autres classes du même package |
|
Uniquement à l’intérieur du package (si le modificateur n’est pas explicitement indiqué) |
|
Uniquement à l’intérieur de la classe courante |
Examinons-les plus en détail — avec des exemples, une pointe d’humour et quelques rebondissements.
public — accès public
public, c’est comme une annonce au monde entier : « Accès ouvert à tous ! ». Si une classe, un champ, une méthode ou un constructeur est déclaré public, on peut y accéder depuis n’importe quelle autre classe, même depuis un autre package.
Exemple :
public class Cat {
public String name;
public void sayMeow() {
System.out.println("Miaou !");
}
}
On peut accéder à cette classe et à ses champs/méthodes depuis n’importe où. C’est pratique si vous voulez que votre classe soit accessible à tous, par exemple lorsque vous écrivez une bibliothèque.
Mais ! Les champs publics ne sont pas toujours une bonne idée (voir la leçon précédente). En général, seuls les méthodes qui doivent être accessibles de l’extérieur sont publiques, tandis que les champs sont presque toujours laissés private.
private — accès à l’intérieur de la classe seulement
private, c’est comme un coffre-fort à code : personne, à part la classe elle-même, ne peut accéder à ces membres. Même les sous-classes ne voient pas les champs et méthodes privés !
Exemple :
public class Cat {
private String secretName;
public void setSecretName(String name) {
secretName = name;
}
public String getSecretName() {
return secretName;
}
}
Ici, secretName ne peut pas être lu ni modifié directement depuis une autre classe. Seule la classe Cat (ou ses méthodes) peut le faire. C’est le principe de l’encapsulation : on masque les détails internes et on n’y accède qu’à travers des méthodes.
protected — accès protégé
protected, c’est comme un pass VIP : l’accès est autorisé à la classe, à ses sous-classes (même si elles se trouvent dans d’autres packages) et à toutes les classes du package courant.
Exemple :
public class Animal {
protected int age;
protected void growOlder() {
age++;
}
}
Désormais, toute classe qui hérite de Animal pourra accéder au champ age et à la méthode growOlder().
public class Cat extends Animal {
public void haveBirthday() {
growOlder();
System.out.println("Le chat a " + age + " ans !");
}
}
Les membres protected sont également accessibles à toutes les classes du même package.
(package-private) — accès à l’intérieur du package
Si vous n’indiquez aucun modificateur d’accès, alors le membre est considéré comme package-private (ou « accès par défaut »). C’est comme une porte sans serrure, mais seulement pour les habitués : l’accès n’est permis qu’aux classes du même package.
Exemple :
class Dog {
String name; // package-private
void bark() { // package-private
System.out.println("Ouaf !");
}
}
La classe Dog, son champ name et sa méthode bark() ne sont accessibles qu’à l’intérieur du même package. Toute tentative d’y accéder depuis un autre package provoquera une erreur de compilation.
2. Application des modificateurs aux champs et aux méthodes
Pourquoi les champs sont presque toujours private
Les champs d’une classe représentent son état interne. S’ils sont publics, n’importe quel code externe peut les modifier à tout moment. C’est comme si vous laissiez des enfants inconnus jouer avec vos meubles comme avec des Lego : un jour, vous vous réveillerez et le frigo sera posé à l’envers dans la salle de bains.
Exemple de mauvaise encapsulation :
public class Person {
public String name;
public int age;
}
Exemple de bonne encapsulation :
public class Person {
private String name;
private int age;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Les méthodes (getters et setters) permettent de contrôler comment les classes externes peuvent modifier les champs. Par exemple, on peut interdire un âge négatif.
Quand rendre les méthodes public, protected ou package-private
- public — si la méthode doit être accessible à tous. En général, il s’agit de la fonctionnalité principale de la classe.
- protected — si la méthode n’est utile qu’aux sous-classes ou à l’intérieur du package (par exemple des méthodes utilitaires utiles dans les classes dérivées).
- package-private — si la méthode ne doit être utilisée qu’à l’intérieur du package, sans être accessible de l’extérieur (par exemple, des détails d’implémentation internes).
- private — si la méthode n’est utilisée qu’à l’intérieur de la classe elle-même (par exemple, des utilitaires pour la logique interne).
Exemple :
public class BankAccount {
private double balance;
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
protected void applyInterest() {
balance *= 1.05;
}
void internalAudit() {
// package-private : uniquement pour les classes du package
}
private void logAction(String action) {
// uniquement pour les besoins internes de la classe
}
}
3. Exemples de code : une classe avec différents niveaux d’accès
Créons une classe qui contient tout : des membres publics, privés, protégés et de package. Essayons ensuite d’y accéder depuis d’autres classes et voyons ce qui se passe.
package zoo;
public class Animal {
public String publicName = "Accessible à tous";
protected String protectedName = "Uniquement pour les sous-classes et le package";
String packageName = "Uniquement pour le package";
private String privateName = "Uniquement pour Animal";
public void publicMethod() {
System.out.println("Méthode publique");
}
protected void protectedMethod() {
System.out.println("Méthode protégée");
}
void packageMethod() {
System.out.println("Méthode de package");
}
private void privateMethod() {
System.out.println("Méthode privée");
}
}
Essayons maintenant d’accéder à ces membres depuis une autre classe du même package :
package zoo;
public class Main {
public static void main(String[] args) {
Animal animal = new Animal();
System.out.println(animal.publicName); // OK
System.out.println(animal.protectedName); // OK
System.out.println(animal.packageName); // OK
System.out.println(animal.privateName); // Erreur : private
animal.publicMethod(); // OK
animal.protectedMethod(); // OK
animal.packageMethod(); // OK
animal.privateMethod(); // Erreur : private
}
}
Et maintenant, essayons d’y accéder depuis un autre package :
package other;
import zoo.Animal;
public class Test {
public static void main(String[] args) {
Animal animal = new Animal();
System.out.println(animal.publicName); // OK
System.out.println(animal.protectedName); // Erreur : protected
System.out.println(animal.packageName); // Erreur : package-private
System.out.println(animal.privateName); // Erreur : private
animal.publicMethod(); // OK
animal.protectedMethod(); // Erreur : protected
animal.packageMethod(); // Erreur : package-private
animal.privateMethod(); // Erreur : private
}
}
Conclusion :
- public — accessible partout.
- protected — accessible dans le package et dans les sous-classes (même depuis d’autres packages, via l’héritage).
- package-private — uniquement dans le package.
- private — uniquement dans la classe.
4. Bonnes pratiques : comment choisir le modificateur d’accès
Réduisez au minimum la visibilité
Moins il y a de code qui peut voir votre champ ou votre méthode, mieux c’est. N’exposez que ce qui est réellement nécessaire au code externe. C’est le principe du moindre privilège (principle of least privilege).
- Les champs doivent presque toujours être private. Exceptions : uniquement pour de vraies constantes (public static final), mais nous en parlerons plus en détail dans les prochaines leçons.
- Les méthodes sont public seulement si elles font partie de l’interface publique de la classe.
- Les méthodes utilitaires internes — private.
- Les méthodes destinées aux sous-classes — protected.
- Les méthodes de service internes au package — package-private.
Pourquoi est-ce important ?
- Tout changement dans les détails d’implémentation peut casser du code tiers si ces détails sont exposés.
- La classe devient plus difficile à tester et à maintenir.
- Des erreurs accidentelles (par exemple, une modification incorrecte d’un champ) peuvent entraîner des bugs.
Parfois, les débutants se disent : « À quoi bon ces complications, mettons tout en public ! » Mais ensuite, quand le projet grossit, il faut réécrire la moitié du programme simplement parce que quelqu’un modifiait directement les champs de la classe.
5. Erreurs typiques avec les modificateurs d’accès
Erreur n° 1 : vous avez laissé des champs public ou package-private par défaut.
Si vous n’indiquez pas de modificateur, un champ ou une méthode sera accessible à toutes les classes du package. Cela peut mener à des situations inattendues si quelqu’un commence à modifier vos champs directement.
Erreur n° 2 : tentative d’accéder à un membre private depuis une autre classe.
Le compilateur ne vous laissera pas faire — vous obtiendrez une erreur. Et si vous essayez de contourner cela via reflection (réflexion), bienvenue dans le monde des bugs et des plantages inattendus.
Erreur n° 3 : trop de public.
Si vous déclarez tout et n’importe quoi public, votre classe ressemblera à une boîte ouverte remplie de câbles — n’importe qui peut tirer sur le mauvais câble et tout casser.
Erreur n° 4 : vous n’utilisez pas protected pour les méthodes destinées uniquement aux sous-classes.
Si une méthode est nécessaire uniquement pour l’extension dans les classes dérivées, déclarez-la protected, et non public.
Erreur n° 5 : visibilité implicite package-private.
On oublie parfois d’indiquer le modificateur, et la méthode devient accessible à tout le package. Cela peut surprendre si vous pensiez qu’elle était private.
GO TO FULL VERSION