4. Membres d'une classe, visibilité

Les éléments déclarés à l'intérieur d'une classe Java sont appelés des membres. Il existe cinq types de membres:
  • des champs ;
  • des méthodes (les constructeurs d'une classe en font partie) ;
  • des blocs statiques ;
  • des blocs non statiques ;
  • des classes membre.
Nous avons déjà vu les classes membre et les blocs statiques.

4.1. Bloc non statique

Un bloc non statique est formellement identique à un bloc statique. Simplement, il ne comporte pas le modificateur static dans sa déclaration. Un tel bloc est utilisé pour initialiser les objets instances de cette classe. L'utilisation de ce genre de bloc peut être pratique, mais pose des problèmes lorsqu'il s'agit de tester unitairement des classes, comme nous le verrons.

4.2. Accès à un membre, visibilité

Pour pouvoir accéder à un membre non statique d’un objet, il faut posséder une référence sur cet objet. Si ce membre est statique, une référence sur la classe suffit. Ensuite, la machine Java suit un certain nombre de règles pour savoir si ce membre, statique ou non, que l’on cherche à lire ou modifier est accessible ou non. Pour cela, elle utilise les règles de visibilité. La visibilité d’un membre est définie par un mot clé qui peut prendre trois valeurs : private, protected ou public. Les règles d'accès sont les suivantes. Les membres private ne sont pas accessibles de l'extérieur d'une classe. On ne peut donc ni les lire, ni les modifier. Les membres public sont accessibles de toute classe. Ils sont donc lisibles et modifiables de tout objet. L'accès aux membres protected est un peu plus subtil. Ces membres ne sont accessibles que des instances de classes :
  • qui étendent la classe qui contient le membre protected ;
  • qui se trouvent dans le même package que celle qui contient le membre protected.
Voyons ceci sur un exemple. Construisons tout d'abord une classe Marin.

Exemple 28. Classe Marin pour les exemples de visibilité

public  class Marin {  
      
     protected String nom ;  
  
     protected String getNom() {  
         return  this.nom ;  
    }  
}

Toutes les classes se trouvant dans le même package que Marin ont accès aux deux membres protected de Marin : nom et getNom(). Les instances des classes qui étendent Marin ont également accès à ces membres. Si un membre est déclaré sans modificateur de visibilité, alors on dit qu'il est " package protected ". C'est-à-dire qu'il est accessible de toute classe du même package, en plus des classes qui étendent celle dans laquelle il est déclaré. Si l'on souhaite respecter les principes d'encapsulation enseignés par la programmation orienté objet, on évitera absolument de déclarer des champs autre que private, et on gardera à l'esprit qu'il n'y a pas beaucoup de différence entre public et protected. Il est effectivement très facile d'accéder à un champ protected d'une classe.

4.3. Les champs

Les champs sont les membres qui permettent de stocker des valeurs de types de base ou des objets. Un champ est déclaré suivant la syntaxe suivante :
 [visibilité] [static] typeDeChamp nomDuChamp [ = initialisation] ;
La visibilité du champ peut être omise (ce qui n'est pas conseillé), elle peut prendre les valeurs private, protected ou public. Le mot-clé static indique que le champ est statique. S'il est omis, le champ est non statique. Le type du champ peut être un type de base, ou tout nom de classe. Suit le nom du champ, et une éventuelle initialisation. Nous verrons dans la suite à quel moment exact cette initialisation est exécutée.

4.4. Signature d'une méthode

Une méthode est caractérisée par sa signature . La signature d’une méthode est composée :
  • de son nom ;
  • de la liste ordonnée des paramètres qu'elle accepte en entrée.
En particulier, les éléments suivants ne font pas partie de la signature d'une méthode :
  • le modificateur de visibilité ;
  • le type de retour ;
  • le modificateur static ;
  • la clause throws.
Une classe ne peut pas avoir deux méthodes qui possèdent même signature. Donc, il n'est pas possible d'avoir dans une classe deux méthodes de même nom et qui prenent les mêmes paramètres, mais avec des types de retour différents.

4.5. Les méthodes

Les méthodes sont les parties d’un objet qui permettent d’effectuer des traitements, de dialoguer avec d’autres objets, d’accéder à des ressources telles que des fichiers, des bases de données, des terminaux graphiques ou non, etc… Les règles de visibilité d'une méthode sont les mêmes que pour les champs. Une méthode peut donc être private, protected, ou public. Une méthode est déclarée de la façon suivante.
 [visibilité] [static] [signature] [throws exception] { … }
Si l'on omet le modificateur de visibilité pour une méthode (ce qui n'est pas conseillé), alors elle est package protected . De même que pour un champ, une méthode peut être déclarée static. On parle alors de méthode de classe. Elle est dans ce cas stockée dans un espace mémoire commun à tous les objets instances de cette classe. Une méthode statique ne peut accéder elle-même aux champs et méthodes non statiques de cette classe. Elle peut bien sûr recevoir en argument des champs non statiques et les traiter. Une méthode peut rencontrer une erreur lors de son exécution, par exemple une division par zéro ou une erreur d’ouverture de fichier. La dernière déclaration permet de préciser le comportement face à cette erreur. Nous verrons cela en détails plus loin. Une méthode pour laquelle on déclare un type de retour doit obligatoirement retourner une valeur. Le compilateur génèrera une erreur s'il se rend compte qu'il existe un chemin de sortie pour cette méthode, qui ne comporte pas de return. Ainsi, la méthode suivante ne compile pas.

Exemple 29. Erreur de compilation due à une valeur de retour non toujours définie

public  boolean testParite(int i) {  // erreur de compilation : la méthode 
                                    // ne retourne pas toujours une valeur  
     if (i %  2 ==  0)  
         return true ;  
}

À l'inverse, les deux méthodes suivantes compilent correctement.

Exemple 30. Types de retour correctement définis

public  boolean testParite1(int i) {  
     if (i %  2 ==  0)  
         return true ;  
     else  
         return false ;  
}  
  
 public  boolean testParite2(int i) {  
     if (i %  2 ==  0)  
         return true ;  
  
     return false ;  
}

4.6. Getters et Setters

Deux types de méthodes ont un statut particulier en Java : les getters et les setters . Afin de respecter le principe d'encapsulation, les champs non statiques d'une méthode sont déclarés private. Afin de pouvoir lire ces champs, et éventuellement d'en modifier la valeur, on ajoute à la classe un getter et un setter pour chacun de ses champs private. Le nom de ces deux méthodes est fixé par une convention universellement suivie, et sur laquelle de nombreux frameworks s'appuient. Il convient donc de la respecter ! Un getter commence par get, suivi d'un nom, qui est en général le nom du champ, commençant par une majuscule. Il retourne une valeur en général du même type que le champ associé. Le setter associé commence par set, suivi du même nom que le get. Il prend en paramètre un unique argument, du même type que le type de retour du getter associé. La présence d'un getter définit dans une classe une propriété. Si aucun setter associé n'est présent, alors cette propriété est en lecture seule. Voyons ceci sur un exemple.

Exemple 31. Getters et setters de la classe Marin

public  class Marin {  
      
     private String nomDuMarin ;  
     private String prenomDuMarin ;  
      
     public Marin(String nom) {  
        nomDuMarin = nom ;  
    }  
      
     public String getNom() {  
         return nomDuMarin ;  
    }  
      
     public String getPrenom() {  
         return prenomDuMarin ;  
    }  
      
     public  void setPrenom(String prenom) {  
         this.prenomDuMarin = prenom ;  
    }  
}

Cette classe Marin comporte deux propriétés :
  • nom, qui est en lecture seule. Le nom d'un marin est fixé lors de sa construction, et ne peut plus être modifié ;
  • prenom, que l'on peut lire et modifier.
On remarquera que le nom des champs n'est pas pris en compte lors de la détermination du nom d'une propriété. Enfin, précison ici que le langage Java ne définit pas la notion d' attribut . Cette notion est une notion UML et XML.
Java langage & API
Retour au blog Java le soir
Cours & Tutoriaux
Table des matières
Introduction : un peu d'histoire
1. Java : genèse d'un nouveau langage
Programmer en Java
1. Un premier exemple
1.1. Aperçu général, cycle de vie
1.2. Un premier programme
1.3. Programmes, applets, servlets, etc...
2. Une première classe
2.1. Écriture d'une classe
2.2. Instanciation de cette classe
3. Types de base, classes et objets
3.1. Types de base
3.2. Classes et objets
Classes Object et String
1. Introduction
2. La classe Object
2.1. La méthode toString()
2.2. La méthode clone()
2.3. La méthode equals()
2.4. La méthode hashCode()
2.5. La méthode finalize()
2.6. La méthode getClass()
3. La classe String
3.1. Introduction
3.2. Construction d'un objet de type String
3.3. Concaténation, StringBuffer et StringBuilder
3.4. Concaténations de chaînes de caractères depuis Java 8
3.5. Concaténations de chaînes de caractères depuis Java 11
3.6. Extraction d'une sous-chaîne de caractères
3.7. Comparaison de deux chaînes de caractères
3.8. Méthodes de comparaisons lexicographiques
3.9. Méthode de recherche de caractères
3.10. Méthode de modification de chaîne
3.11. Méthode de duplication
3.12. Support de l'unicode, internationalisation
Structure d'une classe
1. Introduction
2. Classes
2.1. Classes publiques
2.2. Classes internes
2.3. Classe membre
2.4. Classes locales
2.5. Classes anonymes
2.6. Le mot-clé this
3. Éléments statiques
3.1. Champ statique
3.2. Cas des constantes
3.3. Bloc statique
3.4. Classe membre statique
4. Membres d'une classe, visibilité
4.1. Bloc non statique
4.2. Accès à un membre, visibilité
4.3. Les champs
4.4. Signature d'une méthode
4.5. Les méthodes
4.6. Getters et Setters
5. Constructeur, instanciation
5.1. Chargement d'une classe
5.2. Constructeurs d'une classe
5.3. Instanciation d'un objet
5.4. Destruction d'objets
5.5. Le mot-clé final
6. Énumérations
6.1. Déclaration d'une énumération
6.2. Classe énumération
6.3. Méthode toString()
6.4. Méthode valueOf()
6.5. Méthode values()
6.6. Méthode ordinal()
6.7. Méthode compareTo()
6.8. Constructeurs privés
6.9. Classe utilitaire : EnumSet
Noms, opérateurs, tableaux
1. Introduction
2. Identificateurs, noms et expressions
2.1. Identificateurs et noms
2.2. Expressions
3. Opérateurs, ordre d'exécution
3.1. Ordre d'exécution
3.2. Les opérateurs
3.3. Les opérateurs ++ et --
3.4. Les opérateurs % et /
3.5. Les opérateurs <<, >> et >>>
3.6. L'opérateur instanceof
3.7. Les opérateurs &, | et ^
3.8. Les opérateurs && et ||
3.9. L'opérateur ? ... :
3.10. Les opérateurs d'affectation
4. Tableaux
4.1. Création d'un tableau
4.2. Initialisation d'un tableau
4.3. Utilisation d'un tableau comme un Object
4.4. Tableaux de tableaux
4.5. Copie de tableaux
5. Blocs, boucles et contrôles
5.1. Blocs
5.2. Mots-clés réservés
5.3. Tests : if et switch
5.4. Boucles : for, while, do ... while
5.5. Commandes continue et break
5.6. Commandes return et goto
Nombres, précision, calculs
1. Introduction
2. Calculs
2.1. Précision
2.2. Codage des nombres flottants
2.3. Le mot-clé strictfp
2.4. Conversion de types
3. Dépassements de capacité
3.1. Cas des entiers
3.2. Cas des flottants
3.3. Bibliothèques BigInteger et BigDecimal
4. Fonctions mathématiques
4.1. Fonctions usuelles
4.2. Générateurs aléatoires
5. Classes enveloppe
5.1. Associer les types de base à des objets
5.2. Auto-boxing
Héritage, abstraction, interfaces
1. Introduction
2. Abstraction et encapsulation
2.1. Abstraction
2.2. Encapsulation
3. Héritage
3.1. Définition de l'héritage
3.2. Conséquences pour les membres
3.3. Polymorphisme
3.4. Empêcher l'héritage
4. Classes abstraites
5. Interfaces
5.1. Introduction
5.2. Définition
5.3. Java 8 et les interfaces
5.4. Utilisation des interfaces
5.5. Définition de constantes dans les interfaces
5.6. Utilité des interfaces
Packages
1. Introduction
2. Notion de paquet
2.1. Déclaration d’appartenance à un paquet
2.2. Chargement d’une classe
2.3. Choix de nom
3. Archives, chemin de recherche, classpath
3.1. Archives
3.2. Variable CLASSPATH
3.3. Notion de classloader
3.4. Bilan sur les classes chargées
3.5. Visibilité
3.6. Conseils d'écriture, bibliothèque standard
Exceptions
1. Introduction
2. Erreurs et Exceptions
2.1. Classe Throwable, notion de stack trace
2.2. Classe Error
2.3. Classe RuntimException
2.4. Classe Exception
3. Déclenchement d'une exception
3.1. Exceptions déclenchées par la JVM
3.2. Exceptions déclenchées par l'application
4. Capter une exception
4.1. Traiter une exception localement
4.2. Code de captage
5. Créer ses propres exceptions
Entrées / sorties
1. Introduction
2. Notion de fichier
2.1. Introduction
2.2. La classe File
2.3. Construction d'une instance de File
2.4. Méthodes exposées
3. Flux de sortie
3.1. Introduction, notion de flux
3.2. Écriture de caractères, classe Writer
3.3. Bufferisation, construction d'un flux sur un autre
3.4. Utilisation de PrintWriter
3.5. Écriture d'octets, OuputStream
3.6. Écriture de types primitifs : DataOutputStream
3.7. Écriture d'objets : ObjectOutputStream
4. Flux d'entrée
4.1. Introduction
4.2. Lecture de caractères, classe Reader
4.3. Bufferisation, lecture ligne par ligne
4.4. Lecture d'octets : InputStream
4.5. Lecture de types primitifs : DataInputStream
4.6. Lecture d'objets : ObjectInputStream
5. Lecture et écriture de flux croisés
5.1. Introduction
5.2. Lire des caractères sur un flux binaire : classe InputStreamReader
5.3. Écrire des caractères sur un flux binaire : classe OutputStreamWriter
5.4. Remarque sur les jeux de caractères
6. Serialization d'objets
6.1. Enjeu de la sérialization d'objets
6.2. Serialization d'un objet
6.3. Sérialization d'une grappe d'objets
6.4. Première surcharge : méthode writeObject() readObject()
6.5. Deuxième surcharge : utilisation d'un externalizer
6.6. Troisième surcharge : utilisation d'un objet proxy
7. Flux compressés
7.1. Introduction
7.2. Flux de type gzip
7.3. Flux de type zip