Portée des variables en Java
Comprenez où une variable Java est visible — portée locale, d'instance, de classe (statique) et de bloc.
La portée d'une variable est la région du programme où vous êtes autorisé à utiliser son nom. Un nom déclaré à l'intérieur d'une méthode n'est pas visible depuis une autre ; un nom déclaré à l'intérieur d'une boucle for n'est pas visible après la boucle. Le compilateur applique ces règles strictement — tentez d'accéder à un nom en dehors de sa portée et votre code ne compilera pas.
Java distingue trois grandes catégories de variables, chacune avec sa propre portée : les variables locales (y compris les paramètres), les champs d'instance et les champs de classe. Ce chapitre se concentre sur la portée locale, car c'est elle qui façonne les méthodes que vous avez écrites jusqu'ici.
Variables locales
Une variable locale est déclarée à l'intérieur d'une méthode ou d'un bloc, et n'est visible que depuis le point de déclaration jusqu'à la fin du bloc englobant :
public static int demo() {
int x = 1; // x is in scope from here...
System.out.println(x);
return x;
} // ...to here
// System.out.println(x); // ERROR: x not visible outside demo()Les variables locales ne sont pas initialisées par Java — vous devez leur affecter une valeur avant de les lire. Le compilateur effectue un suivi de l'affectation définie et refuse de compiler la lecture d'une variable locale potentiellement non initialisée :
public static void unassigned() {
int n;
// System.out.println(n); // ERROR: variable n might not have been initialized
n = 5;
System.out.println(n); // OK now
}Cela diffère des champs d'instance et de classe, que Java initialise par défaut (les nombres à 0, boolean à false, les références d'objet à null).
Portée de bloc
Les accolades définissent un bloc. Les variables déclarées à l'intérieur d'un bloc ne sont visibles qu'à l'intérieur de ce bloc, y compris dans les blocs imbriqués, mais pas à l'extérieur :
public static void blocks() {
int outer = 10;
if (outer > 0) {
int inner = 20;
System.out.println(outer + inner); // both visible
}
// System.out.println(inner); // ERROR: inner is out of scope here
}Cela s'applique aux blocs if, for, while, do-while, switch, ainsi qu'aux blocs { ... } nus que vous écrivez pour regrouper du code. La variable de boucle d'un for n'est dans la portée qu'à l'intérieur de la boucle :
for (int i = 0; i < 3; i++) {
System.out.println(i);
}
// System.out.println(i); // ERROR: i is out of scopeSi vous avez besoin de l'indice après la boucle, déclarez i à l'extérieur :
int i = 0;
for (; i < 3; i++) {
System.out.println(i);
}
System.out.println("stopped at " + i);Paramètres de méthode
Les paramètres sont des variables locales qui se trouvent être initialisées par l'appelant. Leur portée est l'ensemble du corps de la méthode, de l'accolade ouvrante à l'accolade fermante :
public static int square(int n) { // n is in scope from here...
return n * n;
} // ...to hereChaque appel dispose de ses propres emplacements de paramètres, de sorte que les appels récursifs ne se perturbent pas mutuellement.
Masquage (Shadowing)
Java interdit de déclarer une variable locale portant le même nom qu'une variable déjà dans la portée dans le même bloc ou un bloc englobant de la même méthode :
public static void clash() {
int x = 1;
// int x = 2; // ERROR: variable x is already defined
if (true) {
// int x = 3; // ERROR: x is in scope from the outer block
}
}C'est plus strict que ce que permettent de nombreux langages — Java suppose que réutiliser un nom à l'intérieur d'une méthode est une erreur. (Les champs d'instance et de classe peuvent être masqués par des variables locales du même nom ; nous aborderons cela dans la partie POO du livre.)
Notez que deux blocs frères peuvent réutiliser un nom, car leurs portées ne se chevauchent pas — aucun n'est visible à l'intérieur de l'autre :
public static void siblings() {
for (int i = 0; i < 3; i++) { /* first i */ }
for (int i = 0; i < 3; i++) { /* a fresh i — legal, the first is gone */ }
}Durée de vie vs. portée
La portée indique où vous pouvez utiliser le nom. La durée de vie indique combien de temps le stockage sous-jacent existe. Elles coïncident généralement — lorsqu'une variable locale sort de la portée, son emplacement disparaît — mais pour les objets, la distinction est importante :
public static String makeName() {
String s = new String("Ada"); // s in scope here
return s;
} // s out of scope, but the String object lives onLa variable s disparaît, mais l'objet String auquel elle faisait référence est toujours en vie — l'appelant conserve une référence, de sorte que le ramasse-miettes ne le récupérera pas. Les variables locales limitent les noms, pas les durées de vie des objets.
Un aperçu des champs d'instance et de classe
À l'intérieur d'une classe, à côté des méthodes, vous pouvez déclarer des champs — des variables dont la portée est toute la classe. Il en existe deux types :
- Les champs d'instance appartiennent à chaque objet créé à partir de la classe. Chaque
Dogpossède son proprenameetage. - Les champs de classe (déclarés
static) appartiennent à la classe elle-même ; il n'existe qu'une seule copie partagée.
public class Counter {
private static int totalCreated = 0; // class field — one shared
private int value = 0; // instance field — one per Counter
public Counter() {
totalCreated++;
}
public void inc() {
value++; // refers to this instance's field
}
}Nous n'irons pas plus loin ici — Classes et Objets présente la classe elle-même, et Attributs de classe couvre les champs d'instance en détail. En résumé : les champs sont visibles partout dans la classe ; les variables locales ne sont visibles que dans le bloc où elles sont déclarées.
Exemple pratique
La suite
La portée vous indique où vit un nom. La question suivante — une fois qu'un argument a été passé à une méthode — est ce que la méthode reçoit réellement. Le chapitre sur le passage par valeur dissipe la confusion la plus courante en Java : ce qui se passe réellement lorsque vous passez une référence d'objet.