Signaler

Java passage par reference

Posez votre question ulquiorra 31Messages postés jeudi 6 septembre 2007Date d'inscription 29 juillet 2009 Dernière intervention - Dernière réponse le 12 nov. 2015 à 19:14
Bonjour,
j'ai une problème au niveau d'une methode que je viens de creer .
Cette methode recoit un argument de type short , elle lui affecte une valeur, mais le problème c que ca valeur ne change pas en dehors de la fonction
dans main () j'appelle la methode : i=0, affect(i); la méthode fait i=1; quand j'affiche la valeur de i c'est toujours 0;
je crois que je devrait passer l'argument par reference ou par adresse mais je ne sais pas comment le faire en java
Afficher la suite 
Utile
+25
moins plus
Bonsoir,
En java, tu ne peux pas choisir de passer tes paramètres par référence ou par valeur.
La règle est la suivante :
- ton paramètre est un objet, alors il est passé par référence
- ton paramètre est un type primitif, alors il est passé par valeur (par copie)

Donc si tu passe un short en paramètre, alors tu ne pourras pas modifier sa valeur initiale (tu vas changer la valeur de la copie, mais ça ne sera pas répercuté sur la variable initiale).
Si tu passes un Short, on peut imaginer modifier sa valeur interne, mais en fait non puisque Short est immutable (et final pour rien améliorer) :(

Donc une solution est de créer un pojo comme le suivant :
public class ShortContainer {
 private short value;
 
 public short getValue() { return this.value; }
 public void setValue(short value) { this.value = value; }
}


Voilà. C'est un peu dommage car au final c'est un peu comme si on utilisait un Short, mais bon ça répond à ton problème.

Cordialement,
Ajouter un commentaire
Utile
+3
moins plus
J'ai le même probleme avec un objet de type String ou encore avec un objet de type HttpRequest.
J'envois l'objet (enfin en principe la référence à l'objet) à une méthode d'une autre classe en paramètre.
La valeur est modifiée dans la méthode. La valeur ne bouge pas à la sortie de ma méthode...

Voici le code :

public class main {

public static void main(String[] args) {
String test="test";
ValideurTest.test(test);
System.out.println("Résultat : "+test);
}

public class ValideurTest {

public static void test(String s){
s="le test marche";
}

}

A l'exécution :
Résultat : test


Comment faire ?
SeiromeM- 11 mai 2012 à 14:50
J'aimerai bien savoir aussi ...
Répondre
KX 13451Messages postés samedi 31 mai 2008Date d'inscription ContributeurStatut 21 septembre 2016 Dernière intervention - 11 mai 2012 à 15:58
On ne peut pas !

En fait la référence 's' est modifiée et correspond après affectation à l'objet String de valeur "le test marche", alors que la référence 'test' n'a pas été modifiée et correspond toujours à l'objet String de valeur "test", ce qu'il faudrait ce n'est donc pas modifier les références mais modifier les objets, en faisant appel à des méthodes de modification. Or les objets String sont immuables, c'est à dire qu'aucune méthode de la classe String ne va modifier l'objet, elles vont systématiquement créer de nouveaux objets qui auront donc des références différentes et ça reviendra au même..?

Ce que l'on peut faire, c'est passer par un StringBuilder dont on pourra modifier la valeur sans changer aux références. Exemple :

public class Main
{
	public static void main(String...args) 
	{
		StringBuilder test = new StringBuilder("test");
		
		ValideurTest.test(test);
		
		System.out.println("Résultat : "+test);
	}
}

public class ValideurTest
{
	public static void test(StringBuilder s)
	{ 
		s.replace(0, s.length(), "le test marche");	
	}
}
Répondre
zibox2- 15 mai 2012 à 19:33
Avec un StringBuffer à la place du String ça marche !
Dommage que la classe String n'ait pas une "affect()" par défaut...

Merci pour vos réponses !

++
Répondre
Loukiluk- 12 nov. 2015 à 12:34
Non justement.
Mettre une méthode affect() à String est une mauvaise idée.
String, Integer, Byte, Short, Long, Double, Float, Boolean et Char (à ne pas confondre avec les types primitifs) sont des classes immuables. C'est-à-dire qu'une instance ce des classes ne peut être modifiée en cours de route.
En pratique, on a l'impression que c'est le cas, car on peut faire ceci :
Integer i = 2;
i = 3;

Mais en fait, ce code n'est pas compilé directement comme ça, en réalité, le compilateur Java remplace de lui-même par :
Integer i = new Integer(2);
i = new Integer(3);

On a donc bien deux objets différents qui sont affectés à i. Et ça fonctionne de la même manière pour les String. Ce code :
public static void test(String s) {
    s = "le test marche";
}

devient (juste avant la réelle compilation) :
public static void test(String s) {
    s = new String("le test marche");
}


Petite note avant la suite : quand je parle de variable objet, c'est ce que contient par exemple s ou i dans mes exemples du dessus. Quand je parle de l'objet (mais pas de la variable objet, qui sont des pointeurs), je parle de l'objet en lui-même : par exemple (ci-dessus) Integer(2) ou "le test marche".

Autre détail important qu'il faut bien avoir en tête (et que les habitués du C et du C++ ont l'habitude de gérer), c'est que les variables objets ne sont pas des valeurs, mais des pointeurs. Toutes les variables objets sans exception (les variables de type primitif ne représentent pas des objets). C'est une des raisons pour lesquelles les classes correspondant à ces types primitifs ont été créées (Integer, Short et compagnie).
Cela veut dire que 's' ne contient pas directement la chaîne de caractère "le test marche", mais en fait l'adresse qui pointe sur l'emplacement mémoire où est situé la chaîne de caractères "le test marche". D'ailleurs, si jamais on fait : *String s2 = "le test marche";* dans la méthode statique 'test', s2 contiendra l'adresse mémoire où se situe la chaîne de caractères "le test marche".
C'est la raison majeure pour laquelle les String sont immuables (de même que Integer, Short, etc...) : ça permet à Java de faire une optimisation sans risque, qui est non pas de recréer la chaîne de caractères ailleurs dans la mémoire et affecter son adresse à s2, mais plutôt de lui (s2) affecter directement l'adresse de "le test marche" (qui ezlle existe déjà dans la mémoire).
Au final, s et s2 contiendront la même adresse mémoire. Ça, c'est en théorie. En pratique, c'est un peu plus subtil.

Du fait que ce sont en fait des pointeurs, en découle une autre conséquence (et d'autres, mais je vais pas être exhaustif) : les objets passés en paramètres transmettent en réalité l'adresse, aucun copie d'objet n'est effectuée.
Reprenons le code de tout à l'heure :
public class main {
public static void main(String[] args) {
    String test = "test";
    ValideurTest.test(test);
    System.out.println("Résultat : "+ test);
}

public class ValideurTest {
    public static void test(String s) {
    s = "le test marche";
    }
} 

Le s dans la méthode main contient l'adresse qui pointe (dans la mémoire) vers la chaîne de caractères. Admettons que ce soit l'adresse 1000.
En schématisant :

@1000 : "test" (le @ équivaut à adresse)
[main.main]s : @1000

(J'utilise [<classe>.<méthode>]<variable> pour pouvoir bien les différencier).
On appelle la méthode statique test de ValideurTest en lui passant en paramètre [main.main]s.
Dans cette méthode, le paramètre s ([ValidateurTest.test]s), qui est une autre variable objet, reçoit l'adresse mémoire de [main.main]s. Il contient donc désormais l'adresse 1000.
En schématisant :

@1000 : "test"
[main.main]s : @1000
[ValideurTest.test]s : @1000

Ensuite, on affecte "le test marche" à [ValideurTest.test]s (en admettant que Java crée la chaîne de caractères à l'adresse 1008). On a maintenant :

@1000 : "test"
@1008 : "le test marche"
[main.main]s : @1000
[ValideurTest.test]s : @1008

Qu'est-ce qu'il s'est passé ? On a changé l'adresse pointée par [ValideurTest.test]s, mais pas celle de [main.main]s. [main.main]s désigne donc toujours la chaîne de caractères "test". A la fin de la méthode ValideurTest.test, [ValideurTest.test]s disparais et la mémoire où se situe la chaîne de caractères "le test marche" sera libérée par le ramasse-miettes (Garbage Collector en anglais) de la JVM (Java Virtual Machine : Machine Virtuelle Java, en français) à un moment ou un autre.
Le ramasse-miettes est un processus automaituqe qui libère la mémoire des objets qui ne sont plus référencés (c-à-d qu'il n'y a plus de variable objet qui pointe vers ces objets). Il est présent pendant toute la durée d'exécution du programme, c'est la JVM qui le lance.

Ce fonctionnement, qui est un peu partout dans Java, est difficile à appréhender quand on n'a pas fait de C ni de C++. C'est une des raisons qui me pousse à conseiller ces langages avant de faire du Java.
Une autre solution est de tomber sur un tutoriel ou un prof qui explique tout ça en détail (et par chance, c'est mon cas : mon prof de Java a passé 2h de cours en Amphi pour expliquer uniquement comment fonctionne la mémoire en Java, en comparant avec le C (je suis en 3e année de Licence Info)). Tout ça, je l'ai compris en 5mn grâce au fait que je fais du C depuis 7 ans et du C++ depuis 5 ans.
Mais l'inconvénient est que ces deux langages demandent beaucoup de rigueur lors de la manipulation de la mémoire, ce qui les rend difficiles à apprendre en tant que débutant, sauf exceptions (certains comprennent bien le fonctionnement des pointeurs en 1 mois, d'autres mettent 1 an avant d'avoir le déclic).
Ça dépend de ce qu'on veut et à quoi on a accès. Bref, je commence à être hors sujet xD

Pour terminer, s'il faut retenir une chose : c'est que les variables objets (pas les variables de types primitifs) contiennent l'adresse vers l'emplacement mémoire (la RAM) où est stocké l'objet. Tant qu'on a compris ça et ce qui en découle, pour le reste ça roule !
Répondre
KX 13451Messages postés samedi 31 mai 2008Date d'inscription ModérateurStatut 21 septembre 2016 Dernière intervention - 12 nov. 2015 à 19:14
@Loukiluk
Dans les grandes lignes je suis d'accord avec toi, il y a cependant une petite subtilité dont tu ne parles pas (peut être que je vais te l'apprendre).

La classe String gère un cache de données de tel sorte que tout les String créés en dur dans le code sont mutualisés, il n'y aura qu'un seul objet par valeur, et on pourra comparer leurs références.

Exemple :
class A {
    public static final String TOTO = "toto";
}

class B {
    public static void main(String[] args) {
        String toto = "toto";
        System.out.println(toto == A.TOTO); // true
        String newToto = new String("toto");
        System.out.println(newToto == A.TOTO); // false
        String internToto = newToto.intern(); // ajoute newToto au pool de String
        System.out.println(internToto == A.TOTO); // true
}

Remarque : à la vue de cette mécanique il serait dramatique de modifier un String (même si c'est faisable sous certaines conditions) puisqu'il modifierait tout les autres String qui utilisent la même référence...
Répondre
Ajouter un commentaire
Utile
+0
moins plus
glabal i pour que i prenne sa valeur dans tout ton code :)
ulquiorra 31Messages postés jeudi 6 septembre 2007Date d'inscription 29 juillet 2009 Dernière intervention - 21 juil. 2009 à 14:54
Désolé mais je ne veux pas faire ca

y aura il une autre solution ?
Répondre
Djidane- 9 oct. 2009 à 13:22
Simplement en faisant

i = affect(i);

Bien à toi,
Djidane
Répondre
Ajouter un commentaire
Utile
+0
moins plus
la class String est immutable c est pour cela que la valeur ne peut changer
Ajouter un commentaire

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes.

Le fait d'être membre vous permet d'avoir des options supplémentaires.

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !