Par valeur on entend en général le résultat de l’évaluation d’une expression du langage de programmation. Si on prend un point de vue mathématique, une valeur peut être à peu près n’importe quoi, un entier (de ℤ), un ensemble d’entiers etc. Si on prend un point de vue technique, une valeur est ce que l’ordinateur manipule facilement, ce qui entraîne des contraintes : par exemple, un entier n’a pas plus de 32 chiffres binaires (pas plus de 32 bits). Dans les descriptions qui suivent nous entendons valeur plutôt dans ce sens technique. Par variable on entend en général (selon le point de vue technique) une case qui possède un nom où peut être rangée une valeur. Une variable est donc une portion nommée de la mémoire de la machine.
Il y a en Java deux grandes catégories de valeurs les scalaires et les objets. La distinction est en fait technique, elle tient à la façon dont ces valeurs sont traitées par la machine, ou plus exactement sont rangées dans la mémoire. Les valeurs scalaires se suffisent à elle-mêmes. Les valeurs des objets sont des références. Une référence « pointe » vers quelque chose (une zone de la mémoire) — référence est un nom civilisé pour pointeur ou adresse en mémoire. Écrivons par exemple
Les variables x
et t
sont deux cases, qui contiennent
chacune une valeur, la première valeur étant scalaire et la seconde
une référence.
Un schéma résume la situation.
Le tableau {1, 2, 3}
correspond à une zone de mémoire qui
contient des trois entiers, mais la valeur qui est rangée dans la
variable t est une référence pointant vers cette zone.
Le schéma est une simplification de l’état de la mémoire,
les zones mémoires apparaissent comme des cases (les variables portent
un nom) et les références apparaissent comme des flèches qui pointent
vers les cases.
Si x et y sont deux variables, la construction
y = x
se traduit par une copie de la valeur
contenue dans la variable x dans la variable y, que cette valeur
soit une référence ou non.
Ainsi, le code
produit l’état mémoire simplifié suivant.
Le schéma permet par exemple de comprendre pourquoi (ou plutôt
comment) le programme suivant affiche 4
.
Il existe une référence qui ne pointe nulle part null, nous pouvons l’employer partout où une référence est attendue.
Dans les schémas nous représentons null ainsi :
Puisque null ne pointe nulle part il n’est pas possible
de le « déréférencer » c’est à dire d’aller voir où il pointe.
Un essai par exemple de t[0]
déclenche une erreur à l’exécution.
L’opérateur d’égalité == de Java s’applique aux valeurs — ainsi que l’opérateur différence !=. Si l’égalité de deux scalaires ne pose aucun problème, il faut comprendre que == entre deux objets traduit l’égalité des références et que deux références sont égales, si et seulement si elles pointent vers la même zone de mémoire. Autrement dit, le programme
affiche t==u : true, t==v : false. Les références t et u sont égales parce qu’elles pointent vers le même objet. Les références t et v qui pointent vers des objets distincts sont distinctes. Cela peut se comprendre si on revient aux états mémoire simplifiés.
On dit parfois que == est
l’égalité physique.
L’égalité physique donne parfois
des résultats surprenants. Soit le programme Test
simple suivant
Une fois compilé et lancé, ce programme affiche t==u : true, t==w : false. Ce qui révèle que les chaînes (objets) référencés par t et u sont exactement les mêmes, tandis que w est une autre chaîne.
La plupart du temps, un programme a besoin de savoir si deux chaînes ont exactement les mêmes caractères et non pas si elles occupent la même zone de mémoire. Il en résulte principalement qu’il ne faut pas tester l’égalité des chaînes (et à vrai dire des objets en général) par ==. Dans le cas des chaînes, il existe une méthode equals spécialisée (voir B.6.1.3) qui compare les chaînes caractère par caractère. La méthode equals est l’égalité structurelle des chaînes.