Rechercher
 

Améliorations / Corrections

Vous avez des améliorations (ou des corrections) à proposer pour ce document : je vous remerçie par avance de m'en faire part, cela m'aide à améliorer le site.

Emplacement :

Description des améliorations :

Les niveaux de visibilité en Java
(public, private, protected et package)

La notion de package en Java Les archives Java et l'outil jar



Accès rapide :
La visibilité de vos membres dans une classe
La visibilité publique - public
La visibilité protégée - protected
La visibilité « package private »
La visibilité privée - private
La visibilité de vos membres dans une interface
Changement de visibilité en cas de redéfinition de méthodes
La visibilité des vos classes
Les classes publiques
Les classes « package private »
La visibilité des classes internes (inner classes)

Il est maintenant temps de revenir sur les niveaux de visibilité proposés par Java. Dans les faits, on en parle déjà depuis pas mal de temps. Mais pour bien comprendre les nuances les différents niveaux de visibilité de Java, il fallait avoir préalablement parlé de package, ce que nous avons fait dans le chapitre précédent.

La visibilité de vos membres dans une classe

Quand vous définissez des membres dans une classe, vous avez le choix parmi quatre niveaux de visibilité. Trois de ces niveaux (public, protected et private) sont associés à des mots clés. Le quatrième niveau s'obtient en ne spécifiant aucune mot clé relatif à la visibilité. Et oui, contrairement, à ce que beaucoup de monde pense, ne rien préciser ne veut pas dire qu'on a un accès public.

La visibilité publique - public

Si vous définissez un membre (attribut, méthode...) public il sera alors accessible de n'importe ou dans le programme. La classe aura donc accès à ce membre, mais aussi n'importe quelle autre classe.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
public class Demo {
 
    public int attribute = 10;
    
    public void doSomething() {
        System.out.println( "Do something" );
    }
            
}
Exemple de déclarations de membres publics

La visibilité protégée - protected

Un membre marqué dans une classe comme étant protégé (mot clé protected) peut être manipulé :

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
package fr.koor.sample;

import java.awt.Point;

class Shape {
    protected Point center = new Point();       // Essayez avec un private => Ko 
}

class Circle extends Shape {
    public Circle() {
        super();
        System.out.println( "Center == " + this.center );
    }
}

public class Sample {

