Dupliquer des objects en Java

J’ai appris que lorsque vous modifiez une variable en Java, elle ne change pas la variable sur laquelle elle était basée

int a = new Integer(5); int b = a; b = b + b; System.out.println(a); // 5 as expected System.out.println(b); // 10 as expected 

J’ai supposé une chose similaire pour les objects. Considérez cette classe.

 public class SomeObject { public Ssortingng text; public SomeObject(Ssortingng text) { this.setText(text); } public Ssortingng getText() { return text; } public void setText(Ssortingng text) { this.text = text; } } 

Après avoir essayé ce code, je me suis trompé.

 SomeObject s1 = new SomeObject("first"); SomeObject s2 = s1; s2.setText("second"); System.out.println(s1.getText()); // second as UNexpected System.out.println(s2.getText()); // second as expected 

S’il vous plaît expliquez-moi pourquoi la modification de l’un des objects affecte l’autre. Je comprends que la valeur du texte variable est stockée au même endroit en mémoire pour les deux objects.

Pourquoi les valeurs des variables sont-elles indépendantes mais corrélées pour les objects?

Aussi, comment dupliquer SomeObject, si la simple affectation ne fait pas le travail?

Chaque variable en Java est une référence . Alors quand tu le fais

 SomeClass s2 = s1; 

vous pointez simplement s2 vers le même object que s1 pointe vers. Vous atsortingbuez en fait la valeur de la référence s1 (qui pointe sur une instance de SomeClass ) à s2. Si vous modifiez s1 , s2 sera également modifié (car il pointe vers le même object).

Il existe une exception, les types primitifs: int, double, float, boolean, char, byte, short, long . Ils sont stockés par valeur. Ainsi, lorsque vous utilisez = , vous n’atsortingbuez que la valeur, mais ils ne peuvent pas pointer vers le même object (car ils ne sont pas des références). Cela signifie que

 int b = a; 

ne définit que la valeur de b à la valeur de a . Si vous changez a , b ne changera pas.

En fin de compte, tout est assigné par valeur, c’est juste la valeur de la référence et non la valeur de l’object (à l’exception des types primitifs mentionnés ci-dessus).

Donc, dans votre cas, si vous voulez faire une copie de s1 , vous pouvez le faire comme ceci:

 SomeClass s1 = new SomeClass("first"); SomeClass s2 = new SomeClass(s1.getText()); 

