variable - reflexivité java




Passer une chaîne par référence en Java? (9)

Aaron Digulla a la meilleure réponse jusqu'à présent. Une variante de sa deuxième option consiste à utiliser la classe wrapper ou container MutableObject de la librairie lang commune 3+:

void fillString(MutableObject<String> c) { c.setValue(c.getValue() + "foo"); }

vous enregistrez la déclaration de la classe de conteneur. L'inconvénient est une dépendance à la bibliothèque lang commune. Mais la lib a beaucoup de fonction utile et presque tous les projets plus importants sur lesquels j'ai travaillé l'ont utilisé.

J'ai l'habitude de faire ce qui suit en C :

void main() {
    String zText = "";
    fillString(zText);
    printf(zText);
}

void fillString(String zText) {
    zText += "foo";
}

Et la sortie est:

foo

Cependant, en Java, cela ne semble pas fonctionner. Je suppose que l'objet String est copié à la place de passé par référencé . Je pensais que les chaînes étaient des objets, qui sont toujours transmis par référence.

Qu'est-ce qui se passe ici?


Ce qui se passe, c'est que la référence est passée en valeur, c'est-à-dire qu'une copie de la référence est passée. Rien dans java n'est passé par référence, et comme une chaîne est immuable, cette affectation crée un nouvel objet chaîne que la copie de la référence pointe vers. La référence d'origine pointe toujours vers la chaîne vide.

Ce serait la même chose pour n'importe quel objet, c'est-à-dire, en le définissant à une nouvelle valeur dans une méthode. L'exemple ci-dessous rend plus évident ce qui se passe, mais concaténer une chaîne est vraiment la même chose.

void foo( object o )
{
    o = new Object( );  // original reference still points to old value on the heap
}

En Java rien n'est passé par référence . Tout est passé en valeur . Les références d'objet sont passées par valeur. De plus, les chaînes sont immuables . Donc, quand vous ajoutez à la chaîne passée, vous obtenez simplement une nouvelle chaîne. Vous pouvez utiliser une valeur de retour ou passer un StringBuffer à la place.


La chaîne est immuable dans Java. vous ne pouvez pas modifier / modifier un littéral / objet de chaîne existant.

String s = "Bonjour"; s = s + "salut";

Ici, la référence précédente s est remplacée par la nouvelle référence pointant vers la valeur "HelloHi".

Cependant, pour apporter la mutabilité nous avons StringBuilder et StringBuffer.

StringBuilder s = nouveau StringBuilder (); s.append ("Salut");

ceci ajoute la nouvelle valeur "Hi" à la même référence. //



Les chaînes sont passées par référence (enfin, à proprement parler, comme le note Ed Swangren, une référence à la chaîne est passée en valeur ) mais elles sont immuables.

zText += foo;

est équivalent à

zText = new String(zText + "foo");

C'est-à-dire, il modifie la variable (locale) zText sorte qu'elle pointe maintenant vers un nouvel emplacement de mémoire, dans lequel est une String avec le contenu original de zText , avec "foo" ajouté. L'objet d'origine n'est pas modifié et la variable locale zText la méthode main() zText toujours vers la chaîne d'origine (vide).

class StringFiller {

  static void fillString(String zText) {
    zText += "foo";
    System.out.println("Local value: " + zText);
  }

  public static void main(String[] args) {
    String zText = "";
    System.out.println("Original value: " + zText);
    fillString(zText);
    System.out.println("Final value: " + zText);
  }

}

imprime

Original value:
Local value: foo
Final value:

Si vous souhaitez modifier la chaîne, vous pouvez, comme indiqué, utiliser StringBuilder ou bien un conteneur (un tableau ou une classe de conteneur personnalisée) qui vous donne un niveau supplémentaire d'indirection de pointeur. Vous pouvez également renvoyer la nouvelle valeur et l'affecter:

class StringFiller2 {

