Published by

Il y a 12 ans -

Temps de lecture 3 minutes

Légende urbaine : En java, les objets sont passés par référence

La semaine passée, le site TheServerSide.com a vu se rejouer un de ces éternels débats du monde Java. Le sujet : le mode de passage des paramètres de méthode dans le langage Java.

Cette querelle resurgit malheureusement trop souvent lorsque la communauté Java est amenée à échanger avec les autres communautés, ou même en interne. Le problème trouve son origine dans une simplification abusive d’un des mécanismes de fonctionnement du langage Java. Vous avez peut-être déjà entendu l’affirmation suivante : lors d’un appel de méthode en java, les paramètres de type primitif sont passés par valeur, et ceux de type objet sont passés par référence. Si vous deviez arrêter ici votre lecture, sachez au moins ceci : cette affirmation est erronée. Et nous allons tâcher d’expliquer pourquoi.

Rappel

Un rappel pour ceux d’entre vous qui ne sont pas familiers du sujet. Lors d’un appel de méthode, on constitue une pile d’opérandes que l’on transmet à la méthode. Un paramètre transmis par valeur est copié sur cette pile d’opérandes. C’est donc une copie que la méthode manipule, et il ne peut pas y avoir de modification de la valeur d’origine. Lorsque l’on passe un paramètre par référence, on ne se contente pas de placer une adresse mémoire (ou pseudo adresse mémoire) sur la pile, on définit un nouvel alias pour une valeur existante. Le mot n’est pas choisi au hasard, les deux variables sont complètement équivalentes.

Une des origines de la confusion

Les spécifications de la JVM définissent les types primitifs et les types références (les classes, les tableaux et les interfaces). Les variables associées aux types références contiennent une pseudo adresse mémoire (appelée référence) vers un espace alloué dynamiquement. Et c’est ici que l’amalgame se fait. Une variable de type référence en Java ne correspond pas à la définition stricte d’une référence. Elle correspond au principe connu sous le nom de Handle. Un Handle est un type qui contient une pseudo adresse mémoire et qui autorise l’accès à la valeur sous-jacente.

La preuve

Le code suivant illustre par un exemple simple, le mode de gestion des paramètres dans le langage Java :

package fr.xebia.lang;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

import org.junit.Test;

public class PassByValueTest {
  @Test
  public void with_primitive_type() {
    int value = 5;

    modify(value); // on transmet bien une copie de la variable
    assertEquals(5, value); // la preuve
  }

  private void modify(int copy) {
    copy = copy - 112;
  }

  @Test
  public void with_reference_type() {
    StringBuilder value = new StringBuilder();

    nullify(value); // la valeur n'est pas transmise par référence
    assertNotNull(value); // la preuve

    modify(value); // un Handle ou une référence Java autorise l'accès à l'objet sous-jacent
    assertEquals("it works as expected", value.toString());
  }

  // Un passage par référence modifierait également la variable 'value'
  private void nullify(StringBuilder copy) {
    copy = null;
  }

  private void modify(StringBuilder copy) {
    copy.append("it works as expected");
  }
}

Le mot de la fin

Le mode de passage des paramètres revêt bien plus d’importance dans un langage comme le C++, qui propose plusieurs modes, que dans le langage Java où un unique mode est utilisé. Nous sommes plus que conscients que, pour la plupart d’entre nous, cette précision ne bouleversera pas notre perception du langage Java. Toutefois, pour que la communauté Java et les autres communautés puissent échanger, il est nécessaire que nous partagions le même vocabulaire.

Java MythBusters: En java, les paramètres pour les types primitifs sont passés par valeur, et pour les types objets c’est une copie de la référence Java (un Handle) qui est transmise.

Published by

Commentaire

