7753 votes

Java est-il "pass-by-reference" ou "pass-by-value" ?

J'ai toujours pensé que Java utilisait passe par référence .

Cependant, j'ai vu un article de blog qui prétend que Java utilise pass-by-value .

Je ne crois pas comprendre la distinction qu'ils font.

Quelle est l'explication ?

4 votes

On dirait plus couramment qu'une variable "passée par référence" peut être mutée. Le terme apparaît dans les manuels parce que les théoriciens du langage avaient besoin d'un moyen de distinguer la façon dont on traite les types de données primitifs (int, bool, byte) des objets complexes et structurés (array, streams, class) -- c'est-à-dire ceux dont l'allocation de mémoire peut être illimitée.

2 votes

Je tiens à souligner que vous ne devez pas y penser dans la plupart des cas. J'ai programmé en Java pendant de nombreuses années jusqu'à ce que j'apprenne le C++. Jusqu'à ce moment-là, je n'avais aucune idée de ce que sont le pass-by-reference et le pass-by-value. La solution intuitive a toujours fonctionné pour moi, c'est pourquoi java est l'un des meilleurs langages pour les débutants. Donc si vous êtes actuellement inquiet, si votre fonction a besoin d'une référence ou d'une valeur, passez-la simplement telle quelle et tout ira bien.

17 votes

Java transmet la référence par valeur.

7062voto

erlando Points 5802

Java est toujours pass-by-value . Malheureusement, lorsque nous traitons des objets, nous traitons en réalité des objets-mains appelés références qui sont également transmises par valeur. Cette terminologie et cette sémantique déroutent facilement de nombreux débutants.

C'est comme ça :

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    Dog oldDog = aDog;

    // we pass the object to foo
    foo(aDog);
    // aDog variable is still pointing to the "Max" dog when foo(...) returns
    aDog.getName().equals("Max"); // true
    aDog.getName().equals("Fifi"); // false
    aDog == oldDog; // true
}

public static void foo(Dog d) {
    d.getName().equals("Max"); // true
    // change d inside of foo() to point to a new Dog instance "Fifi"
    d = new Dog("Fifi");
    d.getName().equals("Fifi"); // true
}

Dans l'exemple ci-dessus aDog.getName() retournera toujours "Max" . La valeur aDog sur main n'est pas modifié dans la fonction foo avec le Dog "Fifi" car la référence de l'objet est passée par valeur. S'il était passé par référence, alors le aDog.getName() en main rendrait "Fifi" après l'appel à foo .

De même :

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    Dog oldDog = aDog;

    foo(aDog);
    // when foo(...) returns, the name of the dog has been changed to "Fifi"
    aDog.getName().equals("Fifi"); // true
    // but it is still the same dog:
    aDog == oldDog; // true
}

public static void foo(Dog d) {
    d.getName().equals("Max"); // true
    // this changes the name of d to be "Fifi"
    d.setName("Fifi");
}

Dans l'exemple ci-dessus, Fifi est le nom du chien après l'appel à foo(aDog) car le nom de l'objet a été défini à l'intérieur de l'élément foo(...) . Toute opération qui foo effectue sur d sont telles que, à toutes fins utiles, elles sont réalisées sur aDog mais c'est no possible de changer la valeur de la variable aDog lui-même.

Pour plus d'informations sur le passage par référence et le passage par valeur, consultez la réponse SO suivante : https://stackoverflow.com/a/430958/6005228 . Ceci explique plus en détail la sémantique et l'histoire des deux et explique également pourquoi Java et de nombreux autres langages modernes semblent faire les deux dans certains cas.

12 votes

Alors, qu'arrive-t-il à "Fifi" dans le premier exemple ? Cesse-t-il d'exister, n'a-t-il jamais été créé, ou existe-t-il dans le tas mais sans variable de référence dans la pile ?

0 votes

Que voulez-vous dire par la dernière phrase but it is not possible to change the value of the variable aDog itself. ?

108 votes

Pour moi, dire que la référence d'un objet est transmise par valeur revient à dire que l'objet est transmis par référence. Je suis un novice de Java, mais je présume que (par opposition) données primitives est une passe par valeur.

3592voto

Scott Stanchfield Points 15863

Je viens de remarquer que vous avez fait référence mon article .

La spécification Java dit que tout en Java est de type pass-by-value. La notion de "pass-by-reference" n'existe pas en Java.

