variable Java est-il «passe-à-référence» ou «passe-à-valeur»?




this en java (24)

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?


Java est toujours transmis par valeur, sans aucune exception.

Alors, comment se fait-il que tout le monde puisse être dérouté par cela et croire que Java est passe par référence, ou s’ils pensent avoir un exemple de Java agissant comme passe par référence? Le point clé est que Java ne fournit jamais d’accès direct aux valeurs des objets eux - mêmes , quelles que soient les circonstances. Le seul accès aux objets se fait par une référence à cet objet. Etant donné que les objets Java sont toujours consultés via une référence, plutôt que directement, il est courant de parler de champs et de variables et d'arguments de méthodes en tant qu'objets ; lorsqu'ils sont pédants, ils ne sont que des références à des objets . La confusion découle de ce changement (à proprement parler incorrect) de changement de nomenclature.

Donc, quand on appelle une méthode

  • Pour les arguments primitifs ( int , long , etc.), le paramètre pass by value est la valeur réelle de la primitive (par exemple, 3).
  • Pour les objets, la valeur passe par 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 ont copié des références public void doSomething(Foo foo) { .. } sur les mêmes objets.

Naturellement, passer par valeur une référence à un objet ressemble beaucoup à (et ne peut pas être distingué dans la pratique de) passer d'un objet par référence.


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)

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

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.


Vous ne pouvez jamais passer par référence en Java, et l'une des manières évidentes consiste à renvoyer plusieurs valeurs à partir d'un appel de méthode. Considérez le bit de code suivant en C ++:

void getValues(int& arg1, int& arg2) {
    arg1 = 1;
    arg2 = 2;
}
void caller() {
    int x;
    int y;
    getValues(x, y);
    cout << "Result: " << x << " " << y << endl;
}

Parfois, vous souhaitez utiliser le même modèle en Java, mais vous ne pouvez pas; du moins pas directement. Au lieu de cela, vous pouvez faire quelque chose comme ceci:

void getValues(int[] arg1, int[] arg2) {
    arg1[0] = 1;
    arg2[0] = 2;
}
void caller() {
    int[] x = new int[1];
    int[] y = new int[1];
    getValues(x, y);
    System.out.println("Result: " + x[0] + " " + y[0]);
}

Comme cela a été expliqué dans les réponses précédentes, en Java, vous passez un pointeur sur le tableau sous forme de valeur getValues. Cela suffit, car la méthode modifie ensuite l'élément de tableau et, par convention, vous vous attendez à ce que l'élément 0 contienne la valeur de retour. Évidemment, vous pouvez le faire de différentes manières, telles que la structuration de votre code pour que cela ne soit pas nécessaire ou la construction d'une classe pouvant contenir la valeur de retour ou permettre son paramétrage. Mais le modèle simple disponible en C ++ ci-dessus n'est pas disponible en Java.


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 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 .


En gros, la réaffectation des paramètres d'objet n'affecte pas l'argument, par exemple,

private void foo(Object bar) {
    bar = null;
}

public static void main(String[] args) {
    String baz = "Hah!";
    foo(baz);
    System.out.println(baz);
}

imprimera au "Hah!"lieu de null. La raison pour laquelle cela fonctionne est parce que barest une copie de la valeur de baz, qui est juste une référence à"Hah!" .Si elle était la référence elle - même, puis fooaurait redéfini bazà null.


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.


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.


Java transmet les paramètres par valeur et uniquement par valeur .

Pour résumer l'histoire courte:

Pour ceux qui viennent de C #: IL N'Y A PAS de paramètre "out".

Pour ceux qui viennent de PASCAL: IL N'Y A PAS de paramètre "var" .

Cela signifie que vous ne pouvez pas modifier la référence de l'objet lui-même, mais vous pouvez toujours modifier les propriétés de l'objet.

Une solution de contournement consiste à utiliser le StringBuilderparamètre à la place String. Et vous pouvez toujours utiliser des tableaux!


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 ...


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.


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]

La distinction, ou peut-être juste la façon dont je me souviens car j’avais la même impression que l’affiche originale est la suivante: Java est toujours transmis par valeur. Tous les objets (en Java, tout sauf les primitives) en Java sont des références. Ces références sont passées par valeur.


En résumé, Java objets Java ont des propriétés très particulières.