    public static void main( String[] args ) {
        Circle c = new Circle();
        // TODO
    }
    
}
Exemple d'utilisation du mot clé protected
si vous êtes déjà développeur C++ (langage dont dérive Java), notez bien que le mot clé protected n'a pas exactement la même signification entre les deux langages. En C++, ce mot clé n'a aucun lien avec la notion de namespaces (l'équivalent de la notion de packages pour Java).

La visibilité « package private »

En l'absence de mot clé pour spécifier la visibilité, un membre sera considéré comme étant « package private » (on peut simplifier en parlant de visibilité package). C'est donc le mode de visibilité par défaut (absence de mot clé). Cela veut dire que le membre sera accessible par tout code Java localisé dans le même package. Voici un exemple d'utilisation.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
package fr.koor.sample;
            
public class TestVisibility {
    public int attr1 = 10;
    protected int attr2 = 20;
    int attr3 = 30;             // Visibilité package private !
    private int attr4 = 40;

}
Exemple de visibilité « package private »

Si une autre classe du même package essaye d'utiliser la classe TestVisibility, elle aura accès aux membres de type « package private » (mais aussi aux membres protégés et publics). Voici un exemple de code.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
package fr.koor.sample;
            
public class OtherClass {

    public static void main( String [] args ) {
    
        TestVisibility test = new TestVisibility();
        System.out.println( test.attr1 );   // Tout le monde a accès à un membre public.
        System.out.println( test.attr2 );   // On est dans le même package => Ok.
        System.out.println( test.attr3 );   // On est dans le même package => Ok.
        //System.out.println( test.attr4 ); // Pas possible, car privé.
    
    }

}
Test sur la visibilité package private

Par contre, si une autre classe, située dans un autre package, essaye d'utiliser la classe TestVisibility, elle n'aura plus accès aux membres « package private ». Pour ce qui est des champs protégés, tout dépend de si la classe considérée dérive ou non de TestVisibility. Voici un exemple de code.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
package fr.koor.otherpackage;
            
// Si vous décommentez la relation d'héritage, attr2 deviendra accessible.
public class ThirdClass /* extends TestVisiblity */ {

    public static void main( String [] args ) {
    
        TestVisibility test = new TestVisibility();
        System.out.println( test.attr1 );    // Tout le monde a accès à un membre public.
        //System.out.println( test.attr2 );  // Pas possible dans notre cas.
        //System.out.println( test.attr3 );  // Pas possible, car pas dans le même package.
        //System.out.println( test.attr4 );  // Pas possible, car privé.
    
    }

}
Test sur la visibilité package private

La visibilité privée - private

Dans ce dernier cas, seule la classe ayant définit le membre pourra y accéder.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
package fr.koor.sample;
            
public class Demo {

    private static int privateAttribute = 10;
    
    public static void main( String [] args ) {
        
        System.out.println( "J'ai accès à l'attribut static privé : " + privateAttribute );
        
    }

}
Exemple de visibilité privée

La visibilité de vos membres dans une interface

Les règles pour une interface ne sont pas les mêmes que pour les classes. En premier lieu, en l'absence de mot clé, un membre d'interface est implicitement public. Cela implique de la visibilité « package private » n'est pas possible pour un membre d'interface. Au passage, la visibilité protected n'est pas supportée non plus.

En conséquence l'interface suivante défini deux méthodes publiques.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
package fr.koor.sample;
 
public interface Demo {

    void doSomething();     // Par défaut, visibilité "public"
    void doAnother();       // Par défaut, visibilité "public"
           
}
Exemple de définition d'une interface exposant des méthodes publiques

L'interface ci-dessus est donc strictement équivalente à celle proposée ci-dessous.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
package fr.koor.sample;
 
public interface Demo {

    public abstract void doSomething();
    public abstract void doAnother();
           
}
Exemple de définition d'une interface exposant des méthodes publiques
depuis Java SE 9, il est aussi possible de définir des méthodes privées et concrètes dans une interface. Elles seront accessibles par le biais de méthodes par défaut (default methods) de l'interface : elles seront quant à elles déclarées comme étant publiques.
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
package fr.koor.sample;

import java.util.Date;

public interface Demo {

    void abstractMethod();
    
    default void doSomething() {
        this.log( "Depuis le Java SE 9" );
    }
    
    // Attention, Java SE 9 minimum requis !!!   
    private void log( String message ) {
        System.out.println( new Date() + " - " + message );
    }
    
}
Exemple de méthodes privées sur interface (Java SE 9.0 minimum)

Changement de visibilité en cas de redéfinition de méthodes

En cas de redéfinition de méthodes (héritage ou implémentation d'interface), il est possible de modifier la visibilité de ces méthodes. Mais attention, vous devez respecter une règle : vous pouvez ouvrir la visibilité et non pas la restreindre.

Par exemple, la classe Object définie la méthode clone permettant de cloner un objet : cette méthode est déclarée comme étant protected. Si vous souhaitez redéfinir cette méthode pour permettre le clonage de vos instances, vous pouvez « ouvrir » sa visibilité et la rendre publique. Voici un exemple concret.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
 28 
 29 
 30 
 31 
 32 
 33 
 34 
 35 
 36 
 37 
 38 
 39 
 40 
 41 
package fr.koor.sample;

public class Matrix extends Object {

    int [][] data;
    
    public Matrix( int lines, int columns ) {
        data = new int[lines][];
        for( int i=0; i<lines; i++ ) {
            data[i] = new int[columns];
            for( int j=0; j<columns; j++ ) {
                data[i][j] = (int) (Math.random() * 10);
            }
        }
    }
    
    // Exemple d'ouverture de visibilité de la méthode sur une redéfinition.
    // Essayer de changer public par protected (ok) puis par private (ko).
    @Override
    public Matrix clone() throws CloneNotSupportedException {
        int lines = data.length;
        int columns = data[0].length;
        
        Matrix copy = new Matrix( lines, columns );
        for( int i=0; i<lines; i++ ) {
            data[i] = new int[columns];
            for( int j=0; j<columns; j++ ) {
                copy.data[i][j] = this.data[i][j];
            }
        }       
        
        return copy;
    }
    
    public static void main(String[] args) throws Exception {
        Matrix m = new Matrix(3, 3);
        Matrix m2 = m.clone();
        // La suite ...
    }
    
}
Exemple d'ouverture de visibilité d'une méthode lors de sa redéfinition.
vous aurez remarqué que la redéfinition de la méthode clone ne renvoit pas un résultat de type Object, mais bien de type Matrix. C'est légal : tant que le type de retour est un sous-type de celui spécifier sur la méthode parente (dans notre cas, Matrix dérive bien de Object) vous avez le droit de modifier la signature. On parle alors de principe de covariance !
si vous tentez une réduction de visibilité, vous aurez droit à l'erreur de compilation suivante.
$> javac Matrix.java
Matrix.java:13: error: clone() in Matrix cannot override clone() in Object
    private Object clone() throws CloneNotSupportedException {
                   ^
  attempting to assign weaker access privileges; was protected
1 error
$> 

La visibilité des vos classes

Vous pouvez aussi définir des niveaux de visibilités sur vos définitions de classes. Différents cas sont à considérer.

Les classes publiques

Pour rappel, vous pouvez définir qu'une seule classe publique par fichier. Cette classe publique doit avoir très exactement le même nom que celui du fichier (sans l'extension, bien entendu) et de manière « case sensitive ». Une classe publique est accessible en tout point de votre programme Java. Je ne reviendrais pas plus sur cette possibilité, on en parle déjà depuis le début de ce tutoriel Java.

Les classes « package private »

Un fichier de code Java peut contenir bien plus qu'une seule définition de classe. Mais, outre la classe publique, les autres seront considérés comme des classes restreintes au package dans lequel est placé le fichier. Elles ne devront porter aucun qualificateur de visibilité.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
package fr.koor.sample;

// Classe visible uniquement dans le package fr.koor.sample.
class PrivateClass {
    
    public PrivateClass() {
        System.out.println( "PrivateClass constructor" );
    }
    
}

// Classe publique visible dans tout le projet. 
public class Sample {

    public Sample() {
        PrivateClass obj = new PrivateClass();
        // TODO
    }
    
}
Exemple de définition d'une classe restreinte au package courant

La visibilité des classes internes (inner classes)

Une classe interne est une classe définie à l'intérieur d'une autre classe. Si elle est définie sous forme de membre de la classe portante, alors vous pourrez utiliser un des quatre niveaux de visibilité classiques : public, protected, package private, private. Voici un exemple de définition d'une classe interne.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
package fr.koor.sample;
            
public class OuterClass {

    // Accessible uniquement dans la classe OuterClass.
    private class InnerClass {
    
    }

    public OuterClass() {
        InnerClass inner = new InnerClass();
        // TODO    
    }

}
Exemple de définition d'une classe interne


La notion de package en Java Les archives Java et l'outil jar