variable - this en java




Java est-il «passe-à-référence» ou «passe-à-valeur»? (20)

J'ai toujours pensé que Java était passe-à-référence .

Cependant, j'ai vu quelques articles de blog (par exemple, ce blog ) affirmer que ce n'est pas le cas.

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

Quelle est l'explication?


Une référence est toujours une valeur lorsqu'elle est représentée, quelle que soit la langue utilisée.

Pour obtenir une vue en dehors de la boîte, examinons Assembly ou une gestion de mémoire de bas niveau. Au niveau de la CPU, une référence à n'importe quoi devient immédiatement une valeur si elle est écrite dans la mémoire ou dans l'un des registres de la CPU. (C’est pourquoi le pointeur est une bonne définition. C’est une valeur qui a un but en même temps).

Les données en mémoire ont un emplacement et à cet emplacement une valeur (octet, mot, peu importe). Dans Assembly, nous avons une solution pratique pour attribuer un nom à un certain emplacement (variable), mais lors de la compilation du code, l'assembleur remplace simplement Nom par l'emplacement désigné, tout comme votre navigateur remplace les noms de domaine par des adresses IP.

Au fond, il est techniquement impossible de faire référence à quoi que ce soit dans n'importe quelle langue sans la représenter (quand cela devient immédiatement une valeur).

Disons que nous avons une variable Foo, son emplacement est à l'octet 47e dans la mémoire et sa valeur est 5. Nous avons une autre variable Ref2Foo qui est à l' octet 223e en mémoire, et sa valeur sera 47. Ce Ref2Foo pourrait être une variable technique , pas explicitement créé par le programme. Si vous ne regardez que 5 et 47 sans autre information, vous ne verrez que deux valeurs . Si vous les utilisez comme références, 5vous devez voyager:

(Name)[Location] -> [Value at the Location]
---------------------
(Ref2Foo)[223]  -> 47
(Foo)[47]       -> 5

Voici comment fonctionnent les tables de saut.

Si nous voulons appeler une méthode / fonction / procédure ayant la valeur de Foo, il existe plusieurs façons de transmettre la variable à la méthode, en fonction du langage et de ses différents modes d'appel de méthode:

  1. 5 est copié dans l’un des registres de la CPU (c’est-à-dire EAX).
  2. 5 obtient PUSHd à la pile.
  3. 47 est copié dans l'un des registres de la CPU
  4. 47 PUSH à la pile.
  5. 223 est copié dans l’un des registres de la CPU.
  6. 223 obtient PUSHd à la pile.

Dans tous les cas, au-dessus d'une valeur - une copie d'une valeur existante - a été créée, il appartient maintenant à la méthode de réception de la gérer. Lorsque vous écrivez "Foo" dans la méthode, celle-ci est lue à partir de EAX, ou automatiquement déréférencée ou dupliquée, le processus dépend de la façon dont la langue fonctionne et / ou du type de dictée de Foo. Ceci est caché du développeur jusqu'à ce qu'il contourne le processus de déréférencement. Ainsi, une référence est une valeur lorsqu'elle est représentée, car une référence est une valeur qui doit être traitée (au niveau de la langue).