En général, Java a types primitifs ( int, bool, char, double, etc.) qui sont transmis directement par la valeur. Ensuite, Java a des objets (tout ce qui en découle java.lang.Object). En réalité, les objets sont toujours traités via une référence (une référence étant un pointeur que vous ne pouvez pas toucher). Cela signifie qu'en réalité, les objets sont passés par référence, car les références ne sont normalement pas intéressantes. Cela signifie toutefois que vous ne pouvez pas changer d'objet sur lequel vous pointez car la référence est passée par valeur.

Cela vous semble étrange et déroutant? Voyons comment C implémente passe par référence et passe par valeur. En C, la convention par défaut est passe par valeur. void foo(int x)passe un int par valeur. void foo(int *x)est une fonction qui ne veut pas int a, mais un pointeur vers un int: foo(&a). On utiliserait cela avec l' &opérateur pour passer une adresse variable.

Prenez cela en C ++, et nous avons des références. Les références sont fondamentalement (dans ce contexte) un sucre syntaxique qui masque la partie du pointeur de l'équation: void foo(int &x)est appelée par foo(a), où le compilateur lui-même sait qu'il s'agit d'une référence et où l'adresse de la non-référence adoit être passée. En Java, toutes les variables faisant référence à des objets sont en fait de type référence, forçant de fait un appel par référence pour la plupart des intentions et des objectifs sans le contrôle (et la complexité) finement précis offert, par exemple, par C ++.


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.


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 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.


Je pense que discuter du "passage par référence vs passage par valeur" n'est pas super utile.

Si vous dites "Java est une référence (valeur / référence)", dans les deux cas, vous ne fournissez pas une réponse complète. Voici quelques informations supplémentaires qui, espérons-le, aideront à comprendre ce qui se passe dans la mémoire.

Cours intensif sur pile / tas avant d’aboutir à la mise en oeuvre de Java: les valeurs s’allient et s’échappent de la pile de manière ordonnée, comme une pile d’assiettes dans une cafétéria. La mémoire dans le tas (également appelée mémoire dynamique) est aléatoire et désorganisée. La JVM trouve simplement de la place partout où elle le peut et la libère car les variables qui l'utilisent ne sont plus nécessaires.

D'accord. Tout d'abord, les primitives locales vont sur la pile. Donc ce code:

int x = 3;
float y = 101.1f;
boolean amIAwesome = true;

résultats dans ceci:

Lorsque vous déclarez et instanciez un objet. L'objet réel va sur le tas. Qu'est-ce qui se passe sur la pile? L'adresse de l'objet sur le tas. Les programmeurs C ++ appellent cela un pointeur, mais certains développeurs Java sont contre le mot "pointeur". Peu importe. Sachez simplement que l'adresse de l'objet va sur la pile.

Ainsi:

int problems = 99;
String name = "Jay-Z";

Un tableau est un objet, il va donc aussi sur le tas. Et qu'en est-il des objets dans le tableau? Ils obtiennent leur propre espace de tas et l'adresse de chaque objet va à l'intérieur du tableau.

JButton[] marxBros = new JButton[3];
marxBros[0] = new JButton("Groucho");
marxBros[1] = new JButton("Zeppo");
marxBros[2] = new JButton("Harpo");

Alors, qu'est-ce qui est passé lorsque vous appelez une méthode? Si vous transmettez un objet, vous transmettez en réalité l'adresse de l'objet. Certains pourraient dire la "valeur" de l'adresse, et d'autres disent que c'est juste une référence à l'objet. Telle est la genèse de la guerre sainte entre les partisans de "référence" et de "valeur". Ce que vous appelez cela n'est pas aussi important que de comprendre que ce qui est transmis est l'adresse de l'objet.

private static void shout(String name){
    System.out.println("There goes " + name + "!");
}

public static void main(String[] args){
    String hisName = "John J. Jingleheimerschmitz";
    String myName = hisName;
    shout(myName);
}

Une chaîne est créée et son espace est alloué dans le tas. L'adresse de la chaîne est stockée dans la pile. L'identifiant hisName étant hisName que l'adresse de la deuxième chaîne est identique à celle de la première, aucune nouvelle chaîne n'est créée. et aucun nouvel espace de pile n'est alloué, mais un nouvel identifiant est créé sur la pile. Ensuite, nous appelons shout() : un nouveau cadre de pile est créé et un nouvel identifiant, name est créé et l’adresse de la chaîne existante est affectée.

Alors, valeur, référence? Vous dites "pomme de terre".


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.


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".


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?





pass-by-value