J'ai toujours pensé que Java était passé par référence; mais j'ai vu quelques posts (par exemple, ce blog) qui affirment qu'elle ne l'est pas. Je ne pense pas que je comprends la distinction qu'ils font.
Quelle est l'explication?
Réponses
Trop de publicités?Java est toujours passé par valeur. La difficulté peut être de comprendre que Java passe les objets comme des références et ces références sont passés par valeur.
Il va comme ceci:
Dog aDog = new Dog("Max");
foo(aDog);
aDog.getName().equals("Max"); // true
public void foo(Dog d) {
d.getName().equals("Max"); // true
d = new Dog("Fifi");
d.getName().equals("Fifi"); // true
}
Dans cet exemple, aDog.getName()
retournera toujours "Max"
. d
n'est pas remplacé dans la fonction de l'objet de référence est passé par valeur.
De la même manière:
Dog aDog = new Dog("Max");
foo(aDog);
aDog.getName().equals("Fifi"); // true
public void foo(Dog d) {
d.getName().equals("Max"); // true
d.setName("Fifi");
}
Je viens de remarquer que vous l'avez mentionné dans mon article ;)
La Java Spec dit que tout dans Java est passé par valeur. Il n'y a pas une telle chose comme "par référence" en Java.
La clé pour comprendre c'est que quelque chose comme
Dog myDog;
est pas un Chien; c'est en fait un pointeur à un Chien.
Ce que cela signifie, c'est quand vous avez
Dog myDog = new Dog("Rover");
foo(myDog);
vous êtes essentiellement en passant l' adresse de l'créée Dog
objet à l' foo
méthode.
(Je dis essentiellement parce que Java pointeurs ne sont pas les adresses directes, mais il est plus facile de penser de cette façon)
Supposons que l' Dog
objet se trouve à l'adresse mémoire 42. Cela signifie que nous passons 42 de la méthode.
si la Méthode ont été définis 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
la valeur 42 - à la ligne "AAA"
someDog
est suivie à l'Dog
il pointe vers (Dog
objet à l'adresse 42)- qu'
Dog
(l'un à l'adresse 42) est demandé de changer son nom de Max
- à la ligne "BBB"
- un nouveau
Dog
est créé. Disons qu'il est à l'adresse 74 - nous affecter le paramètre
someDog
à 74
- un nouveau
- à la ligne "CCC"
- someDog est suivie à l'
Dog
il pointe vers (Dog
objet à l'adresse 74) - qu'
Dog
(l'un à l'adresse 74) est demandé de changer son nom pour Rowlf
- someDog est suivie à l'
- ensuite, nous reviendrons
Maintenant, nous allons réfléchir sur ce qui se passe en dehors de la méthode:
Avez - myDog
changement?
Il y a la clé.
En gardant à l'esprit qu' myDog
est un pointeur, et pas un Dog
, la réponse est NON. myDog
a encore de la valeur 42; il est toujours en pointant à la version originale Dog
.
Il est parfaitement valide pour suivre une adresse et de changer ce qui est à la fin de celui-ci; cela ne change pas la variable, cependant.
Java fonctionne exactement comme le C. Vous pouvez affecter un pointeur, passer le pointeur à une méthode, suivez le pointeur de la méthode et de modifier les données qui a été souligné. Cependant, vous ne pouvez pas modifier l'endroit où le pointeur de points.
En C++, Ada, Pascal et d'autres langues que le soutien par référence, vous pouvez réellement changer la variable qui a été adoptée.
Si Java avait passé par référence sémantique, l' foo
méthode que nous avons définie ci-dessus aurait changé où myDog
pointait quand il a assigné someDog
sur la ligne de BBB.
Pensez à des paramètres de référence comme alias de la variable passée dans. Lorsque cet alias est attribué, est donc la variable qui a été passé.
Est-ce que c'est? (Je vais ajouter ce que un addendum à mon article...)
-- Scott
Java passe toujours des arguments par valeur et NON par référence.
Laissez-moi vous expliquer cela par 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 en plusieurs étapes:
La déclaration d'une référence nommée
f
de typeFoo
et l'attribuer à un nouvel objet de typeFoo
avec un attribut"f"
.Foo f = new Foo("f");
À partir de la méthode de côté, une référence de type
Foo
avec un noma
est déclaré et il est d'abord affecté à l'null
.public static void changeReference(Foo a)
Comme vous l'appelez la méthode
changeReference
, la référencea
seront affectés à l'objet qui est passé comme un argument.changeReference(f);
La déclaration d'une référence nommée
b
de typeFoo
et l'attribuer à un nouvel objet de typeFoo
avec un attribut"b"
.Foo b = new Foo("b");
a = b
re-affectation de la référencea
PASf
pour l'objet dont son attribut est -"b"
.Comme vous l'appelez
modifyReference(Foo c)
méthode, une référencec
est créée et affectée à l'objet avec l'attribut"f"
.c.setAttribute("c");
va changer l'attribut de l'objet de référencec
de points à elle, et c'est même l'objet de référencef
des points.
J'espère que vous comprenez maintenant comment le passage d'objets comme arguments fonctionne en Java :)
Cela vous donnera un aperçu de la façon dont Java fonctionne vraiment, au point que, lors de votre prochaine discussion à propos de Java, le passage par référence ou passage par valeur, vous aurez juste sourire :-)
L'étape une, merci de l'effacer de votre esprit un mot qui commence par "p' "_ _ _ _ _ _ _", surtout si vous venez d'autres langages de programmation. Java et 'p' ne peut pas être écrit dans le même livre, forum, ou même txt.
Étape deux souvenez-vous que lorsque vous transmettez un Objet dans une méthode que vous êtes en passant la référence de l'Objet et non l'Objet lui-même.
- Étudiant: Maître, est-ce à dire que Java est passé par référence?
- Maître: Sauterelle, Pas.
Maintenant, pensez à ce qu'est une référence de l'Objet/variable ne/est:
- Une variable contient les bits qui indiquent la JVM comment arriver à l'Objet référencé dans la mémoire (Heap).
- Lors du passage d'arguments à une méthode vous n'ÊTES PAS en passant la variable de référence, mais une copie des bits de la variable de référence. Quelque chose comme ceci: 3bad086a. 3bad086a représente un moyen d'obtenir de l'objet passé.
- Si vous êtes juste de passage 3bad086a que c'est la valeur de la référence.
- Vous êtes de passage de la valeur de la référence, et non la référence elle-même (et non pas de l'objet).
- Cette valeur est COPIÉE et compte tenu de la méthode.
Dans ce qui suit (merci de ne pas essayer de compiler/exécuter cette...):
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. }
Ce qui se passe?
- La variable personne est créée dans la ligne #1 et c'est nul au début.
- Un nouvel Objet est créé dans la ligne #2, stockées dans la mémoire, et la variable d'une personne est donné la référence de l'objet Personne. C'est, à son adresse. Disons 3bad086a.
- La variable personne tenant l'adresse de l'Objet est passé à la fonction en ligne #3.
- Dans la ligne #4 vous pouvez écouter le son du silence
- Vérifiez le commentaire sur la ligne n ° 5
- Une méthode de variable locale -anotherReferenceToTheSamePersonObject- est créé, puis vient la magie en ligne n ° 6:
- La variable de référence/ personne est copié bit-par-bit et passé à anotherReferenceToTheSamePersonObject l'intérieur de la fonction.
- Aucun nouveau cas de la Personne sont créés.
- Les deux "personne" et "anotherReferenceToTheSamePersonObject" contenir la même valeur de 3bad086a.
- N'essayez pas de cela, mais personne==anotherReferenceToTheSamePersonObject serait vrai.
- Les deux variables ont des COPIES IDENTIQUES de la référence et tous deux se réfèrent à la même Personne, l'Objet, le MÊME Objet sur le Tas et NON UNE COPIE.
Une image vaut mille mots:
Notez que le anotherReferenceToTheSamePersonObject flèches est dirigé vers l'Objet et non pas vers la variable personne!
Si vous ne l'avez pas alors fais-moi confiance et n'oubliez pas que c'est mieux de dire que Java est le passage par valeur. Eh bien, passe par la valeur de référence. Eh bien, même mieux, c'est de passer par-copie-de-la-variable-valeur! ;)
Maintenant n'hésitez pas à me détester, mais note que, compte tenu de cela il n'y a pas de différence entre la réussite des types de données primitifs et des Objets lorsque l'on parle des arguments de méthode.
Vous passez toujours une copie de bits de la valeur de la référence!
- Si c'est un type de données primitif de ces bits contenant la valeur du type de données primitif lui-même.
- Si c'est un Objet les bits contenant la valeur de l'adresse, qui raconte la JVM comment faire pour obtenir l'Objet.
Java est passé par valeur, car à l'intérieur d'une méthode, vous pouvez modifier l'Objet référencé autant que vous voulez mais pas n'importe comment dur vous essayez, vous ne serez jamais en mesure de modifier le passé de la variable qui va garder le référencement (pas de p _ _ _ _ _ _ _) le même Objet, n'importe quoi!
Le changeName fonction ci-dessus ne seront jamais en mesure de modifier le contenu réel (les valeurs des bits) de la référence. En d'autres mots changeName ne peut pas faire de Personne à personne de référence à un autre Objet.
Bien sûr, vous pouvez couper court et juste dire que Java est passé par valeur!
Java est toujours le passage par valeur, sans exception, jamais.
Alors, comment est-ce que quelqu'un peut être à tout confus par cela, et de croire que Java est passer par référence, ou pensent qu'ils ont un exemple de Java agissant en tant que passage par référence? Le point clé est que Java ne jamais offre un accès direct aux valeurs des objets eux-mêmes, dans toutes les circonstances. Le seul accès aux objets est par le biais d'une référence à cet objet. Parce que les objets Java sont toujours accessibles via une référence, plutôt que directement, il est courant de parler de champs et variables et arguments de méthode comme étant des objets, lorsqu'manière affectée ils ne sont que des références à des objets. La confusion vient de ce (strictement parlant, incorrect) changement de nomenclature.
Ainsi, lors de l'appel d'une méthode
- Pour les arguments primitifs (
int
,long
, etc.), le passage par valeur est la valeur réelle de la primitive (par exemple, 3). - Pour les objets, le passage par valeur est la valeur de la référence à l'objet.
Donc, si vous avez doSomething(foo)
et public void doSomething(Foo foo) { .. }
les deux Foos avez copié les références qui pointent vers le même objet.
Naturellement, en passant par la valeur d'une référence à un objet ressemble beaucoup (impossible de distinguer dans la pratique) de passage d'un objet par référence.