Maintenant nous avons passé Foo à la méthode:

  • dans les cas 1. et 2. si vous modifiez Foo ( Foo = 9), cela n'affecte que la portée locale car vous avez une copie de la valeur. De l'intérieur de la méthode, nous ne pouvons même pas déterminer où se trouvait le Foo original en mémoire.
  • dans les cas 3. et 4. si vous utilisez des constructions de langage par défaut et changez Foo ( Foo = 11), cela pourrait changer Foo globalement (dépend du langage, c'est-à-dire Java ou le procedure findMin(x, y, z: integer; var m de Pascal : integer);). Toutefois, si le langage vous permet de contourner le processus de déréférence, vous pouvez changer 47, dites à 49. À ce stade, Foo semble avoir été modifié si vous le lisez, car vous avez changé le pointeur local . Et si vous deviez modifier ce Foo à l'intérieur de la méthode ( Foo = 12), vous fuberez probablement l'exécution du programme (alias. Segfault) car vous écrirez dans une mémoire différente de celle attendue, vous pouvez même modifier une zone destinée à contenir des exécutables. le programme et l’écriture modifieront le code courant (Foo n’est plus maintenant 47). MAIS la valeur de Foo47n'a pas changé globalement, seulement celui à l'intérieur de la méthode, car 47était également une copie de la méthode.
  • dans les cas 5. et 6. si vous modifiez 223dans la méthode, cela crée le même chaos que dans 3. ou 4. (un pointeur, pointant sur une valeur désormais incorrecte, qui est à nouveau utilisé comme pointeur), mais il s'agit toujours d'un paramètre local. problème, car 223 a été copié . Cependant, si vous êtes capable de déréférencer Ref2Foo(c'est-à-dire 223), d'atteindre et de modifier la valeur pointée 47, par exemple, 49cela affectera Foo globalement , car dans ce cas, les méthodes ont une copie de 223mais le référencé 47n'existe qu'une seule fois, à 49conduire chaque Ref2Foodouble-déréférencement à une mauvaise valeur.

Si vous sélectionnez des détails insignifiants, même les langues qui passent par référence transmettent des valeurs aux fonctions, mais ces fonctions savent qu'elles doivent l'utiliser à des fins de déréférencement. Cette valeur de référence de référence est simplement masquée pour le programmeur car elle est pratiquement inutile et la terminologie n’est que transmission par référence .

Une valeur de passe par valeur stricte est également inutile, cela signifierait qu'un tableau de 100 Mo devrait être copié chaque fois que nous appelons une méthode avec le tableau en argument, par conséquent, Java ne peut pas être strictement passé par valeur. Chaque langue passera une référence à cet énorme tableau (en tant que valeur) et utilisera le mécanisme de copie sur écriture si ce tableau peut être modifié localement dans la méthode ou autorise la méthode (comme le fait Java) à modifier le tableau globalement (de la vue de l'appelant) et quelques langues permettent de modifier la valeur de la référence elle-même.

En bref, et dans la terminologie propre à Java, Java est une valeur donnéevaleur peut être: une valeur réelle ou une valeur représentant une référence .


Java est toujours transmis par valeur . Malheureusement, ils ont décidé d'appeler l'emplacement d'un objet une "référence". Lorsque nous transmettons la valeur d'un objet, nous lui transmettons la référence . C'est déroutant pour les débutants.

Ça va comme ça:

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    // 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 
}

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 dans main n'est pas modifiée 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 aDog.getName() dans main retournerait "Fifi" après l'appel à foo .

Également:

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    foo(aDog);
    // when foo(...) returns, the name of the dog has been changed to "Fifi"
    aDog.getName().equals("Fifi"); // 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 était défini à l'intérieur de foo(...) . Toutes les opérations que foo effectue sur d sont telles que, à toutes fins pratiques, elles sont effectuées sur aDog lui-même (sauf lorsque d est modifié pour indiquer une instance de Dog différente, telle que d = new Dog("Boxer") ).


Java transmet les références par valeur.

Donc, vous ne pouvez pas changer la référence qui est transmise.


Java transmet toujours les arguments par valeur, NON par référence.

Laissez-moi vous expliquer ceci à travers un example :

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 expliquer cela par étapes:

  1. Déclarez une référence nommée f de type Foo et affectez-la à un nouvel objet de type Foo avec un attribut "f" .

    Foo f = new Foo("f");
    

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

    public static void changeReference(Foo a)
    

  3. Lorsque vous appelez la méthode changeReference , la référence a sera affectée à l'objet qui est transmis en tant qu'argument.

    changeReference(f);
    

  4. Déclarez une référence nommée b de type Foo et affectez-la à un nouvel objet de type Foo avec un attribut "b" .

    Foo b = new Foo("b");
    

  5. a = b réaffecte la référence a NOT f à l'objet dont l'attribut est "b" .

  6. Lorsque vous appelez la modifyReference(Foo c) , une référence c est créée et attribuée à l'objet avec l'attribut "f" .

  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 référence f pointe vers lui.

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


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

La spécification Java indique que tout en Java est transmis valeur par valeur. "Pass-by-reference" n'existe pas en Java.

La clé pour comprendre cela est que quelque chose comme

Dog myDog;

n'est pas un chien; c'est en fait un pointeur sur un chien.

Qu'est-ce que cela signifie, c'est quand vous avez

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

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

