Friday, May 21, 2004

En Java, chaque objet est different... Ou presque...

Pour ceux d'entre vous qui connaissent le langage Java, vous savez surement que chaque objet cree n'est en fait qu'un pointeur vers l'adresse de l'objet qui est en memoire. La Virtual Machine (JVM) s'occupe elle meme d'allouer la memoire necessaire a l'objet, de le creer et retourne un pointeur vers ce nouvel objet. Ce qui est d'ailleurs une force du langage, puisque le developpeur n'a pas a gerer lui meme les allocations de memoire ainsi que de liberer cet espace memoire une fois l'utilisation de cet objet terminee. Le Garbage Collector s'occupe de liberer les objets n'ayant plus de references. (L'ennui c'est qu'on ne sais jamais quand (et si) le Garbage Collector liberera cet objet). Enfin bon, mon post n'a pas pour but de philosopher sur les vertues du Garbage Collector.

Comme la majorite de vous, j'ai toujours cru que chaque nouvel objet cree avait une adresse differente. Cette affirmation est exacte dans 99% des cas. Mais, en lisant dans mon bouquin de certification dimanche dernier, j'ai appris une chose bien interessante concernant les java.lang.String.

Prenons comme exemple le code suivant:
public class JambonClass {

String stringOne = "jambon";
String stringTwo = "jambon";

public static void main(String[] args) {
JambonClass obj = new JambonClass();

if (obj.stringOne == obj.stringTwo) {
System.out.println("Pareil");
} else {
System.out.println("Pas pareil");
}
}
}


Logiquement, l'output de la methode main() devrait retourner:
Pas pareil

Tout simplement parce que lorsque l'on veut comparer le contenu de deus Strings, on utilise la methode equals().

Cependant, pour cet exemple, l'output sera plutot:
Pareil

Pourquoi? Non pas parce que le contenu des deux String est identique (quoi qu'il faut admettre que c'est un peu vrai...), mais plutot parce qu'ils ont la meme adresse memoire.

Comment cela s'explque-t-il?
L'explication est simple, mais pas evidente si l'on n'a pas lu la specification de la JVM. En effet, a la compilation de la declaration de la premiere String (stringOne), le compilateur l'ajoute dans son "Pool de Strings Literales". Plus tard, lors de la compilation de la declaration de la deuxieme String (stringTwo), le compilateur verifiera si la chaie de caractere est deja presente dans le Pool. Si la String est deja dans le pool (ce qui est le cas), le compilateur lui assigne l'adresse de la string du pool, au lieu d'allouer de l'espace memoire pour un nouvel objet.

Ce mode de fonctionnement s'explique au fait que les Strings sont des objets immuables. Bref, quand une String est creer, il est impossible de la modifier. D'ailleurs, vous remarquerez que les methodes de l'objet String ont l'attribut static puisqu'elles retournent des nouvelles String au lieu de modifier l'originale. (ex. toUpperCase(), toLowerCase(), etc.)

C'est pour cela que lorsque l'on compare deux objets a l'aide de l'operatueur ==, Java compare les references des objets (adresse memoire) et non leur contenu.

Il faut donc etre vigilant, puisque les Strings d'un programme ne sont pas toujours dans la meme Classe comme dans l'exemple ci-dessus. Et surtout, ne jamais comparer d'objets a l'aide de l'operateur ==.

J'ai cru bon de partager cela avec vous car, il faut bien l'admettre, il est difficile de developper une application aujourd'hui sans utiliser la classe java.lang.String.

Bon code.

1 comment:

Anonymous said...

Héhé william en même temps il avait prévenu moi j'ai tout compris j'ai lu la mm choses dans mes cours ya quelques années et ce fût aussi pour moi une grande découverte, quoique logique quand on y repense :)
Comparaison de string ou de pointeur, c'est devenu un classique à force ;p