La clé pour comprendre cela est que quelque chose comme

Dog myDog;

est no un chien ; il s'agit en fait d'un pointeur à un chien. L'utilisation du terme "référence" en Java est très trompeuse et est à l'origine de la plupart des confusions ici. Ce qu'ils appellent des "références" agissent/se sentent plus comme ce que nous appelons des "pointeurs" dans la plupart des autres langages.

Ce que cela signifie, c'est que lorsque vous avez

Dog myDog = new Dog("Rover");
foo(myDog);

vous passez essentiellement le adresse de la création Dog à l'objet foo méthode.

(Je dis essentiellement parce que les pointeurs/références Java ne sont pas des adresses directes, mais il est plus facile d'y penser de cette façon).

Supposons que le Dog réside à l'adresse mémoire 42. Cela signifie que nous passons 42 à la méthode.

si la Méthode était définie comme

public void foo(Dog someDog) {
    someDog.setName("Max");     // AAA
    someDog = new Dog("Fifi");  // BBB
    someDog.setName("Rowlf");   // CCC
}

regardons ce qui se passe.

  • le paramètre someDog est fixé à la valeur 42
  • à la ligne "AAA"
    • someDog est suivi jusqu'au Dog vers lequel il pointe (le Dog à l'adresse 42)
    • que Dog (celui de l'adresse 42) est prié de changer son nom en Max
  • à la ligne "BBB
    • une nouvelle Dog est créé. Disons qu'il est à l'adresse 74
    • nous attribuons le paramètre someDog à 74
  • à la ligne "CCC"
    • someDog est suivi jusqu'à la Dog vers lequel il pointe (le Dog à l'adresse 74)
    • que Dog (celui de l'adresse 74) est prié de changer son nom en Rowlf
  • alors, nous retournons

Réfléchissons maintenant à ce qui se passe en dehors de la méthode :

Est-ce que myDog changement ?

Voilà la clé.

En gardant à l'esprit que myDog est un pointeur et non un véritable Dog la réponse est NON. myDog a toujours la valeur 42 ; il pointe toujours vers l'objet original Dog (mais notez qu'à cause de la ligne "AAA", son nom est maintenant "Max" - toujours le même chien ; myDog La valeur de l'entreprise n'a pas changé).

C'est parfaitement valable de suivez une adresse et changer ce qui se trouve à la fin de celle-ci ; cela ne change pas la variable, cependant.

Java fonctionne exactement comme le C. Vous pouvez assigner un pointeur, passer le pointeur à une méthode, suivre le pointeur dans la méthode et modifier les données qui ont été pointées. Toutefois, l'appelant ne verra pas les modifications que vous apportez à l'endroit où pointe ce pointeur. (Dans un langage avec une sémantique pass-by-reference, la fonction méthode peut changer le pointeur et l'appelant verra ce changement).

En C++, Ada, Pascal et d'autres langages qui supportent le pass-by-reference, vous pouvez réellement changer la variable qui a été passée.

Si Java disposait d'une sémantique pass-by-reference, l'option foo que nous avons définie ci-dessus aurait changé l'endroit où myDog pointait lorsqu'il a assigné someDog sur la ligne BBB.

Pensez aux paramètres de référence comme étant des alias pour la variable passée. Lorsque cet alias est attribué, la variable transmise l'est également.

0 votes

Petite question de clarification sur l'exemple ci-dessus, donc lorsque vous créez un nouveau chien à BBB à l'adresse 72, cela implique-t-il qu'au retour le chien créé à 72 et sa valeur sont perdus et reviennent à 42 ?

3 votes

0 votes

@ebresie En grande partie oui (je vais préciser le "en grande partie" dans un instant). Le seul pointeur vers le nouveau chien à 74 (je suppose que vous vouliez dire 74 plutôt que 72) est le paramètre de la fonction foo. Lorsque foo revient, tous ses paramètres sont retirés de la pile, de sorte qu'il ne reste plus rien qui pointe vers 72 et qu'il peut être récupéré par les ordures. Je dis "principalement" car il n'y a pas de "retour en arrière" ; le pointeur myDog dans l'appelant pointait vers 42 depuis le début et n'a jamais changé, quoi qu'il arrive dans la fonction, d'où l'absence de "retour en arrière".

2124voto

Eng.Fouad Points 44085

Java passe toujours les arguments par valeur PAS par référence.


Laissez-moi vous expliquer cela à travers un exemple :

public class Main {

     public static void main(String[] args) {
          Foo f = new Foo("f");
          changeReference(f); // It won't change the reference!
          modifyReference(f); // It will modify the object that the reference variable "f" refers to!
     }

     public static void changeReference(Foo a) {
          Foo b = new Foo("b");
          a = b;
     }

     public static void modifyReference(Foo c) {
          c.setAttribute("c");
     }

}

Je vais vous expliquer cela par étapes :

  1. Déclarer une référence nommée f de type Foo et lui attribuer un nouvel objet de type Foo avec un attribut "f" .

    Foo f = new Foo("f");

    enter image description here

  2. Du côté de la méthode, une référence de type Foo avec un nom a est déclaré et on lui attribue initialement null .

    public static void changeReference(Foo a)

    enter image description here

  3. Lorsque vous appelez la méthode changeReference la référence a se verra attribuer l'objet qui est passé comme argument.

    changeReference(f);

    enter image description here

  4. Déclarer une référence nommée b de type Foo et lui attribuer un nouvel objet de type Foo avec un attribut "b" .

    Foo b = new Foo("b");

    enter image description here

  5. a = b effectue une nouvelle affectation à la référence a , no f de l'objet dont l'attribut est "b" .

    enter image description here

  6. Comme vous le dites modifyReference(Foo c) méthode, une référence c est créé et l'objet avec l'attribut "f" .

    enter image description here

  7. c.setAttribute("c"); changera l'attribut de l'objet qui référence c pointe vers lui, et c'est le même objet que la référence f l'indique.

    enter image description here

J'espère que vous comprenez maintenant comment passer des objets comme arguments en Java :)

74 votes

Java passe toujours les arguments par valeur, mais ce que vous passez par valeur est une référence à un objet, pas une copie de l'objet. Simple, non ?

1 votes

J'espère que le livre d'Herbert Schildt qui m'a appris Java avait enseigné ceci

1 votes

"Object not by Reference", vraiment ?

857voto

SCdF Points 11397

En Java, on passe toujours par valeur, sans exception, jamais .

Comment se fait-il donc que quelqu'un puisse être confus par cela, et croire que Java est une référence passive, ou penser qu'il a un exemple de Java agissant comme une référence passive ? Le point essentiel est que Java jamais fournit un accès direct aux valeurs de les objets eux-mêmes en cualquier circonstances. Le seul accès aux objets se fait par le biais d'un référence à cet objet. Comme les objets Java sont siempre auxquels on accède par le biais d'une référence, plutôt que directement, il est courant de parler de champs et de variables et les arguments de la méthode comme étant objets alors qu'en réalité, ils ne sont que références à des objets . La confusion provient de ce changement de nomenclature (incorrect à proprement parler).

Ainsi, lors de l'appel d'une méthode

  • Pour les arguments primitifs ( int , long etc.), la valeur de passage est la suivante la valeur réelle de la primitive (par exemple, 3).
  • Pour les objets, le passage par la valeur est la valeur de la référence à l'objet .

Donc si vous avez doSomething(foo) y public void doSomething(Foo foo) { .. } les deux Foos ont copié références qui pointent vers les mêmes objets.

Naturellement, passer par valeur une référence à un objet ressemble beaucoup (et est indiscernable en pratique) à passer un objet par référence.

0 votes

La version 2.2 de JVMS est assez claire à ce sujet : Il existe ... deux types de valeurs qui peuvent être stockées dans des variables, transmises en tant qu'arguments, retournées par des méthodes et utilisées : valeurs primitives y valeurs de référence ." Les références d'objets sont des valeurs. Tout est transmis par valeur.

0 votes

L'implication opérationnelle : f(x) (en passant une variable) ne sera jamais assigné à x même. Il n'existe pas d'adresse variable ( alias ) a été adopté. Une décision de conception linguistique solide.

800voto

Gevorg Points 4218

Cela vous donnera un aperçu de la façon dont Java fonctionne réellement, à tel point que lors de votre prochaine discussion sur le passage par référence ou le passage par valeur de Java, vous sourirez simplement :-)

Première étape : effacez de votre esprit le mot qui commence par "p" "_ _ _ _ _ _ _", surtout si vous venez d'autres langages de programmation. Java et 'p' ne peuvent pas être écrits dans le même livre, forum, ou même txt.

Deuxième étape : n'oubliez pas que lorsque vous passez un objet dans une méthode, vous passez la référence de l'objet et non l'objet lui-même.

  • Étudiant : Maître, cela veut-il dire que Java est pass-by-reference ?
  • Maître : Sauterelle, non.

Maintenant, pensez à ce que fait/est la référence/variable d'un objet :

  1. Une variable contient les bits qui indiquent à la JVM comment atteindre l'objet référencé en mémoire (Heap).
  2. Lorsque vous passez des arguments à une méthode vous ne passez PAS la variable de référence, mais une copie des bits dans la variable de référence . Quelque chose comme ça : 3bad086a. 3bad086a représente un moyen d'accéder à l'objet passé.
  3. Donc tu passes juste 3bad086a que c'est la valeur de la référence.
  4. Vous passez la valeur de la référence et non la référence elle-même (et non l'objet).
  5. Cette valeur est en fait COPIEE et donnée à la méthode .

Dans ce qui suit (n'essayez pas de compiler/exécuter ceci...) :

1. Person person;
2. person = new Person("Tom");
3. changeName(person);
4.
5. //I didn't use Person person below as an argument to be nice
6. static void changeName(Person anotherReferenceToTheSamePersonObject) {
7.     anotherReferenceToTheSamePersonObject.setName("Jerry");
8. }

Que se passe-t-il ?

  • La variable personne est créé à la ligne 1 et il est nul au début.
  • Un nouvel objet Personne est créé à la ligne 2, stocké en mémoire, et la variable personne reçoit la référence de l'objet Personne. C'est-à-dire son adresse. Disons 3bad086a.
  • La variable personne contenant l'adresse de l'objet est passée à la fonction dans la ligne #3.
  • Dans la ligne 4, vous pouvez écouter le son du silence.
  • Vérifiez le commentaire de la ligne 5
  • Une méthode variable locale - autreReferenceToTheSamePersonObject - est créé et ensuite vient la magie de la ligne 6 :
    • La variable/référence personne est copié bit par bit et transmis à autreReferenceToTheSamePersonObject à l'intérieur de la fonction.
    • Aucune nouvelle instance de Personne n'est créée.
    • Les deux " personne " et " autreReferenceToTheSamePersonObject " ont la même valeur de 3bad086a.
    • Ne l'essayez pas, mais person==une autre référence au même objet-personne serait vrai.
    • Les deux variables ont des COPIES IDENTIQUES de la référence et elles font toutes deux référence au même objet personne, le MÊME objet sur le tas et NON UNE COPIE.

Une image vaut mille mots :

Pass by Value

Notez que les flèches anotherReferenceToTheSamePersonObject sont dirigées vers l'objet et non vers la variable person !

Si tu n'as pas compris, fais-moi confiance et rappelle-toi que c'est mieux de dire que Java est une méthode de transmission par valeur . Bien, passer par une valeur de référence . Oh bien, encore mieux c'est passe-par-copie-de-la-variable-valeur ! ;)

Maintenant, sentez-vous libre de me haïr, mais notez que compte tenu de cette il n'y a pas de différence entre le passage de types de données primitifs et d'Objets quand on parle d'arguments de méthode.

Vous passez toujours une copie des bits de la valeur de la référence !

  • S'il s'agit d'un type de données primitif, ces bits contiendront la valeur du type de données primitif lui-même.
  • S'il s'agit d'un objet, les bits contiennent la valeur de l'adresse qui indique à la JVM comment accéder à l'objet.

Java est de type pass-by-value parce qu'à l'intérieur d'une méthode, vous pouvez modifier l'objet référencé autant que vous le voulez, mais peu importe vos efforts, vous ne pourrez jamais modifier la variable passée qui continuera à référencer (pas p _ _ _ _ _ _) le même objet quoi qu'il arrive !


La fonction changeName ci-dessus ne sera jamais en mesure de modifier le contenu réel (les valeurs binaires) de la référence passée. En d'autres termes, changeName ne peut pas faire en sorte qu'une personne fasse référence à un autre objet.


Bien sûr, vous pouvez faire court et juste dire que Java est de type pass-by-value !

0 votes

I tried this: <br />Fichier file = nouveau Fichier("C:/") ; changeFile(file) ; System.out.println(file.getAbsolutePath()) ; } public static void changeFile(File f) { f = nouveau Fichier("D:/") ; }`

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X