(Je dis essentiellement parce que les pointeurs Java ne sont pas des adresses directes, mais il est plus facile de les penser ainsi)

Supposons que l'objet Dog réside à l'adresse de 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 a la valeur 42
  • à la ligne "AAA"
    • someDog est suivi jusqu'au Dog someDog (l'objet Dog à l'adresse 42)
    • ce Dog (celui à l'adresse 42) est invité à changer son nom en Max
  • à la ligne "BBB"
    • un nouveau Dog est créé. Disons qu'il est à l'adresse 74
    • on assigne le paramètre someDog à 74
  • à la ligne "CCC"
    • someDog est suivi jusqu'au Dog indiqué (l'objet Dog à l'adresse 74)
    • ce Dog (celui à l'adresse 74) est invité à changer son nom en Rowlf
  • alors, nous revenons

Maintenant réfléchissons à ce qui se passe en dehors de la méthode:

myDog t- myDog changé?

Il y a la clé.

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

Il est parfaitement valable de suivre une adresse et de changer ce qui se trouve à la fin. cela ne change pas la variable, cependant.

Java fonctionne exactement comme le point C. Vous pouvez affecter un pointeur, le transmettre à une méthode, suivre le pointeur de la méthode et modifier les données pointées. Cependant, vous ne pouvez pas modifier le pointeur.

En C ++, Ada, Pascal et d'autres langages prenant en charge la référence par référence, vous pouvez réellement modifier la variable transmise.

Si Java avait une sémantique de référence par référence, la méthode foo nous avons définie ci-dessus aurait changé à l'endroit myDog lorsqu'il avait assigné someDog à la ligne BBB.

Considérez les paramètres de référence comme des alias pour la variable transmise. Lorsque cet alias est attribué, il en va de même pour la variable transmise.


Juste pour montrer le contraste, comparez les extraits de code C++ et Java :

En C ++: Remarque: Code incorrect - Fuites de mémoire! Mais cela démontre le point.

void cppMethod(int val, int &ref, Dog obj, Dog &objRef, Dog *objPtr, Dog *&objPtrRef)
{
    val = 7; // Modifies the copy
    ref = 7; // Modifies the original variable
    obj.SetName("obj"); // Modifies the copy of Dog passed
    objRef.SetName("objRef"); // Modifies the original Dog passed
    objPtr->SetName("objPtr"); // Modifies the original Dog pointed to 
                               // by the copy of the pointer passed.
    objPtr = new Dog("newObjPtr");  // Modifies the copy of the pointer, 
                                   // leaving the original object alone.
    objPtrRef->SetName("objRefPtr"); // Modifies the original Dog pointed to 
                                    // by the original pointer passed. 
    objPtrRef = new Dog("newObjPtrRef"); // Modifies the original pointer passed
}

int main()
{
    int a = 0;
    int b = 0;
    Dog d0 = Dog("d0");
    Dog d1 = Dog("d1");
    Dog *d2 = new Dog("d2");
    Dog *d3 = new Dog("d3");
    cppMethod(a, b, d0, d1, d2, d3);
    // a is still set to 0
    // b is now set to 7
    // d0 still have name "d0"
    // d1 now has name "objRef"
    // d2 now has name "objPtr"
    // d3 now has name "newObjPtrRef"
}

En Java,

public static void javaMethod(int val, Dog objPtr)
{
   val = 7; // Modifies the copy
   objPtr.SetName("objPtr") // Modifies the original Dog pointed to 
                            // by the copy of the pointer passed.
   objPtr = new Dog("newObjPtr");  // Modifies the copy of the pointer, 
                                  // leaving the original object alone.
}

public static void main()
{
    int a = 0;
    Dog d0 = new Dog("d0");
    javaMethod(a, d0);
    // a is still set to 0
    // d0 now has name "objPtr"
}

Java ne dispose que des deux types de transmission: par valeur pour les types intégrés et par valeur du pointeur pour les types d'objet.


En Java, seules les références sont passées et sont passées par valeur:

Les arguments Java sont tous passés par valeur (la référence est copiée lorsqu'elle est utilisée par la méthode):

Dans le cas des types primitifs, le comportement Java est simple: la valeur est copiée dans une autre instance du type primitif.

Dans le cas des objets, il en va de même: les variables d'objet sont des pointeurs (compartiments) contenant uniquement l' adresse de l' objet créée à l'aide du mot clé "new" et sont copiées comme des types primitifs.

Le comportement peut sembler différent des types primitifs: Parce que la variable d'objet copiée contient la même adresse (vers le même objet), le contenu / les membres de l'objet peuvent toujours être modifiés dans une méthode et un accès ultérieur à l'extérieur, donnant l'illusion que l'objet (contenant) lui-même a été passé par référence.

"String" Les objets semblent constituer un contre-exemple parfait à la légende urbaine qui dit que "les objets sont passés par référence":

En effet, dans une méthode, vous ne pourrez jamais mettre à jour la valeur d'une chaîne passée en argument:

Un objet String contient les caractères d'un tableau déclaré final qui ne peut pas être modifié. Seule l'adresse de l'objet peut être remplacée par une autre à l'aide de "nouveau". L'utilisation de "new" pour mettre à jour la variable ne permet pas d'accéder à Object de l'extérieur, car la variable a été initialement transmise par valeur et copiée.


Java est toujours passé par valeur, pas par référence

Tout d’abord, nous devons comprendre ce que sont le passage par valeur et le passage par référence.

Pass par valeur signifie que vous faites une copie en mémoire de la valeur du paramètre actuel qui a été transmise. Il s'agit d'une copie du contenu du paramètre actuel .

Pass par référence (également appelé adresse de passage par) signifie qu'une copie de l'adresse du paramètre réel est stockée .

Parfois, Java peut donner l’illusion de passer par référence. Voyons comment cela fonctionne en utilisant l'exemple ci-dessous:

public class PassByValue {
    public static void main(String[] args) {
        Test t = new Test();
        t.name = "initialvalue";
        new PassByValue().changeValue(t);
        System.out.println(t.name);
    }

    public void changeValue(Test f) {
        f.name = "changevalue";
    }
}

class Test {
    String name;
}

Le résultat de ce programme est:

changevalue

Comprenons pas à pas:

Test t = new Test();

Comme nous le savons tous, il va créer un objet dans le tas et renvoyer la valeur de référence à t. Par exemple, supposons que la valeur de t soit 0x100234(nous ne connaissons pas la valeur interne réelle de la machine virtuelle Java, ceci est juste un exemple).

new PassByValue().changeValue(t);

En passant la référence t à la fonction, il ne passera pas directement la valeur de référence réelle de l'objet test, mais créera une copie de t, puis la transmettra à la fonction. Comme il passe par valeur , il transmet une copie de la variable plutôt que la référence réelle de celle-ci. Puisque nous avons dit que la valeur de t était 0x100234, t et f auront la même valeur et par conséquent, ils pointeront vers le même objet.

Si vous changez quelque chose dans la fonction en utilisant la référence f, cela modifiera le contenu existant de l'objet. C'est pourquoi nous avons obtenu le résultat changevaluequi est mis à jour dans la fonction.

Pour mieux comprendre ceci, considérons l'exemple suivant:

public class PassByValue {
    public static void main(String[] args) {
        Test t = new Test();
        t.name = "initialvalue";
        new PassByValue().changeRefence(t);
        System.out.println(t.name);
    }

    public void changeRefence(Test f) {
        f = null;
    }
}

class Test {
    String name;
}

Est-ce que ce lancer un NullPointerException? Non, car il ne fait que transmettre une copie de la référence. Dans le cas du passage par référence, il aurait pu lancer un NullPointerException, comme on le voit ci-dessous:

Espérons que cela aidera.


Java est un appel par valeur.

Comment ça marche

  • Vous transmettez 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 contiennent la valeur du type de données primitif lui-même. C'est pourquoi, si nous modifions la valeur de l'en-tête dans la méthode, elle ne reflète pas les modifications apportées à l'extérieur.

  • S'il s'agit d'un type de données d'objet tel que Foo foo = new Foo (), dans ce cas, la copie de l'adresse de l'objet passe comme un raccourci de fichier, supposons que nous ayons un fichier texte abc.txt sur C: \ desktop et supposons que nous faisons un raccourci de le même fichier et le mettre à l'intérieur de C: \ desktop \ abc-raccourci, donc lorsque vous accédez au fichier à partir de C: \ desktop \ abc.txt et écrivez '' , fermez le fichier et ouvrez de nouveau le fichier à partir du raccourci. write "est la plus grande communauté en ligne pour les programmeurs à apprendre", puis le changement total de fichier sera " est la plus grande communauté en ligne pour les programmeurs à apprendre"ce qui signifie que peu importe d'où vous ouvrez le fichier, chaque fois que nous accédons au même fichier, nous pouvons supposer ici que Foo est un fichier et supposons que foo est stocké à 123hd7h (adresse d'origine telle que C: \ desktop \ abc.txt ) address et 234jdid (adresse copiée telle que C: \ desktop \ abc-shortcut qui contient en fait l'adresse d'origine du fichier à l'intérieur). Alors, pour une meilleure compréhension, créez un fichier de raccourci et ressentez ...


Java transmet les références aux objets par valeur.


Non, ce n'est pas passé par référence.

Java est transmis valeur par valeur conformément à la spécification du langage Java:

Lorsque la méthode ou le constructeur est appelé (§15.12), les valeurs des expressions d'argument réel initialisent les variables de paramètre nouvellement créées , chacune du type déclaré, avant l'exécution du corps de la méthode ou du constructeur. L'identifiant qui apparaît dans DeclaratorId peut être utilisé comme un nom simple dans le corps de la méthode ou du constructeur pour faire référence au paramètre formel .


Quelques corrections à quelques articles.

C ne prend PAS en charge le passage par référence. C'est TOUJOURS passer par valeur. C ++ supporte passe par référence, mais n'est pas la valeur par défaut et est assez dangereux.

Peu importe la valeur en Java: primitive ou adresse (approximativement) d'objet, elle est TOUJOURS passée par valeur.

Si un objet Java "se comporte" comme s'il était passé par référence, il s'agit d'une propriété de mutabilité qui n'a absolument rien à voir avec les mécanismes de passage.

Je ne sais pas pourquoi cela est si déroutant, peut-être parce que tant de "programmeurs" Java ne sont pas formellement formés et ne comprennent donc pas ce qui se passe réellement en mémoire?


Comme beaucoup de gens l'ont mentionné auparavant, Java est toujours transmis par valeur

Voici un autre exemple qui vous aidera à comprendre la différence ( exemple d'échange classique ):

public class Test {
  public static void main(String[] args) {
    Integer a = new Integer(2);
    Integer b = new Integer(3);
    System.out.println("Before: a = " + a + ", b = " + b);
    swap(a,b);
    System.out.println("After: a = " + a + ", b = " + b);
  }

  public static swap(Integer iA, Integer iB) {
    Integer tmp = iA;
    iA = iB;
    iB = tmp;
  }
}

Impressions:

Avant: a = 2, b = 3
Après: a = 2, b = 3

Cela est dû au fait que iA et iB sont de nouvelles variables de référence locales ayant la même valeur que les références transmises (elles désignent a et b respectivement). Donc, essayer de changer les références de iA ou iB ne changera que dans la portée locale et non en dehors de cette méthode.


En Java tout est référencé, donc quand vous avez quelque chose comme: Point pnt1 = new Point(0,0);Java fait comme suit:

  1. Crée un nouvel objet Point
  2. Crée une nouvelle référence de point et initialise cette référence pour pointer (faire référence à) sur un objet Point créé précédemment.
  3. À partir de là, à travers la vie d'un objet Point, vous pourrez accéder à cet objet via une référence pnt1. On peut donc dire qu'en Java, vous manipulez un objet à travers sa référence.

Java ne transmet pas les arguments de méthode par référence; il les passe en valeur. Je vais utiliser l'exemple de ce site :

public static void tricky(Point arg1, Point arg2) {
  arg1.x = 100;
  arg1.y = 100;
  Point temp = arg1;
  arg1 = arg2;
  arg2 = temp;
}
public static void main(String [] args) {
  Point pnt1 = new Point(0,0);
  Point pnt2 = new Point(0,0);
  System.out.println("X1: " + pnt1.x + " Y1: " +pnt1.y); 
  System.out.println("X2: " + pnt2.x + " Y2: " +pnt2.y);
  System.out.println(" ");
  tricky(pnt1,pnt2);
  System.out.println("X1: " + pnt1.x + " Y1:" + pnt1.y); 
  System.out.println("X2: " + pnt2.x + " Y2: " +pnt2.y);  
}

Flux du programme:

Point pnt1 = new Point(0,0);
Point pnt2 = new Point(0,0);

Création de deux objets Point différents avec deux références différentes associées.

System.out.println("X1: " + pnt1.x + " Y1: " +pnt1.y); 
System.out.println("X2: " + pnt2.x + " Y2: " +pnt2.y);
System.out.println(" ");

Comme prévu, la sortie sera:

X1: 0     Y1: 0
X2: 0     Y2: 0

Sur cette ligne, "passage par valeur" entre dans le jeu ...

tricky(pnt1,pnt2);           public void tricky(Point arg1, Point arg2);

Références pnt1et pnt2sont passés par valeur à la méthode délicate, ce qui signifie que maintenant votre références pnt1et pnt2ont leur copiesnom arg1et arg2.Donc pnt1et des arg1 points au même objet. (Idem pour le pnt2et arg2)

Dans la trickyméthode:

 arg1.x = 100;
 arg1.y = 100;

Suivant dans la trickyméthode

Point temp = arg1;
arg1 = arg2;
arg2 = temp;

Ici, vous créez d’abord une nouvelle tempréférence de point qui pointe sur le même endroit comme arg1référence. Ensuite, vous déplacez référence arg1pour pointer au même endroit comme arg2référence. Enfin arg2va pointer vers le même endroit comme temp.

De là , la portée de la trickyméthode est allé et vous n'avez plus accès aux références: arg1, arg2, temp. Mais il est important de noter que tout ce que vous faites avec ces références quand elles sont dans la vie affectera de manière permanente l’objet sur lequel elles sont pointées .

Donc après exécution de la méthode tricky, quand vous revenez à main, vous avez cette situation:

Alors maintenant, l'exécution complète du programme sera:

X1: 0         Y1: 0
X2: 0         Y2: 0
X1: 100       Y1: 100
X2: 0         Y2: 0

J'ai créé un fil consacré à ce genre de questions pour tous les langages de programmation here .

Java est également mentionné . Voici le court résumé:

  • Java passe les paramètres par valeur
  • "par valeur" est le seul moyen en Java de passer un paramètre à une méthode
  • utiliser des méthodes de l'objet donné en paramètre modifiera l'objet car les références pointent vers les objets d'origine. (si cette méthode modifie elle-même certaines valeurs)

Java a seulement passer par valeur. Un exemple très simple pour valider cela.

public void test() {
    MyClass obj = null;
    init(obj);
    //After calling init method, obj still points to null
    //this is because obj is passed as value and not as reference.
}
private void init(MyClass objVar) {
    objVar = new MyClass();
}

Je ne peux pas croire que personne n'ait encore mentionné Barbara Liskov. Quand elle a conçu CLU en 1974, elle a rencontré le même problème de terminologie et elle a inventé le terme appel par partage (également appelé appel par partage d'objet et appel par objet ) pour ce cas spécifique "appel par valeur où la valeur est une référence".


Je pense toujours à cela comme "passe par copie". C'est une copie de la valeur, que ce soit une primitive ou une référence. S'il s'agit d'une primitive, il s'agit d'une copie des bits correspondant à la valeur. S'il s'agit d'un objet, il s'agit d'une copie de la référence.

public class PassByCopy{
    public static void changeName(Dog d){
        d.name = "Fido";
    }
    public static void main(String[] args){
        Dog d = new Dog("Maxx");
        System.out.println("name= "+ d.name);
        changeName(d);
        System.out.println("name= "+ d.name);
    }
}
class Dog{
    public String name;
    public Dog(String s){
        this.name = s;
    }
}

sortie de java PassByCopy:

nom = Maxx
nom = Fido

Les classes et les chaînes de wrapper primitives étant immuables, aucun exemple utilisant ces types ne fonctionnera de la même manière que d'autres types / objets.


Laissez-moi essayer d’expliquer ma compréhension à l’aide de quatre exemples. Java est passe-par-valeur et non pas par référence

/ **

Pass par valeur

En Java, tous les paramètres sont passés par valeur, c'est-à-dire que l'affectation d'un argument de méthode n'est pas visible pour l'appelant.

* /

Exemple 1:

public class PassByValueString {
    public static void main(String[] args) {
        new PassByValueString().caller();
    }

    public void caller() {
        String value = "Nikhil";
        boolean valueflag = false;
        String output = method(value, valueflag);
        /*
         * 'output' is insignificant in this example. we are more interested in
         * 'value' and 'valueflag'
         */
        System.out.println("output : " + output);
        System.out.println("value : " + value);
        System.out.println("valueflag : " + valueflag);

    }

    public String method(String value, boolean valueflag) {
        value = "Anand";
        valueflag = true;
        return "output";
    }
}

Résultat

output : output
value : Nikhil
valueflag : false

Exemple 2:

/ ** * * Pass By Value * * /

public class PassByValueNewString {
    public static void main(String[] args) {
        new PassByValueNewString().caller();
    }

    public void caller() {
        String value = new String("Nikhil");
        boolean valueflag = false;
        String output = method(value, valueflag);
        /*
         * 'output' is insignificant in this example. we are more interested in
         * 'value' and 'valueflag'
         */
        System.out.println("output : " + output);
        System.out.println("value : " + value);
        System.out.println("valueflag : " + valueflag);

    }

    public String method(String value, boolean valueflag) {
        value = "Anand";
        valueflag = true;
        return "output";
    }
}

Résultat

output : output
value : Nikhil
valueflag : false

Exemple 3:

/ ** Ce 'Pass By Value a le sentiment de' Pass By Reference '

Certaines personnes disent que les types primitifs et 'String' sont 'pass by value' et les objets sont 'pass by reference'.

Mais à partir de cet exemple, nous pouvons comprendre qu’il s’agit en fait de transmettre uniquement par valeur, en gardant à l’esprit que nous transmettons ici la référence en tant que valeur. ie: la référence est passée par valeur. C'est pourquoi sont capables de changer et cela reste vrai après la portée locale. Mais nous ne pouvons pas modifier la référence réelle en dehors de la portée d'origine. ce que cela signifie est illustré par l'exemple suivant de PassByValueObjectCase2.

* /

public class PassByValueObjectCase1 {

    private class Student {
        int id;
        String name;
        public Student() {
        }
        public Student(int id, String name) {
            super();
            this.id = id;
            this.name = name;
        }
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        @Override
        public String toString() {
            return "Student [id=" + id + ", name=" + name + "]";
        }
    }

    public static void main(String[] args) {
        new PassByValueObjectCase1().caller();
    }

    public void caller() {
        Student student = new Student(10, "Nikhil");
        String output = method(student);
        /*
         * 'output' is insignificant in this example. we are more interested in
         * 'student'
         */
        System.out.println("output : " + output);
        System.out.println("student : " + student);
    }

    public String method(Student student) {
        student.setName("Anand");
        return "output";
    }
}

Résultat

output : output
student : Student [id=10, name=Anand]

Exemple 4:

/ **

En plus de ce qui a été mentionné dans Example3 (PassByValueObjectCase1.java), nous ne pouvons pas modifier la référence réelle en dehors de la portée d'origine. "

Remarque: je ne colle pas le code pour private class Student. La définition de classe pour Studentest identique à Example3.

* /

public class PassByValueObjectCase2 {

    public static void main(String[] args) {
        new PassByValueObjectCase2().caller();
    }

    public void caller() {
        // student has the actual reference to a Student object created
        // can we change this actual reference outside the local scope? Let's see
        Student student = new Student(10, "Nikhil");
        String output = method(student);
        /*
         * 'output' is insignificant in this example. we are more interested in
         * 'student'
         */
        System.out.println("output : " + output);
        System.out.println("student : " + student); // Will it print Nikhil or Anand?
    }

    public String method(Student student) {
        student = new Student(20, "Anand");
        return "output";
    }

}

Résultat

output : output
student : Student [id=10, name=Nikhil]

Le noeud du problème est que le mot référence dans l'expression "passer par référence" signifie quelque chose de complètement différent du sens habituel du mot référence en Java.

Habituellement, en Java, référence signifie une référence à un objet . Mais les termes techniques transmis par référence / valeur de la théorie des langages de programmation parlent d'une référence à la cellule de mémoire contenant la variable , ce qui est complètement différent.







pass-by-value