Vous pouvez également append un constructeur de copie à SomeClass qui prend une instance en argument et le copie dans sa propre instance.

 class SomeClass { private Ssortingng text; // all your fields and methods go here public SomeClass(SomeClass copyInstance) { this.text = new Ssortingng(copyInstance.text); } } 

Avec cela, vous pouvez copier un object assez facilement:

 SomeClass s2 = new SomeClass(s1); 

La réponse de @brimborium est très bonne (+1 pour lui), mais je veux juste en dire plus en utilisant quelques chiffres. Prenons d’abord la tâche primitive:

 int a = new Integer(5); int b = a; b = b + b; System.out.println(a); // 5 as expected System.out.println(b); // 10 as expected 
 int a = new Integer(5); 

1- La première instruction crée un object Integer de valeur 5. Puis, en l’affectant à la variable a , l’object Integer sera unboxed et stocké dans une primitive.

Après avoir créé l’object Integer et avant l’affectation:

entrer la description de l'image ici

Après la mission:

entrer la description de l'image ici

 int b = a; 

2- Cela ne fera que lire la valeur de a puis la stocker dans b .

(L’object Integer est maintenant éligible pour la récupération de place, mais pas nécessairement pour le moment)

entrer la description de l'image ici

 b = b + b; 

3- Ceci lit la valeur de b deux fois, les ajoute ensemble et place la nouvelle valeur dans b .

entrer la description de l'image ici


D’autre part:

 SomeObject s1 = new SomeObject("first"); SomeObject s2 = s1; s2.setText("second"); System.out.println(s1.getText()); // second as UNexpected System.out.println(s2.getText()); // second as expected 
 SomeObject s1 = new SomeObject("first"); 

1- Crée une nouvelle instance de la classe SomeObject et l’assigne à la référence s1 .

entrer la description de l'image ici

 SomeObject s2 = s1; 

2- Cela fera que la référence s2 pointe vers l’object sur lequel s1 pointe.

entrer la description de l'image ici

 s2.setText("second"); 

3- Lorsque vous utilisez des parameters sur une référence, cela modifiera l’object sur lequel la référence pointe.

entrer la description de l'image ici

 System.out.println(s1.getText()); System.out.println(s2.getText()); 

4- Les deux doivent imprimer en second , puisque les deux références s1 et s2 se réfèrent au même object (comme indiqué dans la figure précédente).

Quand tu fais ça

 SomeObject s1 = new SomeObject("first"); SomeObject s2 = s1; 

vous avez 2 références au même object. Ce qui signifie que quel que soit l’object de référence que vous utilisez, les modifications que vous apporterez seront visibles lors de l’utilisation de la deuxième référence.

Pensez-y comme ceci: vous avez une télévision dans la pièce, mais deux télécommandes: peu importe la télécommande que vous utilisez, vous apporterez toujours des modifications au même object sous-jacent (la télévision).

Lorsque vous écrivez:

 SomeObject s1 = new SomeObject("first"); 

s1 n’est pas un object SomeObject . C’est une référence à l’object SomeObject .

Par conséquent, si vous affectez s2 à s1, vous atsortingbuez simplement des références / descripteurs et l’object sous-jacent est le même. Ceci est important en Java. Tout est pass-by-value, mais vous ne transmettez jamais d’objects, mais uniquement des références à des objects.

Ainsi, lorsque vous affectez s1 = s2 , puis appelez une méthode sur s2 qui modifie un object, l’object sous-jacent est modifié et c’est visible lorsque vous référencez l’object via s1 .

C’est un argument pour l’ immuabilité dans les objects. En rendant les objects immuables, ils ne changeront pas sous vous et se comporteront donc de manière plus prévisible. Si vous souhaitez copier un object, la méthode la plus simple et la plus pratique consiste à écrire une méthode copy() qui crée simplement une nouvelle version et copie les champs. Vous pouvez faire des copies intelligentes en utilisant la sérialisation / reflection, mais c’est plus complexe, évidemment.

Parmi les dix erreurs les programmeurs Java Marque :

6 – Confusion sur le dépassement par valeur et passage par référence

Cela peut être un problème frustrant à diagnostiquer, car lorsque vous examinez le code, vous pouvez être sûr qu’il passe par référence, mais trouvez que sa transmission est réellement effectuée par valeur. Java utilise les deux, vous devez donc comprendre quand vous passez par valeur et quand vous passez par référence.

Lorsque vous transmettez un type de données primitif, tel qu’un caractère char, int, float ou double, à une fonction, vous passez par valeur. Cela signifie qu’une copie du type de données est dupliquée et transmise à la fonction. Si la fonction choisit de modifier cette valeur, elle ne modifiera que la copie. Une fois la fonction terminée et le contrôle renvoyé à la fonction de retour, la variable “real” sera inchangée et aucune modification ne sera enregistrée. Si vous devez modifier un type de données primitif, faites-en une valeur de retour pour une fonction ou placez-la dans un object.

Parce que int est un type primitif, int b = a; est une copie par valeur, ce qui signifie que a et b sont deux objects différents, mais avec la même valeur.

SomeObject s2 = s1; faites s1 et s2 deux références d’un même object, donc si vous en modifiez une, l’autre sera également modifiée.

Une bonne solution consiste à implémenter un autre constructeur comme celui-ci:

 public class SomeObject{ public SomeObject(SomeObject someObject) { setText(someObject.getText()); } // your code } 

Ensuite, utilisez-le comme ça:

 SomeObject s2 = new SomeObject(s1); 

Dans votre code, s1 et s2 sont le même object (vous n’avez créé qu’un object avec new ) et vous laissez s2 pointer sur le même object dans la ligne suivante. Par conséquent, lorsque vous modifiez du text il change à la fois si vous référencez la valeur via s1 et s2 .

L’opérateur + sur Entiers crée un nouvel object, il ne modifie pas l’object existant (l’ajout de 5 + 5 ne donne donc pas 5 à la nouvelle valeur 10 …).

C’est parce que la JVM stocke un pointeur sur s1 . Lorsque vous appelez s2 = s1 , vous dites essentiellement que le pointeur s2 (c’est-à-dire l’adresse mémoire) a la même valeur que celui de s1 . Comme ils pointent tous deux vers le même endroit en mémoire, ils représentent exactement la même chose.

L’opérateur = atsortingbue des valeurs de pointeur. Il ne copie pas l’object.

Le clonage d’objects est une question compliquée en soi. Chaque object a une méthode clone () que vous pourriez être tenté d’utiliser, mais il fait une copie superficielle (ce qui signifie en gros qu’il fait une copie de l’object de niveau supérieur mais que tout object contenu dans celui-ci n’est pas cloné). Si vous voulez jouer avec des copies d’objects, lisez définitivement Java efficace de Joshua Bloch.

Commençons par votre deuxième exemple:

Cette première instruction assigne un nouvel object à s1

 SomeObject s1 = new SomeObject("first"); 

Lorsque vous effectuez l’affectation dans la deuxième instruction ( SomeObject s2 = s1 ), vous s2 à s2 de pointer sur le même object que celui sur s1 pointe actuellement. Vous avez donc deux références au même object.

Notez que vous n’avez pas dupliqué le SomeObject , deux variables ne font que pointer sur le même object. Donc, si vous modifiez s1 ou s2 , vous modifiez en fait le même object (notez que si vous aviez quelque chose comme s2 = new SomeObject("second") ils pointe maintenant vers différents objects).

Dans votre premier exemple, a et b sont des valeurs primitives, la modification de l’une n’affectera pas l’autre.

Sous le capot de Java, tous les objects fonctionnent en utilisant une valeur de passage. Pour les objects, vous passez la “valeur” que vous passez est l’emplacement de l’object en mémoire (il semble donc avoir un effet similaire de passage par référence). Les primitives se comportent différemment et transmettent simplement une copie de la valeur.

Les réponses ci-dessus expliquent le comportement que vous voyez.

En réponse à “Aussi, comment dupliquer SomeObject, si la simple affectation ne fait pas le travail?” – essayez de chercher des cloneable (une interface Java qui offre un moyen de copier des objects) et des « copy constructors » (une approche alternative et sans doute meilleure)

L’affectation d’objects à une référence ne clone pas votre object. Les références sont comme des pointeurs. Ils pointent vers un object et lorsque des opérations sont appelées, elles sont effectuées sur l’object pointé par le pointeur. Dans votre exemple, s1 et s2 pointent vers le même object et les parameters modifient l’état du même object et les modifications sont visibles entre les références.

Changez votre classe afin de créer une nouvelle référence au lieu d’utiliser la même:

 public class SomeObject{ public Ssortingng text; public SomeObject(Ssortingng text){ this.setText(text); } public Ssortingng getText(){ return text; } public void setText(Ssortingng text){ this.text = new Ssortingng(text); } } 

Vous pouvez utiliser quelque chose comme ça (je ne prétends pas avoir la solution idéale):

 public class SomeObject{ private Ssortingng text; public SomeObject(Ssortingng text){ this.text = text; } public SomeObject(SomeObject object) { this.text = new Ssortingng(object.getText()); } public Ssortingng getText(){ return text; } public void setText(Ssortingng text){ this.text = text; } } 

Usage:

 SomeObject s1 = new SomeObject("first"); SomeObject s2 = new SomeObject(s1); s2.setText("second"); System.out.println(s1.getText()); // first System.out.println(s2.getText()); // second 
 int a = new Integer(5) 

Dans le cas ci-dessus, un nouvel entier est créé. Ici Integer est un type non primitif et la valeur qu’il contient est convertie (en int) et assignée à un int ‘a’.

 SomeObject s1 = new SomeObject("first"); SomeObject s2 = s1; 

Dans ce cas, s1 et s2 sont tous deux des types de référence. Ils ne sont pas créés pour contenir une valeur comme les types primitifs, ils contiennent plutôt la référence d’un object. Par souci de compréhension, nous pouvons penser que la référence est un lien qui indique où je peux trouver l’object référencé.

Ici la référence s1 nous dit où on peut trouver la valeur de “first” (qui est en fait stockée dans la mémoire de l’ordinateur dans une instance de SomeObject). En d’autre mots s1 est l’adresse de l’object de classe “SomeObject”. Par la mission suivante –

 SomeObject s2 = s1; 

nous ne faisons que copier la valeur stockée dans s1 à s2 et nous soaps maintenant que s1 contient l’adresse de la chaîne “first”. Après cet assigment, println () produit la même sortie car s1 et s2 réfractant le même object.

Avec le constructeur de copie, vous pouvez copier un object avec la méthode clone () si vous êtes un utilisateur java. Clone peut être utilisé de la manière suivante –

 SomeObject s3 = s1.clone(); 

Pour plus d’informations sur clone (), il s’agit d’un lien utile http://en.wikipedia.org/wiki/Clone_%28Java_method%29

C’est parce que s1 et s2 ne fonctionnent que comme références à vos objects. Lorsque vous affectez s2 = s1 vous n’atsortingbuez que la référence, ce qui signifie que les deux vont pointer vers le même object en mémoire (l’object qui a le texte actuel “first”).

Lorsque vous effectuez un setter maintenant, que ce soit sur s1 ou s2, les deux modifieront le même object.

Lorsque vous affectez et objectez à une variable, vous atsortingbuez réellement la référence à cet object. Les deux variables s1 et s2 font donc référence au même object.

Depuis les objects ont été référencés par une référence. Donc, si vous écrivez s2 = s1, seule la référence sera copiée. C’est comme en C si vous ne gérez que des adresses mémoire. Et si vous copiez l’adresse d’une mémoire et que vous modifiez la valeur derrière cette adresse, les deux pointeurs (références) modifieront la valeur unique à ce stade de la mémoire.

La deuxième ligne ( SomeObject s2 = s1; ) assigne simplement une deuxième variable à la première. Cela conduit à une seconde variable pointant vers la même instance d’object que la première.