35 réponses pour " Légende urbaine : En java, les objets sont passés par référence "

  1. Published by , Il y a 12 ans

    Handle est un terme bien peu transparent. On l’utilise pour tellement de choses ! Je préfère dire que les objets sont passés par pointeur car en Java, tout est pointeur même si ça dit pas son nom :-)

  2. Published by , Il y a 12 ans

    J’aimerais que tu précises ta démonstration, et en quoi l’ambiguité peut poser problème.

    Lors d’un appel de méthode, le Handle passé est passé par valeur. C’est pourquoi il ne peut pas être modifié. Cependant, l’objet référencé, lui, peut changer, ce qui est bien le principe d’un passage par référence.

  3. Published by , Il y a 12 ans

    Ta méthode nullify ne prouve rien.
    Lorsque tu passes un objet par référence, ceci signifie que tu passes en fait une référence vers un objet ; autrement dit, un pointeur constant vers la zone mémoire où réside ton objet. Par conséquent que tu ne peux pas réassigner une valeur à ta référence. Donc, si effectivement le paramètre passé en argument de nullify était une vraie référence, alors le compilo aurait refusé de compiler ton code pour assignement de valeur à une … constante (sachant que, contrairement à C++, Java n’autorise pas la surcharge des opérateurs (donc de =))

    Donc, ce ce n’est pas l’appel assertNotNull(value) qui est la preuve … mais plutôt le résultat de la compilation qui n’aboutit pas à une erreur.

  4. Published by , Il y a 12 ans

    @Florent Bécart
    « C’est pourquoi il ne peut pas être modifié. Cependant, l’objet référencé, lui, peut changer, ce qui est bien le principe d’un passage par référence. »

    Non, pas tout à fait. Une référence n’est qu’un pointeur constant vers une valeur (un objet par exemple). Donc, cette référence, même copiée par valeur, ne peut être modifiée. Que le compilateur autorise cette assignation signifie alors que en fait, ce n’est pas une véritable référence qui est passée, mais un objet tiers (une valeur), autrement dit un handler.

  5. Published by , Il y a 12 ans

    @Waddle: C’est uniquement une question de précision dans le vocabulaire, je me refuse à employer le terme pointeur car le lien entre référence Java et pointeur mémoire est laissé à la discrétion des fournisseurs de JVM.

    @Miguel: Il n’est pas possible de transférer un mécanisme du compilateur C++ au compilateur et la machine virtuelle Java aussi simplement.

    En Java, les structures (ou Value type) n’existent pas. Toutes les variables contiennent donc une adresse mémoire (ou pseudo adresse mémoire). Lorsque l’on parle d’un passage par référence, il faut bien comprendre que c’est la variable qui serait passée par référence et non pas l’objet qu’elle désigne.

  6. Published by , Il y a 12 ans

    @Miguel
    Je ne suis pas vraiment d’accord avec toi. Une référence n’est pas toujours constante (d’ailleurs, il y a const pour ça en C++) tu peux modifier la référence et cela modifie également l’appelant (exemple du C++).

    Un pointeur est autre chose, c’est la valeur d’adressage de l’objet. Celle-ci est passée par valeur (une copie) en Java. Par conséquent, changer la valeur de ce pointeur n’impact que cette copie de la même manière que changer un int n’impact que la méthode en cours et pas l’appelante.

    En Java il s’agit bien de pointeurs. Il suffit de changer les . en -> et d’ajouter une * et le comportement est le même. D’ailleurs, souvent, le terme Handle est indiqué au choix avec Pointer (en anglais dans le texte), mais c’est sûr que Handle ca fait plus sexy que Pointer XD

    Pour finir, je dirais que notre exception préférée nous indique de suite qu’en Java, on se trimballe des pointeurs partout ;-)

  7. Published by , Il y a 12 ans

    @Steve

    Un pointeur mémoire n’implique pas forcément mémoire physique.

  8. Published by , Il y a 12 ans

    Autre manière de décrire le mécanisme : en java tout est passé par valeur (au contraire de langages comme ADA ou C++, par exemple, qui supportent le passage par valeur ou par référence). En ce qui concerne les objets Java, on ne les manipule qu’au travers de références. La seule chose que l’on peut faire c’est donc passer les valeurs de ces références.
    Et tout est bien qui finit bien.

  9. Published by , Il y a 12 ans

    @waddle
    « Lorsque l’on parle d’un passage par référence, il faut bien comprendre que c’est la variable qui serait passée par référence et non pas l’objet qu’elle désigne. »
    Oui, dans Java. D’où le sujet de ton article au sujet des références vs. handlers. Je n’ai pas dis le contraire. J’ai juste indiqué que ton exemple n’était pas suffisant pour monter ceci.

    Sinon, en réponse à ton post 6, au sujet des références constantes et C++ :
    En C++, une référence sur un objet toto n’est en fait qu’un :
    toto * const
    C’est à dire un pointeur constant et qui donc doit être initialisé. (A l’époque où je faisais encore du C++, cette notation et celle de la référence (avec le &) était marquée comme équivalente).
    Ce qui fait que tu peux écrire :
    const toto & => const toto * const
    Autrement dit une référence sur un objet constant ! (Ce n’est pas la référence qui est constante, puisque par sa nature même, elle est par définition constante !)
    Maintenant, tu peux avoir une erreur en C++ avec les références lorsque tu les réassigne non pas pour cause de réassignement de référence … mais parce que ton objet référencé ne fournit pas d’implémentation à l’opérateur ‘=’ ! Donc, si ton objet implémente l’opérateur ‘=’ alors ce que tu crois être une réassignation n’est en fait qu’une copie d’un objet existant vers un autre.

  10. Published by , Il y a 12 ans

    Je n’en reviens que cette discussion démarrée sur un groupe de discussion LinkedIn puisse autant susciter l’intérêt de TheServerSide. J’ai l’impression que la discussion a été démarrée par un troll à l’origine.

    Dommage qu’il n’y ait pas plus de discussions autour du futur du langage Java, de l’OpenJDK et de la politique d’Oracle.

  11. Published by , Il y a 12 ans

    J’ai bossé sur une façade Java utilisée en JNI dans un autre programme .Net.
    Ces problèmes de vocabulaire sont primordiaux dans les contrats d’interfaces pour éviter des confusions et communiquer correctement entre les projets.
    Donc, oui je pense que ces discussions ont un vrai intérêt.

  12. Published by , Il y a 12 ans

    Évidement quand tu fais copy = null, bah tu ne fais rien [..] C’est complétement HS. 1 ==2 ?… Quand tu passes un objet par référence, tu passes bien la référence et tu peux modifier l’objet lié. Mais modifier, ce n’est pas remplacer…

  13. Published by , Il y a 12 ans

    Mon intention lorsque j’ai choisi d’écrire ce billet sur ce sujet polémique n’était pas de la relayer. Cette discussion sur TheServerSide est une discussion que j’avais déjà vu se produire à l’identique sur un autre forum des années auparavant.

    Tout comme Farouk, je souhaite que le langage continue d’évoluer. La réalité est que les évolutions du langage Java sont parfois inspirées par d’autres langages, et que les discussions se font également avec des gens d’autres communautés. Le cas présent est, à mon sens, une imprécision de vocabulaire. Le terme est antérieur au langage Java, nous ne pouvons pas le redéfinir. Et n’est il pas possible d’aller au delà de ce point ?

    L’article tente de le faire en s’appuyant sur un contre-exemple simple.

    @thierryler
    Je vais tenter d’éclaircir mes propos: la variable contient une référence Java, mais la variable elle-même n’est pas transmise par référence.

  14. Published by , Il y a 12 ans

    Oui.

    Quand tu fais
    1 Toto toto = new Toto(« 1 »);
    2 Lala lala = transform(toto);

    Avec
    3 public Lala transform(Toto toto) {
    4 …
    5 }

    A la ligne 2, tu passes la réf toto à la méthode :
    toto -> variable -> réf -> obj

    Si à la ligne 4 tu réaffecte la var :
    4 toto = new Toto(« 2 »);

    En fait ça change la var seulement, mais localement.

    Par contre, tu peux modifier les attributs de l’obj, via sa ref, via sa var :
    4b toto.setName(« 3 »);

    Dans ce cas le changement est effectif en dehors de la méthode.

    Après on peut s’amuser avec final :
    3 public Lala transform(final Toto toto) {

    Dans ce cas, la ligne 4 devient incorrecte mais la ligne 4b est toujours valide. Et de nombreux juniors se font avoir là-dessus, croyant que final bloque les changements alors que ça ne bloque que l’affectation.

  15. Published by , Il y a 12 ans

    Pour s’en convaincre, il suffit de compiler la classe et d’utiliser javap pour voir le bytecode généré. C’est d’autant plus simple à comprendre puisque javap affiche ligne par ligne le code exécuté.

    Le bytecode Java permet de voir les opérandes mises sur la pile.

  16. Published by , Il y a 12 ans

    @Steve : D’accord avec toi

  17. Published by , Il y a 12 ans

    « En java, les paramètres pour les types primitifs sont passés par valeur, et pour les types objets c’est une copie de la référence Java (un Handle) qui est transmise. »

    Un article petit par la taille et grand par sa richesse.

  18. Published by , Il y a 11 ans

    Je me permets de relancer la polémique presque deux ans plus tard, ce qui n’est pas forcement utile mais bon… (je précise également que je n’ai pas eu le courage de lire les commentaires jusqu’au bout, donc je vais surement faire des redites)

    Perso j’ai un peu de mal avec l’exemple fourni, en effet, j’ai l’impression qu’il sous entend que l’appel de « value = null; » modifie la valeur de l’objet désigné par value.
    Or ce n’est jamais le cas, puisqu’en java tout appel du type value = new Objet() n’écrase pas la valeur précédente de value en mémoire mais créée simplement un nouvel objet en mémoire et associe une référence de cet objet à value. C’est le garbage collector qui se charge ensuite (pour peu qu’aucune référence ne pointe vers le premier objet) d’effacer les données.

    Pour reprendre l’exemple donné, le résultat serait le même SANS utilisé de fonction intermédiaire (nullify). On a en effet:
    StringBuilder value = new StringBuilder();
    StringBuilder copy = value;
    copy = null;
    assertNotNull(value);

    A noter que concernant les lignes 2 et 3 c’est exactement ce qu’il se passe dans la méthode nullify (à ceci près qu’ici on se situe dans le même espace de nom)

    Au final il me semble que je rejoins les propos de l’auteur en ce sens que tout cela démontre qu’il n’existe pas d’accès « direct » à une adresse mémoire en java (en tout cas rien d’équivalent à un pointeur C par exemple), néanmoins, contrairement à ce que tend à affirmer ce billet, ceci n’a rien à voir avec le mécanisme de passage de référence des paramètres de fonctions en java, mais concernant bel et bien le mécanisme de gestion de ces références.
    Donc OUI les objets java sont passés par référence, c’est juste que des appels comme objet = null; ou objet = new Objet(), modifier simplement cette référence, et non l’objet en mémoire.
    En gros:
    StringBuilder b = new StringBuilder() // un objet StringBuilder est créé sur le tas
    b.append(« une string);
    b = new StringBuilder(); // un NOUVEL objet est créé sur le tas, de ce fait
    // deux StringBuilder existe jusqu’au passage du garbage collector.

  19. Published by , Il y a 10 ans

    hello,

    je suis pas totalement d’accord avec votre conclusion. Suite a votre article nous avons fait un test avec un collégue a ce sujet.
    En effet lorsque dans le nullify on met a null l’objet il est a null le temps de vie de la méthode et c’est sur un objet intermédiaire puisque qu’on a pas le droit de mettre une référence à null.

    Le bout de code ci dessous essaye de montrer qu’en fait malgré le fait qu’on passe à null et que un objet intermédiaire soit crée on passe quand meme notre objet par référence :
    public class memory {
    public static void main(String[] args)
    {
    StringBuilder t = new StringBuilder(« toto »);
    test(t);
    System.out.println(t.toString());
    }

    public static void test(StringBuilder t) {
    t.append(« hahaha »);
    t = null;
    if (t == null) {
    System.out.println(« t est null »);
    }
    }
    }

    voici ce qu’on obtient sur la console :
    t est null

    totohahaha

    Du coup le t.append fonctionne bien et si nous passions notre objet par copie sur le syso suivant on aurait pas le « totohahaha »

    Peut être ai-je faux quelque part. Je tiens a préciser que je suis juste pas totalement en accord avec la conclusion sur le cas là. :)

    cordialement,

  20. Published by , Il y a 10 ans

    J’adore ce post de blog qui continue de vivre depuis des mois :-)

    @Nicolas G :

    Vous passez bien une copie, mais du pointeur ! Pas de l’objet lui même ! Donc, quand vous faites un .append(), vous déréférencez le pointeur (avec l’opérateur « point ») et donc vous ajouter bien des caractères. Quand vous affecter « null », vous ne déréférencez pas (pas d’opérateur « point ») donc vous changer uniquement le pointeur. Votre pointeur est null, mais pas celui de la méthode main qui pointe toujours vers la zone mémoire.

  21. Published by , Il y a 10 ans

    Cet article est totalement faux tout comme le dernier commentaire.

    Une référence par définition est constante dans le sens ou on ne peut pas séparer le référé de sa référence. Ce qui n’a rien avoir avec le mot const du c++ (comme ecrit dans un commentaire + haut) qui indique que l’objet référencé ne sera pas modifié.

    int i=9;
    test(i);

    void test(const int& reference) { affiche i }
    //la on dit au compilateur que l’objet référencer par reference ne sera pas modifié.

    En java on ne fait QUE des passages par référence sur les Objets.
    Le code de Nicolas G est tout a fait juste et prouve les mauvaises conclusions de l’article. Le message de RobPred5 va également dans ce sens.

    Dans la methode test de NicolasG on passe l’objet par référence. Le .append ne déréférence absolument pas l’objet mais modifie l’objet référé (t du main).
    le t = null; déréférence l’objet t (celui de la methode test) mais qui n’est visible que dans la methode test. Donc une fois que la méthode est terminée l’objet t de la methode test qui est une référence disparait également.
    Donc il ne reste plus que l’objet t du main.

    On déréférence avec un = et si je fais dans la method test:
    StringBuilder u = new SB(« tata »);
    t = u;

    La durée de vie de l’objet u n’est que la methode elle meme donc quand je reviens dans le main t ne peut etre egale a u car u va etre GBC(garbage collecté) car il est sorti des {} de la méthode (ca durée de vie).

    Il est possible de dire que l’on peut déréférencer un objet mais il est pas possible de dire qu’on fait un passage par copie.

  22. Published by , Il y a 10 ans

    De plus voici ce qui se passe rellement en schématisant (si null était un objet).
    public class memory {
    public static void main(String[] args)
    {
    StringBuilder t = new StringBuilder(« toto »); //création de lobjet dans le tas et de t dans la pile du main
    test(t);
    System.out.println(t.toString());
    }//suppression de t dans la pile du main et de lobjet dans le tas

    public static void test(StringBuilder t) {//creation de t dans la pile de test qui pointe sur lobjet créé dans le tas (ds le main)
    t.append(« hahaha »);//modification de l’objet qui est dans le tas du main
    t = null;//modification de t dans la pile de test et « creation de null » dans le tas(en reallité ce n’est vrai que pour t = new SB() mais je schematise)
    if (t == null) {
    System.out.println(« t est null »);
    }
    } // suppression de t dans la pile de test et de null dans le tas
    }

  23. Published by , Il y a 10 ans

    @Stan

    Je pense que c’est vous qui vous trompez car vous utilisez mal le mot « référence ». Ce mot a un sens depuis que le C++ est C++. Parlé de référence pour l’argument d’une méthode Java est totalement abusif.

    void foo()
    {
    int i=9;
    test(i);
    }

    void test(int& reference) { i=0}

    A la sortie de test(), le i de foo() vaut 0. C’est cela une référence. En Java, ce n’est pas le cas donc parlé de référence est abusif.

    Comme dirait Akim Demaille : « En Java, tout est pointeur ».
    Et comme dirait Scott Stanchfield : « Java is strictly pass-by-value, exactly as in C » (http://javadude.com/articles/passbyvalue.htm)

  24. Published by , Il y a 10 ans

    on parle des objets et non des types primitifs.
    Aprés le débat de laisser les types primaires int double etc… est un autre débat et sun s’en est défendu plusieurs fois.

    Maintenant les liens que vous envoyez sont surement trés intéressants il n’en demeurre pas moins qu’Oracle ne dit pas comme vous et parle bien de reference et pour le coup ils sont une bonne référence niveau Java.
    http://docs.oracle.com/javase/tutorial/java/javaOO/arguments.html

    aprés jouer sur les mots me semble un petit peu puéril voir « geek » (ils disent troll maintenant) et Ca n’empeche pas de dire que la conclusion de l’article est fausse : Pas de passage par valeur mais bien par reference pour un objet.(Ou pointeur si cela vous convient mieux)

  25. Published by , Il y a 10 ans

    @Stan

    Tiré de votre lien : « Reference data type parameters, such as objects, are also passed into methods *by value* ». Oracle fait donc bien une erreur en parlant de référence puisqu’ils ne font pas autorité en OOP à ce que je sache. Ou alors c’est celui qui a créé NullPointerException qui a fait une erreur ?

    Quant à ce qu’Oracle soit la référence en Java, je ne suis pas d’accord. La référence, c’est James gosling et il dit dans « The Java Programming Language » :

    « Some people will say incorrectly that objects in Java are « pass by reference. » The term pass by reference *properly means* that when an argument is passed to a function, the invoked function gets a reference to the original value, not a copy of its value. If the function modifies its parameter, the value in the calling code will be changed because the argument and parameter use the same slot in memory. […] There is exactly one parameter passing mode in Java (pass by value) and that helps keep things simple. »

    Pour votre gouverne, je ne suis pas puéril en voulant jouer sur les mots. Les mots ont un sens et c’est parce qu’on ne le respect pas qu’on en est à avoir ce genre de discussion.

    « (Ou pointeur si cela vous convient mieux) » : bref…

  26. Published by , Il y a 10 ans

    Je suis effrayé par vos abus de langage, et remercie Waddle d’avoir pu terminer cette discussion.

  27. Published by , Il y a 10 ans

    Bonjour,

    1 kilo octet ne fais plus 1024 Octets mais 1000 Octets Wouaaah….vite une mise à jour….

    Je dirai que pour la phrase : « lors d’un appel de méthode en java, les paramètres de type primitif sont passés par valeur, et ceux de type objet sont passés par référence » c’est un peu la même chose, c’est une histoire d’effort intellectuel.

    Pour ma part, je sais qu’en JAVA les paramètres primitif sont passés par valeur et les autres par pointeur. Cet article l’illustre très bien. Le passage par référence n’existe pas en JAVA !

    Explications:

    Le passage par valeur : Tout le contenu de la variable est recopié sur la pile par l’appelant et sera dépilé par l’appelé. L’appelé manipule donc UNE COPIE de l’objet.

    Le passage par pointeur : L’adresse mémoire de l’objet est déposé sur la pile par l’appelant et sera dépilé par l’appelé. L’appelé manipule l’objet ORIGINAL.

    Le passage par référence : L’adresse mémoire de la VARIABLE pointant l’objet est déposé sur la pile par appelant. L’appelé manipule donc la VARIABLE ORIGINAL et par voie de conséquence l’OBJET ORIGINAL. Seul ce mode, le passage par référence, permet de modifier le contenu des variables de l’appelant.

    En espérant avoir apporté quelques lumières à quelques uns.

    Au plaisir de vous lire.

    Romuald

  28. Published by , Il y a 10 ans

    Super, d’avoir le code Java mais pas le résultat. :-/

  29. Published by , Il y a 10 ans

    Philippe Mougin dit très clairement les choses.

  30. Published by , Il y a 9 ans

    Heu…
    Je pense que ce petit test en dessous prouve bien que l’expression de passage par référence est la plus juste:

    class A{
    String chaine;
    }
    A a = new A();
    a.chaine = « Chaine de test initiale »;
    A b = a;
    System.out.println(« Avant modification par variable b, a.chaine =  » + a.chaine);
    b.chaine = « Ceci est la chaine modifié »;
    System.out.println(« Apres modification par b, a.chanine =  » + a.chaine);

    Résultat:
    Avant modification par variable b, a.chaine = Chaine de test initiale
    Apres modification par b, a.chanine = Ceci est la chaine modifié

  31. Published by , Il y a 9 ans

    Je sais pas trop pour l’affirmation de Gosling, je viens de faire le test, voilà mon code : (CODE)

    MutableInt idx = new MutableInt();
    String str = ClasseUtil.getStr(idx);
    System.out.println(idx.get()+ » $ « +str);

    public class MutableInt {
    private int n = 0;
    public MutableInt(int i) { this.n = i; }
    public MutableInt() {}
    public int get() { return this.n; }
    public void set(int i) { this.n = i; }
    }

    public static String getStr(MutableInt ix) {
    int nb = new Random().nextInt(9), i = -1;
    ix.set(nb);
    BufferedReader is = null;
    String str = «  »;
    // Blablabla retourne str
    }

    (/CODE) et mon résultat en une ligne:
    4 $ Contenu

  32. Published by , Il y a 8 ans

    Après avoir tester, le résultat n’est pas le même, j’ai juste une question simple : c’est quoi la différence entre :

    private void nullify(StringBuilder copy)

    et

    private void modify(StringBuilder copy)

    ?

  33. Published by , Il y a 8 ans

    Hello,
    En fait le nullify instancie un nouvel objet.
    En ce qui concerne copy.append(« it works as expected »);
    Tu as http://stackoverflow.com/questions/8011338/how-is-stringbuffer-implementing-append-function-without-creating-two-objects

    « As internal structure there is a char array, not a String. It is initially built with length 16 and will be increased every time the capacity is exceeded. If the Strings to append fit within the char array, there is no need to create new Objects. »

    Voili voilou.

  34. Published by , Il y a 7 ans

    Cet article démontre juste la mauvaise compréhension général de la gestion des objets en java… et du fonctionnement de la table des symboles de la JVM.
    Je m’explique : considérons les zone mémoire numérotée en décimal
    avec ce bout de code simplifié pour la compréhension des néophytes :
    1 public static void main(String[] arg){
    2 String sInit = « toto »;
    3 toTata(sInit);
    4 System.out.println(sInit); //Affichera toto
    5}
    6
    7 public void toTata(String s){
    8 s= « tata »;
    9}

    Voila ce qu’il se passe dans la JVM :

    Environnement du main :
    sInit qui pointe vers zone mem 1 contenant « toto »

    Environnement du toTata : on créait un nouveau pointeur s qui pointe vers la même valeur que sInit
    avant la ligne 8 s pointe vers zone mem 1 contenant « toto »
    après la ligne 8 s pointe sur zone mem 2 contenant « tata » car nous créons un nouvel Objet la valeur du pointeur de s est modifié.

    en schéma :
    (attention pour les puristes je ne représente pas ici les différents scopes de la tds)
    avant la ligne 8 :
    sInit -> toto
    s -> toto
    après la ligne 8 :
    sInit -> toto
    s -> tata

    quand on revient dans le main on a absolument pas changé la valeur du pointeur de sInit!
    donc quand on demande d’afficher sInit il nous donne bien ce que contient la zone mem 1 c’est a dire « toto » !

    de même écrire = null ou = « tata » a exactement le même effet on réattribue la valeur du pointeur et rien d’autre!

    Le problème des String est récurent, String est bien un objet mais il s’agit d’un Objet non mutable, c’est a dire qu ‘il ne contient pas de fonction permettant de modifier son contenu. Si vous souhaitez le rendre mutable, il vous suffit de créer une classe qui contient une string comme attribut et de modifié sa valeur comme bon vous semble en créant vos fonctions de modification.

  35. Published by , Il y a 7 ans

    Pour que tout soit clair en terme de vocabulaire (^_^)
    indices que je vais utiliser:
    (a) = quelque soit le langage objet utilisé, car appartient à un domaine de définition au delà du « langage ».
    (C/C++) = tire ses origines du C, de ses ancêtres que je ne connait pas, et de tous les
    langages qui ont fidèlement suivi sont credo. Donc éviter absolument de l’utiliser pour tous les langages.

    J’ai lu l’article et tous les commentaires. En effet le sujet est délicat; pour vous dire la vérité je viens à l’instant de me frotter à cette problématique.
    La raison pour laquelle certains ne saisissent pas le principe et que d’autres disent la même chose mais sans se comprendre est que personne ne redéfinit clairement les expressions « pointeur », « adresse », « référence », « pile d’exécution », « espace de nom », l’opérateur « . » .
    Des termes qui méritent le détour car malgré l’expérience de plusieurs dans le domaine, il restera toujours des personnes(comme moi surement) pour qui c’est ambiguë. D’après ce que j’ai compris de mes lectures et de mon expérience(hésitez pas à corriger si je dis de la merde). Dans le cadre de l’exécution d’un programme:

    – (a) « Une pile d’exécution » est un espace mémoire créé à chaque fois qu’on entre dans une fonction, et un des sous-ensemble de la pile d’exécution est:

    – (a) « l’espace de noms » qui correspond à des pointeurs et/ou références qui n’existerons que pendant l’exécution de la fonction.

    – (a) « l’adresse » peut désigne grossièrement le numéro de location d’une donnée dans la RAM.

    – (C/C++) « le pointeur » est le nom simplifié que nous donnons à une adresse dans l’espace de nom d’une pile d’exécution.

    – (C/C++) Lorsque dans un programme, on attribue pour la première fois un nom à une adresse, on appelle ce nom le pointeur. Si pour la même adresse, on attribut un autre nom(dans le même programme), ce nom sera « la référence ».

    – (C/C++) Soit « objet » un pointeur, ou une référence. Lorsqu’on fait « objet. », on accède directement(sans copie) au contenu de la case mémoire dont le l’adresse a été simplifié par le nom « objet ».

    En partant de ça, j’ai déduit le comportement de la JVM:

    – Dans une fonction même pile d’appel même espace de nom:

    /*Création d’un objet(ou instance de classe) Toto ayant comme pointeur « toto1 » et stockant « contenu1 » dans la case mémoire liée à toto1.*/
    Toto toto1 = new toto(contenu1);

    /*Allocation d’une nouvelle case mémoire pour un objet de classe Toto ayant comme pointeur « toto2 », et copie du « contenu1 » de toto1 dans toto2.
    Je suppose que c’est l’équivalent du tristement célèbre constructeur par copie du C++. */
    Toto toto2 = toto1;

    –IMPORTANT–> /*changement de la case sur laquelle pointe toto1 uniquement dans la pile d’appelle actuelle(je met en exergue le « actuelle » svp). Ainsi l’ancien « contenu1 » est perdu à jamais dans la pile actuelle(sauf si on l’a sauvé dans une autre case) et attend d’être balayé par le garbage collector.*/
    toto1 = null; //ou
    toto1 = new Toto(contenu2);

    – Soient les fonctions « void fonction1(int in) », « void fonction2(Toto in1):

    Le cas de fonction1(int in) est trivial, car le passage en paramètre d’un type primitif est par valeur. En effet, la pile d’exécution de « fonction1 » possède une adresse nommée en pointeur « in » dont le contenu sera une copie de l’entier passé en paramètre.

    Pour fonction2(Toto in1), Toto étant un objet, le paramètre « in1 » se transformera en « référence »(et oui, ici on peut en le dire je pense) pour l’objet de type Toto passé en paramètre. C’est pourquoi, modifier le contenu de in1, équivaudra à modifier le contenu de cet objet. Mais pourquoi lorsqu’on fait
    in1 = null; cela ne change pas la valeur de l’objet passé en paramètre hors de la fonction? Et là je vous renvoie à –IMPORTANT– qui explique exactement ce qui se passe dans le contexte d’exécution d’une fonction.

    Ainsi, dans l’article, lorqu’on fait copy = null, le pointeur « copy », initialement « référence » de « value »(à l’entrée) de la fonction, devient un pointeur sur la case dont l’adresse est nommée « null ». Cela serait revenu au même si on avait fait copy = new StringBuilder; .

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Nous recrutons

Être un Sapient, c'est faire partie d'un groupe de passionnés ; C'est l'opportunité de travailler et de partager avec des pairs parmi les plus talentueux.