java中的String对象是否可变
有个仁兄在 StackOverflow 上发起了一个问题,是这么问的:
众所周知Java中的String对象是不可变的,但我们来看下面这段代码:
String s1 = "Hello World"; String s2 = "Hello World"; String s3 = s1.substring(6); System.out.println(s1); // Hello World System.out.println(s2); // Hello World System.out.println(s3); // World Field field = String.class.getDeclaredField("value"); field.setAccessible(true); char[] value = (char[])field.get(s1); value[6] = 'J'; value[7] = 'a'; value[8] = 'v'; value[9] = 'a'; value[10] = '!'; System.out.println(s1); // Hello Java! System.out.println(s2); // Hello Java! System.out.println(s3); // World
为什么这段代码会是这样的运行结果?为什么s1和s2的值被改变了,但是s3的值却没有?
回答#1:
String对象是不可变的,但这仅意味着你无法通过调用它的公有方法来改变它的值。
上面的代码通过反射机制绕过了正常的API。采用这种方式,你还可以改变枚举的值,甚至可以改变Integer类型自动装箱时使用的查找表。
在这里,由于s1和s2指向同一个内部的字符串对象,因此他们的值都被改变了。正如其他回答所述,这是由编译器实现的。
s3没有被改变的原因确实令我诧异,我过去一直认为s3和s1共享同一个value数组(在Java 7u6之前的版本中的确是这样)。然而,通过查看String类的源码,我们可以看出子串对象的value数组是从原字符串对象中拷贝得到的(通过使用Arrays.copyOfRange(..)方法)。这是s3没有被改变的原因。
你可以安装一个SecurityManager来防止恶意代码执行这一类的操作。但需要注意的是,有些库的实现依赖于这种反射技巧(例如ORM工具,AOP库等)。
我在回复的开头写道String对象并不是真的不可变,只是“看上去不可变”。这可能会误导读者以为String类的当前版本在访问限制方面有所疏忽,但事实上value数组使用了private和final修饰符。因此开发者需要注意:在Java中无法将数组声明为不可变的,即使使用了正确的访问修饰符,也不能将它暴露在类的外面。
鉴于这个话题如此火热,向各位推荐一些进阶读物:2009年JavaZone会议上Heinz Kabutz发表的关于反射技术的疯狂演说,本文涵盖了反射操作中的常见问题,以及其他一些有关反射技术的内容。本文非常好,也非常疯狂。
本文揭示了为什么反射技术在某些场景下非常实用,但在大多数情况下,你应该避免使用它。
回答#2:
在Java中,如果两个String类型的变量被初始化为同一个字符串,那么这两个变量将被赋值以相同的对象引用。这就是表达式“Test1==Test2”返回值为true的原因。
String Test1="Hello World"; String Test2="Hello World"; System.out.println(test1==test2); // true
Test3是由substring()方法创建的一个新的String对象,它并没有和Test1共享同一个value数组。(注:由于原作者笔误,下图中变量test1和test3的首字母并未大写,望读者注意)
我们可以通过反射技术访问String对象,获取到value数组的指针:
Field field = String.class.getDeclaredField("value"); field.setAccessible(true);
改变这个value数组的值便能够改变所有持有该数组指针的String对象的值,因此Test1和Test2的值都发生了改变。但由于Test3是由substring()方法创建的一个新的String对象,它的值并没有被改变。

Outils d'IA chauds

Undresser.AI Undress
Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover
Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool
Images de déshabillage gratuites

Clothoff.io
Dissolvant de vêtements AI

AI Hentai Generator
Générez AI Hentai gratuitement.

Article chaud

Outils chauds

Bloc-notes++7.3.1
Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise
Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1
Puissant environnement de développement intégré PHP

Dreamweaver CS6
Outils de développement Web visuel

SublimeText3 version Mac
Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Guide du nombre parfait en Java. Nous discutons ici de la définition, comment vérifier le nombre parfait en Java ?, des exemples d'implémentation de code.

Guide du générateur de nombres aléatoires en Java. Nous discutons ici des fonctions en Java avec des exemples et de deux générateurs différents avec d'autres exemples.

Guide de Weka en Java. Nous discutons ici de l'introduction, de la façon d'utiliser Weka Java, du type de plate-forme et des avantages avec des exemples.

Guide du nombre de Smith en Java. Nous discutons ici de la définition, comment vérifier le numéro Smith en Java ? exemple avec implémentation de code.

Dans cet article, nous avons conservé les questions d'entretien Java Spring les plus posées avec leurs réponses détaillées. Pour que vous puissiez réussir l'interview.

Java 8 présente l'API Stream, fournissant un moyen puissant et expressif de traiter les collections de données. Cependant, une question courante lors de l'utilisation du flux est: comment se casser ou revenir d'une opération FOREAK? Les boucles traditionnelles permettent une interruption ou un retour précoce, mais la méthode Foreach de Stream ne prend pas directement en charge cette méthode. Cet article expliquera les raisons et explorera des méthodes alternatives pour la mise en œuvre de terminaison prématurée dans les systèmes de traitement de flux. Lire plus approfondie: Améliorations de l'API Java Stream Comprendre le flux Forach La méthode foreach est une opération terminale qui effectue une opération sur chaque élément du flux. Son intention de conception est

Guide de TimeStamp to Date en Java. Ici, nous discutons également de l'introduction et de la façon de convertir l'horodatage en date en Java avec des exemples.

Les capsules sont des figures géométriques tridimensionnelles, composées d'un cylindre et d'un hémisphère aux deux extrémités. Le volume de la capsule peut être calculé en ajoutant le volume du cylindre et le volume de l'hémisphère aux deux extrémités. Ce tutoriel discutera de la façon de calculer le volume d'une capsule donnée en Java en utilisant différentes méthodes. Formule de volume de capsule La formule du volume de la capsule est la suivante: Volume de capsule = volume cylindrique volume de deux hémisphères volume dans, R: Le rayon de l'hémisphère. H: La hauteur du cylindre (à l'exclusion de l'hémisphère). Exemple 1 entrer Rayon = 5 unités Hauteur = 10 unités Sortir Volume = 1570,8 unités cubes expliquer Calculer le volume à l'aide de la formule: Volume = π × r2 × h (4
