Déclaration des Variables Locales et d’Instance
Maintenant que nous avons des méthodes, nous devons parler un peu des variables qu’elles peuvent créer ou utiliser. Comme vous pourriez vous rappeler du Chapitre 1, les variables locales sont celles définies dans une méthode ou un bloc, tandis que les variables d’instance sont celles qui sont définies comme membre d’une classe. Regardons un exemple :
public class Lion {
int faim = 4;
public int nourirAnimauxZoo() {
int gouter = 10; // Variable locale
if(gouter > 4) {
long heureRepas = gouter++;
faim--;
}
return gouter;
}
}
Dans la classe Lion, gouter et heureRepas sont des variables locales uniquement accessibles dans leurs blocs de code respectifs, tandis que faim est une variable d’instance créée dans chaque objet de la classe Lion.
L’objet ou la valeur retournée par une méthode peut être disponible en dehors de la méthode, mais la référence de variable gouter disparaît. Gardez cela à l’esprit en lisant ce chapitre : toutes les références de variables locales sont détruites après l’exécution du bloc, mais les objets qu’elles pointent peuvent encore être accessibles.
Modificateurs de Variables Locales
Il n’y a qu’un seul modificateur qui peut être appliqué à une variable locale : final. Facile à retenir, n’est-ce pas ? Lors de l’écriture de méthodes, les développeurs peuvent vouloir définir une variable qui ne change pas pendant le déroulement de la méthode. Dans cet exemple de code, essayer de changer la valeur ou l’objet que ces variables référencent entraîne une erreur de compilation :
public void bilanSanteAnimauxZoo(boolean estWeekend) {
final int repos;
if(estWeekend) repos = 5; else repos = 20;
System.out.print(repos);
final var girafe = new Animal();
final int[] amis = new int[5];
repos = 10; // NE COMPILE PAS
girafe = new Animal(); // NE COMPILE PAS
amis = null; // NE COMPILE PAS
}
Comme montré avec la variable repos, nous n’avons pas besoin d’assigner une valeur lorsqu’une variable final est déclarée. La règle est seulement qu’elle doit recevoir une valeur avant de pouvoir être utilisée. Nous pouvons même utiliser var et final ensemble. Contrastez cela avec l’exemple suivant :
public void bilanSanteAnimauxZoo(boolean estWeekend) {
final int repos;
if(estWeekend) repos = 5;
System.out.print(repos); // NE COMPILE PAS
}
La variable repos pourrait ne pas avoir reçu de valeur, par exemple si estWeekend est false. Puisque le compilateur n’autorise pas l’utilisation de variables locales qui pourraient ne pas avoir reçu de valeur, le code ne compile pas.
Est-ce que l’utilisation du modificateur final signifie que nous ne pouvons pas modifier les données ? Non. L’attribut final ne fait référence qu’à la référence de variable ; le contenu peut être librement modifié (en supposant que l’objet n’est pas immuable).
public void bilanSanteAnimauxZoo() {
final int repos = 5;
final Animal girafe = new Animal();
final int[] amis = new int[5];
girafe.setNom("Georges");
amis[2] = 2;
}
La variable repos est un primitif, donc c’est juste une valeur qui ne peut pas être modifiée. D’autre part, le contenu des variables girafe et amis peut être librement modifié, à condition que les variables ne soient pas réassignées.
Bien que cela puisse ne pas sembler évident, marquer une variable locale comme final est souvent une bonne pratique. Par exemple, vous pourriez avoir une méthode complexe dans laquelle une variable est référencée des dizaines de fois. Ce serait vraiment mauvais si quelqu’un venait réassigner la variable au milieu de la méthode. Utiliser l’attribut final, c’est comme envoyer un message aux autres développeurs pour qu’ils laissent la variable tranquille !
Variables Effectivement Finales
Une variable locale effectivement finale est une variable qui n’est pas modifiée après avoir été assignée. Cela signifie que la valeur d’une variable ne change pas après avoir été définie, indépendamment de si elle est explicitement marquée comme final. Si vous n’êtes pas sûr qu’une variable locale soit effectivement finale, ajoutez simplement le mot-clé final. Si le code compile toujours, la variable est effectivement finale.
Selon cette définition, lesquelles des variables suivantes sont effectivement finales ?
public String amisZoo() {
String nom = "Henri l'Hippo";
var taille = 10;
boolean mouille;
if(taille > 100) taille++;
nom.substring(0);
mouille = true;
return nom;
}
Rappelez-vous, un test rapide pour savoir si une variable est effectivement finale est d’ajouter final à la déclaration de variable et de voir si cela compile toujours. Dans cet exemple, nom et mouille sont effectivement finales et peuvent être mises à jour avec le modificateur final, mais pas taille. La variable nom reçoit une valeur à la ligne 2 et n’est pas réassignée. La ligne 6 crée une valeur qui n’est jamais utilisée. Rappelez-vous du Chapitre 4, “APIs de Base”, que les chaînes de caractères sont immuables. La variable taille n’est pas effectivement finale car elle pourrait être incrémentée à la ligne 5. La variable mouille reçoit une valeur une seule fois et n’est pas modifiée par la suite.
Paramètres Effectivement Finaux
Rappelez-vous du Chapitre 1 que les paramètres de méthode et de constructeur sont des variables locales qui ont été pré-initialisées. Dans le contexte des variables locales, les mêmes règles concernant final et effectivement final s’appliquent. C’est particulièrement important dans le Chapitre 7 et le Chapitre 8, “Lambdas et Interfaces Fonctionnelles”, puisque les classes locales et les expressions lambda déclarées dans une méthode ne peuvent référencer que des variables locales qui sont final ou effectivement finales.
Modificateurs de Variables d’Instance
Comme les méthodes, les variables d’instance peuvent utiliser des modificateurs d’accès, tels que private, package, protected, et public. Rappelez-vous, l’accès package est indiqué par l’absence de tout modificateur. Nous couvrons chacun des différents modificateurs d’accès plus loin dans ce chapitre.
Les variables d’instance peuvent également utiliser des spécificateurs optionnels, décrits dans le Tableau .
Modificateur | Description |
---|---|
final | Spécifie que la variable d’instance doit être initialisée avec chaque instance de la classe exactement une fois |
volatile | Instruit la JVM que la valeur dans cette variable peut être modifiée par d’autres threads |
transient | Utilisé pour indiquer qu’une variable d’instance ne doit pas être sérialisée avec la classe |
On dirait que nous n’avons besoin de discuter que de final dans ce cours ! Si une variable d’instance est marquée final, alors elle doit recevoir une valeur lorsqu’elle est déclarée ou lorsque l’objet est instancié. Comme une variable locale final, elle ne peut pas recevoir une valeur plus d’une fois, cependant. La classe OursPolare suivante démontre ces propriétés :
public class OursPolare {
final int age = 10;
final int poissonsManges;
final String nom;
{ poissonsManges = 10; }
public OursPolare() {
nom = "Robert";
}
}
La variable age reçoit une valeur quand elle est déclarée, tandis que la variable poissonsManges reçoit une valeur dans un initialiseur d’instance. La variable nom reçoit une valeur dans le constructeur sans argument. Ne pas initialiser une variable d’instance (ou assigner une valeur plus d’une fois) entraînera une erreur de compilation. Nous parlons de l’initialisation des variables final plus en détail lorsque nous couvrons les constructeurs dans le prochain chapitre.
Les variables d’instance reçoivent des valeurs par défaut basées sur leur type quand elles ne sont pas définies. Par exemple, int reçoit une valeur par défaut de 0, tandis qu’une référence d’objet reçoit une valeur par défaut de null. Le compilateur n’applique pas de valeur par défaut aux variables final, cependant. Une variable d’instance final ou une variable statique final doit recevoir une valeur quand elle est déclarée ou comme partie de l’initialisation.