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 ?
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 ?
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.
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 ?
Que voulez-vous dire par la dernière phrase but it is not possible to change the value of the variable aDog itself.
?
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.
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.
someDog
est fixé à la valeur 42someDog
est suivi jusqu'au Dog
vers lequel il pointe (le Dog
à l'adresse 42)Dog
(celui de l'adresse 42) est prié de changer son nom en MaxDog
est créé. Disons qu'il est à l'adresse 74someDog
à 74Dog
vers lequel il pointe (le Dog
à l'adresse 74)Dog
(celui de l'adresse 74) est prié de changer son nom en RowlfRé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.
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 ?
@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".
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 :
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");
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)
Lorsque vous appelez la méthode changeReference
la référence a
se verra attribuer l'objet qui est passé comme argument.
changeReference(f);
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");
a = b
effectue une nouvelle affectation à la référence a
, no f
de l'objet dont l'attribut est "b"
.
Comme vous le dites modifyReference(Foo c)
méthode, une référence c
est créé et l'objet avec l'attribut "f"
.
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.
J'espère que vous comprenez maintenant comment passer des objets comme arguments en Java :)
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
int
, long
etc.), la valeur de passage est la suivante la valeur réelle de la primitive (par exemple, 3).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.
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.
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.
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.
Maintenant, pensez à ce que fait/est la référence/variable d'un objet :
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 ?
Une image vaut mille mots :
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 !
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 !
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.
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.
5 votes
En termes très concis, cette confusion est due au fait qu'en Java, tous les types de données non primitifs sont gérés/accessibles par les éléments suivants références . Cependant, le passage est toujours une valeur. Ainsi, pour tous les types non primitifs, la référence est transmise par sa valeur. Tous les types primitifs sont également passés par leur valeur.
0 votes
Pour ceux qui viennent de
C++
et commencent à être confus quand ils touchentJava
, ceci pourrait aider comme raccourci .0 votes
J'ai trouvé cela très utile : baeldung.com/java-pass-by-value-or-pass-by-reference
0 votes
C'est la différence entre foo.bar(qqch) et foo = qqch. Dans la première, l'objet est modifié en utilisant la variable qui pointe vers lui, et la variable elle-même qui pointe vers l'objet n'a pas été modifiée. Dans la seconde, en revanche, la variable elle-même qui pointait vers l'objet a changé et pointe maintenant vers un autre objet. Si vous avez des connaissances en C++ : Un pointeur est une variable qui détient une adresse mémoire, tandis qu'une référence a la même adresse mémoire que l'élément qu'elle référence. En effet, en Java, un pointeur est transmis par valeur, mais les javanais l'appellent un référence !
0 votes
Qu'est-ce que cela signifierait alors pour un langage de passer une référence non par valeur ? Passer des pointeurs à des pointeurs ? Existe-t-il des langages qui font une telle chose ? Des langages comme C/C++ ne passent-ils pas aussi les références et les pointeurs par valeur ?