  static StringfillString(String zText) {
    zText += "foo";
    System.out.println("Local value: " + zText);
    return zText;
  }

  public static void main(String[] args) {
    String zText = "";
    System.out.println("Original value: " + zText);
    zText = fillString(zText);
    System.out.println("Final value: " + zText);
  }

}

imprime

Original value:
Local value: foo
Final value: foo

C'est probablement la solution la plus proche de Java dans le cas général - voir l'article Java efficace "Favorisez l'immutabilité".

Comme indiqué, cependant, StringBuilder vous donnera souvent de meilleures performances - si vous avez beaucoup à faire, en particulier dans une boucle, utilisez StringBuilder .

Mais essayez de transmettre des Strings immuables plutôt que des StringBuilders mutables si vous le pouvez - votre code sera plus facile à lire et plus facile à maintenir. Envisagez de rendre vos paramètres final et de configurer votre EDI pour qu'il vous avertisse lorsque vous réaffectez un paramètre de méthode à une nouvelle valeur.


String est une classe spéciale en Java. C'est Thread Save qui signifie "Une fois qu'une instance String est créée, le contenu de l'instance String ne sera jamais modifié".

Voici ce qui se passe pour

 zText += "foo";

Tout d'abord, le compilateur Java obtiendra la valeur de l'occurrence de la chaîne de caractères zText, puis créera une nouvelle instance de chaîne dont la valeur est zText ajoutant "foo". Vous savez donc pourquoi l'instance sur laquelle zText pointe ne change pas. C'est totalement une nouvelle instance. En fait, même String "foo" est une nouvelle instance de String. Donc, pour cette instruction, Java créera deux instances de String, l'une est "foo", l'autre est la valeur de zText append "foo". La règle est simple: La valeur de l'instance String ne sera jamais modifiée.

Pour la méthode fillString, vous pouvez utiliser un StringBuffer comme paramètre, ou vous pouvez le changer comme ceci:

String fillString(String zText) {
    return zText += "foo";
}

Vous avez trois options:

  1. Utilisez un StringBuilder:

    StringBuilder zText = new StringBuilder ();
    void fillString(StringBuilder zText) { zText.append ("foo"); }
    
  2. Créez une classe de conteneur et transmettez une instance du conteneur à votre méthode:

    public class Container { public String data; }
    void fillString(Container c) { c.data += "foo"; }
    
  3. Créer un tableau:

    new String[] zText = new String[1];
    zText[0] = "";
    
    void fillString(String[] zText) { zText[0] += "foo"; }
    

Du point de vue des performances, StringBuilder est généralement la meilleure option.


les objets sont passés par référence, les primitives sont passées par valeur.

La chaîne n'est pas une primitive, c'est un objet, et c'est un cas particulier d'objet.

Ceci est à des fins de sauvegarde de la mémoire. En JVM, il existe un pool de chaînes. Pour chaque chaîne créée, JVM essaiera de voir si la même chaîne existe dans le pool de chaînes, et pointera dessus s'il y en a déjà une.

public class TestString
{
    private static String a = "hello world";
    private static String b = "hello world";
    private static String c = "hello " + "world";
    private static String d = new String("hello world");

    private static Object o1 = new Object();
    private static Object o2 = new Object();

    public static void main(String[] args)
    {
        System.out.println("a==b:"+(a == b));
        System.out.println("a==c:"+(a == c));
        System.out.println("a==d:"+(a == d));
        System.out.println("a.equals(d):"+(a.equals(d)));
        System.out.println("o1==o2:"+(o1 == o2));

        passString(a);
        passString(d);
    }

    public static void passString(String s)
    {
        System.out.println("passString:"+(a == s));
    }
}

/ * SORTIE * /

a==b:true
a==c:true
a==d:false
a.equals(d):true
o1==o2:false
passString:true
passString:false

le == vérifie l'adresse mémoire (référence), et le .equals vérifie le contenu (valeur)







pass-by-value