
FAQ JavaConsultez toutes les FAQ
Nombre d'auteurs : 53, nombre de questions : 231, dernière mise à jour : 7 juin 2009
Sommaire→Concepts fondamentaux→Les mots-clés- Quels sont les différents mot-clés du langage Java ?
- Que signifient les mots-clés public, private et protected ?
- Que signifie le mot-clé static ?
- Que signifie le mot-clé final ?
- Que signifient les mots-clés this et super ?
- Que signifie le mot-clé strictfp ?
- Que signifie le mot-clé transient ?
- Que signifie le mot-clé volatile ?
- Et le goto en Java ?
- Comment utiliser les mots-clés break et continue ?
- Pourquoi mon switch ne veut-il pas compiler ?
- [Java 5.0] Comment fonctionne l'ellipse (nombre d'arguments variable) ?
- [Java 5.0] Comment fonctionne la boucle for étendu ?
- [Java 5.0] Comment parcourir un tableau à 2 dimensions avec un for étendu ?
- [Java 5.0] Qu'est-ce que l'import static ?
- Quelle est la différence entre 'import' et 'import static' ?
- Qu'est-ce que l'opérateur ternaire?
Un mot-clé est tout simplement un mot qui a été reservé pour une utilisation spéciale par le langage; vous ne pouvez donc pas employer des mots-clés comme noms de variable, ou nom de classe.
Pour avoir une liste de tous les mots-clés du langage Java et de leurs significations, vous pouvez lire cet article.
Ces trois mots clefs du langage java définissent la portée d'une variable, d'une méthode ou d'une classe. Il existe en fait quatre modificateurs d'accessibilité. Le quatrième est le modificateur vide (rien, pas de modificateur). Il ne faut pas confondre ce dernier avec public.
Voici les caractéristiques de ces modificateurs, du plus permissif au plus restrictif :
| Mot-clé | Portée | Remarques |
|---|---|---|
| public | Les variables, méthodes ou classes publiques sont accessibles par tout objet. | Il ne peut y avoir qu'une seule classe publique par .java et celle-ci doit obligatoirement porter le nom du fichier .java |
| "rien" | Les variables, méthodes ou classes définies sans modificateur sont accessibles par toute classe appartenant au même package. | Attention : les variables sans modificateur ne sont pas accessibles aux classes fille définies dans un autre package. |
| protected | Les variables, méthodes ou classes définies comme protégées ne sont accessibles que par les classes filles et classes du même package.. | |
| private | Les variables, méthodes ou classes définies comme privées ne sont accessibles que par la classe dans laquelle elles sont définies. | Il est fortement conseillé de déclarer comme privés tous les attributs d'une classe, et de créer des méthodes de type get/set pour y accéder. |
Naturellement, toute méthode, variable ou classe est accessible dans la classe ou elle est définie.
Remarque : il y a deux cas particuliers où l'abscence de mot-clé de visibilité ne correspond pas à une visibilité "package-only" :
- Tous les membres (attributs et méthodes) d'une interface ou d'une annotation sont obligatoirement public.
- Tous les constructeurs d'une enum sont obligatoirement private.
Le mot-clé static est utilisable pour des variables, méthodes, classes internes ou blocs de code.
Devant une variable ou méthode :
Le mot clé static devant une variable (ou méthode) indique que celle-ci n'appartient pas à une instance particulière de la classe. Les variables ou méthodes statiques appartiennent à la classe elle-même. On peux ainsi les utiliser sans avoir une instance créée. De nombreuses classes ont des membres ou méthodes statiques. Par exemple la classe Math :
System.out.println(Math.PI);
//affiche la valeur de PI
System.out.println(Math.abs(-1));
//affiche la valeur absolue de -1
Voici quelques remarques :
- On peut aussi manipuler une variable ou méthode statique à partir d'une instance de la classe.
- Pour faire des variables statiques des constantes, il faut combiner le mot-clé static avec le mot-clé final.
- Les méthodes statiques, étant indépendantes de toute instance, n'ont pas accès aux variables ou méthodes non statiques.
Devant un bloc de code :
Le mot-clé static devant un bloc de code indique que celui-ci ne sera exécuté qu'une fois. L'exécution se fait lors du chargement de la classe par le ClassLoader. On peut utiliser ces blocs, par exemple, pour initialiser des variables statiques complexes.
publioc class MaClasse{
public static Map uneVariableStatique = new HashMap();
static{
uneVariableStatique.put("une clef","une valeur");
uneVariableStatique.put("une autre clef","une autre valeur");
//etc .
}
}
Devant une classe interne :
Pour plus d'informations sur ce cas, reportez à la QR Quels sont les différents types de classes internes (nested classes) ?.
Pour une variable membre, elle peut être initialisée :
private final int i = 5;
private final int i;
{
i = 5;
}
private final int i;
public Exemple() {
i = 5;
}
Si la classe possède plusieurs constructeurs, la variable doit être correctement initialisée quel que soit le constructeur utilisé (sinon le compilateur provoquera une erreur).
Pour une variable membre static, elle peut être initialisée :
private static final int X = 5;
private static final int X;
static {
X = 5;
}
Devant un paramètre d'une méthode : empêche l'attribution d'une nouvelle valeur au paramètre:
public void methode(final List<Object> liste, final int entier){
entrier = 0; // interdit
liste = new ArrayList<Object>(); // interdit
}
Pour une variable locale (ou pour un paramètre de la méthode), cela permet également de référencer l'instance dans une classe anonyme.
Les mots-clés this et super désignent respectivement des références sur l'instance courante et sur la classe mère. Voici un exemple qui devrais mettre en valeur cette définition plutôt succincte :
public MaClasse extends ClasseMere {
private String attribut;
/** On peut acceder aux constructeurs de la super-classe*/
public MaClasse(String uneValeur){
super(uneValeur);
//on appelle ici le constructeur de la classe mère
}
/** On peut aussi accéder aux constructeurs de la classe elle-même*/
public MaClasse(String uneValeur){
this("une valeur par défaut");
//on appelle ici le constructeur définis un peu plus haut
}
/** En général l'appel à this est superflu lors d'appels à une méthode*/
public void uneMethode(){}
public void doubleAppel(){
//les deux lignes suivantes sont équivalentes
this.uneMethode();
uneMethode();
}
/** L'appel à this peut être utile pour bien différencier
* les variables de classe des variables de méthodes
*/
public void uneMethode(String attribut){
this.attribut = attribut;
//ici, la variable de classe prend la valeur de la variable
//passée en paramètre de la méthode
}
/** On peut aussi faire appel aux méthodes de la super-classe*/
public void uneAutreMethode(){
//on peux faire quelque chose en plus avant
super.uneAutreMethode();
//mais aussi après
}
}
A noter que dans le cas d'une classe interne (non static), le mot clé this permet également de récupérer l'instance de la classe englobante :
public class Englobante {
private Object attribut;
public final class Interne {
private Object attribut;
private switch() {
Englobante.this.attribut = this.attribut;
}
}
}
Englobante.this fait donc référence à l'instance de la classe englobante et this fait référence à la classe interne.
Ces deux mots-clés sont très liés au concept d'héritage. Pour plus d'informations, voir Qu'est-ce que l'héritage ?
Lien : Qu'est-ce que l'héritage ?
Ce mot clé, qui est une abréviation de Strict floating point, s'applique en tant que modificateur d'accès. Ou plus simplement, on l'utilise de la même manière que les mot-clés public ou synchronized. Avec quelques restrictions : strictfp s'applique en tant que modificateurs de classes, d'interfaces ou de méthodes d'une classe et en aucun cas au constructeur ou aux méthodes d'une interface. L'entité affectée est alors dite "FP-strict".
Java effectue les calculs en garantissant une priorité de la gauche vers la droite.
/** classe FP-strict */
public strictfp class FPDemo {
public static void main(String[] args) {
double d = 8e+307;
/** affiche 4 * d /2 donc 2 * d */
System.out.println(4 * d / 2);
/** affiche 2 * d */
System.out.println(2 * d);
}
}
Mathématiquement ces deux expressions sont identiques, mais interprétées dans un langage, il en va autrement. Java impose un parenthésage : (4*d)/2, et dans notre cas (4*d) produit un dépassement de capacité, donc un résultat infini. En revanche, la deuxième expression produit bien un résultat correct.
Notons que le mot-clé oblige l'implémentation de la JVM à évaluer l'expression tel que prévu dans la spécification du langage. Ne pas faire usage de ce mot-clé ne garantit pas que la JVM réalisera ce calcul de la sorte. Une JVM peut en effet avoir le droit, si la méthode n'est pas FP-strict, d'utiliser des types intermédiaires différents pour éviter de provoquer un dépassement de capacité ou pour s'adapter à l'architecture de la machine. Dans ce cas les deux expressions pourraient, en fonction de la JVM, produire des résultats différents.
Le mot-clé transient est lié à la sérialisation des classes Java (voir : Qu'est-ce que la sérialisation ?). Il permet d'interdire la sérialisation de certaines variables d'une classe.
// la classe que nous allons sérialiser
class Writeable implements java.io.Serializable {
// entier transient
public transient int var1 = 4;
// entier normal
public int var2 = 19;
}
Si nous sérialisons une instance de cette classe, la variable 'var1' ne sera pas sauvegardée, lors de la désérialisation elle prendra la valeur 0, malgré la présence de la valeur par défaut 4. L'attribution d'une valeur par défaut se fait lors de l'instanciation de l'objet ! Or, la méthode consistant à lire un objet depuis un fichier ne crée pas cette instance explicitement. Donc demo n'est jamais initialisé avec sa valeur par défaut. De plus, comme cet attribut est transient, il n'est pas écrit dans le fichier. Cela implique que demo ne reçoit aucune valeur et contient donc 0.
Ce mot-clé trouve des applications dès lors qu'une donnée sensible ne doit en aucun cas apparaître dans un fichier. Un mot de passe par exemple. Mais ce mot-clé peut également permettre de "remettre à zéro" certaines valeurs. Dans le cas d'un jeu, on pourra ainsi ne pas sauvegarder le temps de jeu depuis le début de la partie.
Le mot-clé volatile est utilisé sur les variables qui peuvent être modifiées de manière asynchrone. C'est à dire que plusieurs threads peuvent y accéder simultanément. Ces accès peuvent être pour la lecture et/ou la modification du contenu.
En indiquant que la variable est volatile, on oblige la JVM à rafraîchir son contenu à chaque fois qu'elle est utilisée. On est ainsi certain que la valeur de la variable n'est pas une valeur mise en cache, mais bel et bien sa valeur exacte. Ainsi chaque thread a accès à la valeur la plus récente de la variable.
Remarque : ce mot-clé est relativement peu utilisé et toutes les JVM ne le prennent pas en compte.
while(true) {
// instructions
if( condition ) {
// quitte la boucle
break;
}
if( condition ) {
// retourne au début de la boucle
continue;
}
}
Pour plus d'informations sur break et continue, regardez le lien ci dessous.
Comme nous l'avons vu, il n'y a pas de goto en Java, mais il est possible d'associer un label à une instruction de boucle. Ce label, utilisé en conjonction avec l'instruction break, permet de savoir à quel niveau le break sera effectif.
Un label est une chaîne suivie de ":" et qui se place devant l'instruction de boucle.
Voici un exemple :
Boucle1: while(true){
System.out.println("Boucle 1");
Boucle2: for(int ind=0; ind < 10; ind++) {
System.out.println("Boucle 2");
Boucle3: while(true){
System.out.println("Boucle 3");
break Boucle2;
}
}
break;
}
A votre avis, qu'affiche l'exécution de ce morceau de code ?
Remarque : le label peut aussi être utilisé en conjonction avec le mot clé continue. De la même manière, le label indique à quel niveau de boucle le continue s'applique.
L'instruction switch n'accepte que les types de base, c'est à dire:
- byte
- char
- short
- int
- long
- float
- double
Il n'est pas possible de faire un switch sur une String par exemple. Il faudra passer par une séquence de if .. else if:
if ("A".equals(maString))
{
// ...
}
else if ("B".equals(maString))
{
// ...
}
Il est possible aussi que le compilateur reporte cette erreur:
case expressions must be constant expressions
En effet l'instruction case n'accepte que des constantes.
Des constantes explicites comme dans ce cas:
case 1:
...
break;
Ou une variable déclarée comme constante à l'aide du mot-clé final.
Ainsi si vous obtenez une erreur avec le code suivant:
int a = 1;
switch (maVar)
{
case a:
...
}
Vous ne l'aurez plus avec celui-ci:
final int a = 1;
switch (maVar)
{
case a:
}
L'ellipse permet de créer des méthodes (ou des constructeurs) avec un nombre d'arguments variable. On utilise pour cela trois points (...) après le type des arguments, par exemple la méthode suivante accepte un nombre quelconque de String :
public void method (String... args) {
//
}
A l'intérieur de la méthode, le paramètre args est un tableau contenant les différents paramètres passés à la méthode. Ainsi, cette méthode peut s'utiliser de la manière suivante :
// avec 1 paramètre :
method ("param1");
// avec plusieurs paramètres :
method ("param1", "param2", "param3");
// sans paramètres :
method ();
En réalité, il ne s'agit ni plus ni moins qu'une nouvelle manière de déclarer une méthode avec un tableau en paramètre. En effet, pour le compilateur, cette déclaration correspond à la déclaration d'une méthode avec un tableau de String en paramètre. Et lors de son utilisation, les différents paramètres de l'ellipse sont automatiquement stockés dans un tableau. Ainsi, les exemples d'utilisations ci-dessus correspondent en réalité au code suivant :
// avec 1 paramètre :
method ( new String[]{"param1"} );
// avec plusieurs paramètres :
method ( new String[]{"param1", "param2", "param3"} );
// sans paramètres :
method ( new String[]{} );
On ne peut toutefois utiliser qu'un seul type d'argument variable par méthode, et il doit obligatoirement être en dernière position dans la liste des paramètres.
Le nouveau for de Java 5.0 permet de parcourir tous les éléments d'un élément 'itérable' sans se soucier de son fonctionnement. Il se présente de la forme suivante :
for ( Type variable : Iterable ) {
// ...
}
- Type correspond au type de l'élément de la variable qui contiendra les différentes valeurs.
- variable est justement le nom de cette variable à l'intérieur de la boucle.
- Iterable est l'élément dont les valeurs seront parcourues. Ce doit obligatoirement être soit un tableau, soit une classe implémentant l'interface Iterable.
L'interface Iterable décrit une unique méthode iterator() retournant un Iterator qui sera utilisé par la boucle for pour parcourir les différents éléments. Les Collections de Java implémentent bien entendu cette interface. De plus, le type de la variable doit correspondre au type paramétré de l'Iterator, ce qui permet de se passer de cast et d'utiliser un code sécurisé :
List<String> list = new ArrayList<String>();
list.add("chaine1");
list.add("chaine2");
list.add("chaine3");
list.add("chaine4");
// Affichage des éléments en MAJUSCULE :
for (String s : list) {
System.out.println(s.toUpperCase());
}
Si la boucle est utilisée avec un tableau, le type de la variable doit correspondre avec le type du tableau. Si elle est utilisée avec un objet implémentant l'interface Iterable, le type de la variable doit correspondre au type paramétré d'Iterable (par exemple,* List<String> implémente Iterable<String> et permet donc d'utiliser une String dans la boucle).
Et du fait de son fonctionnement, il est possible de l'utiliser avec n'importe quel type de classe du moment que cette dernière implémente correctement l'interface Iterable<T> (T étant le type à utiliser dans la boucle for...
Lien : Présentation de Tiger : la nouvelle boucle for
Lien : java.lang.Iterable
Le code suivant parcourt un tableau à 2 dimensions d'entiers et les affiche.
int tab[][] = { {1, 2, 3}, {7, 8, 9} };
for(int ligne[] : tab)
{
for(int element : ligne)
{
System.out.println("Item : " + element);
}
}
L'import static permet d'importer les éléments statiques d'une classe afin d'alléger l'écriture du code. Cela permet en effet de ne pas préfixer les éléments statiques par le nom de la classe. Par exemple, les deux codes suivant sont identiques mis à part que le second utilise un import static pour accéder aux méthodes de la classe Math :
public Class Test {
public void calcul (int i) {
Math.round(Math.cos(i*(Math.PI/6)-Math.PI/2)*Math.E);
}
}
import static java.lang.Math.*;
public Class Test {
public void calcul (int i) {
round(cos(i*(PI/6)-PI/2)*E);
}
}
Ce mécanisme est proche du mot-clef using namespace du C++. Toutefois, il est conseillé de limiter son utilisation afin de faciliter la lecture du code et éviter des conflits potentiels. On peut pour cela importer seulement l'élément qui nous intéresse, par exemple :
import static java.lang.System.out;
public Class Test {
public void print () {
out.println("Message d'information");
System.err.println("Message d'erreur");
}
}
Le import et le import static n'ont pas la même fonction.
Le import se fait sur une classe (ou un ensemble de classes via le *) et permet d'éviter de spécifier le package de la classe à chaque fois qu'on l'utilise (en partant du principe que l'on se trouve dans un paquage différent de celui contenant la classe).
Le import static a un fonctionnement similaire mais pour les méthodes et les attributs statiques d'une classe ou d'une interface et les membres d'une enum. En effet, il permet d'éviter de spécifier la classe de la méthode ou de l'attribut statique à chaque fois qu'on l'utilise (en partant du principe que l'on se trouve dans une classe différente de celle contenant la méthode ou l'attribut).
Illustration :
Soit les classes package_a.ClasseA et package_b.ClasseB.
Pour "utiliser" la classe ClasseA dans la classe ClasseB il faut normalement spécifier le package comme suit :
package package_b;
public class ClasseB {
private package_a.ClasseA a;
public CLasseB() {
a = new package_a.ClasseA();
}
}
Le import permet de supprimer cet inconvénient en indiquant à l'avance dans quel package se situe la classe ClasseA et permet donc d'écrire
package package_b;
import package_a.ClasseA;
public class ClasseB {
private ClasseA a;
public CLasseB() {
a = new ClasseA();
}
}
On se retrouve avec la même écriture que si la classe ClasseA était dans le même package que la classe ClasseB.
Supposont maintenant que la classe ClasseA posséde la méthode statique suivante :
public static void staticMethod() {
// code
}
Pour l'appeler depuis la classe ClasseB il faut normalement écrire
ClasseA.staticMethod();
En faisant un import static de la méthode on se retrouve comme si la méthode statique faisait partie de la classe ClasseB et nous permet d'écrire
import static package_a.ClasseA.staticMethod;
...
staticMethod();
Ces deux imports sont indépendants, si je ne fait que le import je pourrait écrire
ClasseA
mais je devrais écrire
ClasseA.staticMethod();
A l'inverse je pourrais très bien ne faire que l'import static et je me retrouverais à devoir écrire
package_a.ClasseA
mais à pouvoir écrire directement
staticMethod();
En résumé il est important de bien retenir que
- le import ne permet la simplification d'écriture que pour les classes
- le import static ne permet la simplification d'écriture que pour les méthodes et les attributs statiques d'une classe ou interface et les membres d'une enum
- le import et le import static sont indépendants
On le considère souvent comme une syntaxte réduite de l'instruction if traditionnelle.
Mais c'est avant tout un opérateur au sens où il produit une valeur.
(A) ? B : C
Si A alors le résultat est B sinon c'est C.
A noter que B est évalué seulement si A et que C est évalué seulement si non A.
Il fournit un moyen compact d'écrire une affectation conditionnée:
chaine=(nouvelleChaine!=null) ? nouvelleChaine : "";
à la place de:
if (nouvelleChaine!=null) {chaine=nouvelleChaine;} else {chaine="";}
NB: Utilisé abusivement il devient rapidement illisible.


















