417 votes

Une chaîne de Java est vraiment immuable ?

Nous savons tous que `` est immuable en Java, mais vérifier le code suivant :

Pourquoi ce programme ne fonctionne pas comme ça ? Et pourquoi la valeur de et a changé, mais ne pas `` ?

415voto

haraldK Points 5751

String est immuable* mais cela ne signifie que vous ne pouvez pas le changer en utilisant son API publique.

Ce que vous faites ici est de contourner la normale de l'API, à l'aide de la réflexion. De la même manière, vous pouvez modifier les valeurs d'énumérations, de modifier la table de recherche utilisé en Entier l'autoboxing etc.

Maintenant, la raison en s1 et s2 la valeur de changement, c'est qu'ils se réfèrent à la même interné chaîne. Le compilateur est-ce (comme mentionné par d'autres réponses).

La raison en s3 n'a pas était en fait un peu surprenant pour moi, comme je l'ai pensé qu'il serait part de l' value tableau (il l'a fait dans la version antérieure de Java, avant de Java 7u6). Cependant, en regardant le code source de l' String, nous pouvons voir que l' value tableau de caractères d'une chaîne est effectivement copiés (à l'aide d' Arrays.copyOfRange(..)). C'est pourquoi il n'est pas modifié.

Vous pouvez installer un SecurityManager, pour éviter de code malveillant à faire de telles choses. Mais gardez à l'esprit que certaines bibliothèques dépendent de l'utilisation de ces sortes de réflexions astuces (généralement, outils ORM, AOP bibliothèques, etc).

*) J'ai d'abord écrit qu' Strings ne sont pas vraiment immuable, juste "efficace immuable". Cela peut être trompeur dans la mise en œuvre actuelle de l' String, où l' value tableau est en effet marquée private final. Il est encore intéressant de noter, cependant, qu'il n'est pas possible de déclarer un tableau en Java comme immuable, donc il faut prendre soin de ne pas l'exposer à l'extérieur de sa classe, même avec le bon modificateurs d'accès.


Comme ce sujet semble massivement populaire, voici quelques suggestions de lectures complémentaires: Heinz Kabutz la Réflexion de la Folie de parler de JavaZone 2009, qui couvre beaucoup de questions dans l'OP, le long de avec d'autres de la réflexion... eh bien... la folie.

Il couvre pourquoi ce qui est parfois utile. Et pourquoi, la plupart du temps, vous devriez éviter. :-)

96voto

Zaheer Ahmed Points 12945

En Java, si deux chaînes de caractères primitifs variable initialisée par la même littéral ils ont attribué la même référence à deux variables:

String Test1="Hello World";
String Test2="Hello World";
System.out.println(test1==test2); // true

initialization

c'est la raison pour comparaison renvoie la valeur true. Troisième chaîne créée à l'aide de substring() qui en font une nouvelle chaîne au lieu de pointer le même.

sub string

lorsque vous accédez à l'aide de la chaîne de la réflexion, Vous obtenez le réel pointeur:

Field field = String.class.getDeclaredField("value");  
field.setAccessible(true);  

Ainsi le changement de ce qui va changer la chaîne de caractères contenant le pointeur de lui, mais que s3 est créé une nouvelle chaîne en raison d' substring() il ne changerait pas.

change

51voto

Bohemian Points 134107

Vous êtes à l'aide de la réflexion afin de contourner l'immutabilité de la Chaîne - c'est une forme d ' "attaque".

Il y a beaucoup d'exemples que vous pouvez créer comme ceci (par exemple, vous pouvez même instancier un Void objet de trop), mais ça ne veut pas dire que la Chaîne n'est pas "immuable".

Il y a des cas d'utilisation où ce type de code peut être utilisé à votre avantage et être "bon codage", comme l' effacement des mots de passe à partir de la mémoire le plus tôt possible (avant le GC).

Selon le directeur de la sécurité, vous ne pouvez pas être en mesure d'exécuter votre code.

30voto

Ankur Points 23539

Vous utilisez la réflexion pour accéder aux détails « mise en oeuvre » de l’objet string. Immuabilité est la fonctionnalité de l’interface publique d’un objet.

24voto

Modificateurs de visibilité et final (c'est à dire l'immuabilité) ne sont pas une mesure contre les codes malveillants en Java; ils sont simplement des outils pour se protéger contre les erreurs et pour rendre le code plus facile à maintenir (un des gros points de vente du système). C'est pourquoi vous pouvez accéder à la mise en œuvre interne des détails comme la sauvegarde de char tableau pour Strings grâce à la réflexion.

Le deuxième effet de vous voir, c'est que tous Strings changer bien qu'il semble que vous n'variation s1. C'est une certaine propriété de Java littéraux de Chaîne qu'ils sont automatiquement interné, c'est à dire mis en cache. Deux littéraux de Chaîne avec la même valeur sera effectivement le même objet. Lorsque vous créez une Chaîne avec des new il ne sera pas interné, automatiquement et vous ne verrez pas cet effet.

#substring jusqu'à récemment (Java 7u6) a travaillé d'une manière similaire, ce qui aurait expliqué le comportement de la version d'origine de votre question. Il n'a pas créer une nouvelle sauvegarde de char tableau mais réutilisés celui de la Chaîne d'origine; qu'il vient de créer un nouvel objet String qui ont utilisé un offset et une longueur à l'heure actuelle, seule une partie de ce tableau. Généralement, il a travaillé comme des Chaînes de caractères sont immuables, à moins de contourner cela. Cette propriété de #substring signifie également que l'ensemble de la Chaîne d'origine ne pouvait pas être nettoyée lorsqu'une plus courte chaîne créée à partir d'elle existait encore.

Comme actuelles de Java et de votre version actuelle de la question, il n'est pas étrange comportement de l' #substring.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X