Tutoriel Java BasicIntroduction à la colonne Insight String String
Recommandé (gratuit) : Tutoriel de base Java
Principe d'implémentation
en Java6 et In Dans la version précédente, l'objet String était un objet qui encapsulait le tableau de caractères. Il comportait principalement quatre variables membres : tableau de caractères, décalage, nombre de caractères et hachage de la valeur de hachage.
De Java7 à Java8, il n'y a plus de variables de décalage et de comptage dans la classe String. L’avantage est que l’objet String occupe un peu moins de mémoire.
À partir de la version Java 9, le champ char[] a été remplacé par le champ byte[] et un nouveau codeur d'attribut a été maintenu, qui est un identifiant du format d'encodage.
Un caractère char occupe 16 bits et 2 octets. Dans ce cas, stocker des caractères dans un codage sur un seul octet (caractères occupant un octet) est un gaspillage considérable. Afin d'économiser de l'espace mémoire, la classe String du JDK1.9 utilise un tableau d'octets de 8 bits et 1 octet pour stocker les chaînes.
La fonction du nouveau codeur d'attribut est que lors du calcul de la longueur de la chaîne ou de l'utilisation de la fonction indexOf(), nous devons déterminer comment calculer la longueur de la chaîne en fonction de ce champ. L'attribut coder a deux valeurs par défaut : 0 et 1. 0 représente Latin-1 (codage sur un seul octet) et 1 représente UTF-16. Si String détermine que la chaîne contient uniquement Latin-1, la valeur de l'attribut du codeur est 0, sinon elle est 1.
Immuable
En regardant le code de la classe String, vous pouvez constater que la classe String est modifiée par le mot-clé final, donc ceci La classe ne peut pas être héritée et le tableau de caractères variables dans la classe String est également modifié par final, donc l'objet String ne peut pas être modifié.
L'immuabilité des objets String présente principalement les avantages suivants :
Tout d'abord, assurer la sécurité des objets String. En supposant que l'objet String est mutable, l'objet String peut être modifié de manière malveillante.
Deuxièmement, cela garantit que la valeur de l'attribut de hachage ne changera pas fréquemment, garantissant ainsi l'unicité, afin que des conteneurs comme HashMap puissent implémenter la fonction de cache clé-valeur correspondante.
Troisièmement, vous pouvez implémenter un pool de constantes de chaîne.
En Java, il existe généralement deux façons de créer des objets chaîne :
La première consiste à le créer via une constante chaîne, telle que String str = "abc"
.
La seconde consiste à créer des variables de chaîne sous la forme de nouvelles, telles que String str = new String("abc")
.
Lorsque la première méthode est utilisée pour créer un objet chaîne dans le code, lors de la compilation du fichier de classe, la chaîne constante "abc" sera placée dans la structure constante, "abc" lorsque la classe est chargée. sera créé dans le pool constant ; str fera alors référence à un objet chaîne dans le pool constant. Cette méthode peut réduire la création répétée d'objets chaîne avec la même valeur et économiser de la mémoire.
String str = new String("abc")
De cette façon, d'abord lors de la compilation du fichier de classe, la chaîne constante "abc" sera placée dans la structure constante. Lorsque la classe sera chargée, "abc" sera créé dans le pool de constantes. ; Deuxièmement, lors de l'appel de new, la commande JVM appellera le constructeur de String, et le tableau char dans l'objet String fera référence au tableau char de la chaîne "abc" dans le pool de constantes , qui est créé dans le tas de mémoire Un objet String ; enfin, str fera référence à l'objet String, et la référence à l'objet String est différente de la référence à la chaîne "abc" dans le pool de constantes.
, la variable str pointe vers l'adresse de stockage de l'objet String, ce qui signifie que str n'est pas un objet, mais juste une référence d'objet. String str = new String("abc")
Concaténation de chaînes
Ajout constant
String str = "ab" + "cd" + "ef";Voir la compilation bytecode
0 ldc #2 <abcdef>2 astore_13 returnpeut être constaté que le compilateur optimise le code comme suit
String str= "abcdef";
Ajout de variable
String a = "ab";String b = "cd";String c = a + b;En regardant le bytecode compilé
0 ldc #2 <ab> 2 astore_1 3 ldc #3 <cd> 5 astore_2 6 new #4 <java/lang/StringBuilder> 9 dup10 invokespecial #5 <java/lang/StringBuilder.<init>>13 aload_114 invokevirtual #6 <java/lang/StringBuilder.append>17 aload_218 invokevirtual #6 <java/lang/StringBuilder.append>21 invokevirtual #7 <java/lang/StringBuilder.toString>24 astore_325 returnvous pouvez constater que lorsque Java ajoute des chaînes, la couche inférieure utilise StringBuilder et le code est optimisé comme suit :
String c = new StringBuilder().append("ab").append("cd").toString();
String. stagiaire
String a = new String("abc").intern();String b = new String("abc").intern();System.out.print(a == b);Résultat de sortie :
trueDans string
constant, l'objet sera placé par défaut dans le pool de constantes. Par exemple : String a = "123"
在字符串变量中,对象是会创建在堆内存中,同时也会在常量池中创建一个字符串对象,String 对象中的 char 数组将会引用常量池中的 char 数组,并返回堆内存对象引用。例如:String b = new String("abc")
如果调用 intern 方法,会去查看字符串常量池中是否有等于该对象的字符串的引用,如果没有,在 JDK1.6 版本中会复制堆中的字符串到常量池中,并返回该字符串引用,堆内存中原有的字符串由于没有引用指向它,将会通过垃圾回收器回收。
在 JDK1.7 版本以后,由于常量池已经合并到了堆中,所以不会再复制具体字符串了,只是会把首次遇到的字符串的引用添加到常量池中;如果有,就返回常量池中的字符串引用。
下面开始分析上面的代码块:
在一开始字符串”abc”会在加载类时,在常量池中创建一个字符串对象。
创建 a 变量时,调用 new Sting() 会在堆内存中创建一个 String 对象,String 对象中的 char 数组将会引用常量池中字符串。在调用 intern 方法之后,会去常量池中查找是否有等于该字符串对象的引用,有就返回常量池中的字符串引用。
创建 b 变量时,调用 new Sting() 会在堆内存中创建一个 String 对象,String 对象中的 char 数组将会引用常量池中字符串。在调用 intern 方法之后,会去常量池中查找是否有等于该字符串对象的引用,有就返回常量池中的字符串引用。
而在堆内存中的两个String对象,由于没有引用指向它,将会被垃圾回收。所以 a 和 b 引用的是同一个对象。
如果在运行时,创建字符串对象,将会直接在堆内存中创建,不会在常量池中创建。所以动态创建的字符串对象,调用 intern 方法,在 JDK1.6 版本中会去常量池中创建运行时常量以及返回字符串引用,在 JDK1.7 版本之后,会将堆中的字符串常量的引用放入到常量池中,当其它堆中的字符串对象通过 intern 方法获取字符串对象引用时,则会去常量池中判断是否有相同值的字符串的引用,此时有,则返回该常量池中字符串引用,跟之前的字符串指向同一地址的字符串对象。
以一张图来总结 String 字符串的创建分配内存地址情况:
使用 intern 方法需要注意的一点是,一定要结合实际场景。因为常量池的实现是类似于一个 HashTable 的实现方式,HashTable 存储的数据越大,遍历的时间复杂度就会增加。如果数据过大,会增加整个字符串常量池的负担。
判断字符串是否相等
// 运行环境 JDK1.8String str1 = "abc";String str2 = new String("abc");String str3= str2.intern();System.out.println(str1==str2); // falseSystem.out.println(str2==str3); // falseSystem.out.println(str1==str3); // true
// 运行环境 JDK1.8String s1 = new String("1") + new String("1");s1.intern();String s2 = "11";System.out.println(s1 == s2); // true , 如果不执行1.intern(),则返回false
String s1 = new String("1") + new String("1")
会在堆中组合一个新的字符串对象"11"
,在s1.intern()
之后,由于常量池中没有该字符串的引用,所以常量池中生成一个堆中字符串"11"
的引用,此时String s2 = "11"
返回的是堆字符串"11"
的引用,所以s1==s2
。
在JDK1.7版本以及之后的版本运行以下代码,你会发现结果为true,在JDK1.6版本运行的结果却为false:
String s1 = new String("1") + new String("1");System.out.println( s1.intern()==s1);
StringBuilder与StringBuffer
由于String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,这样不仅效率低下,而且大量浪费有限的内存空间。
和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的对象。
StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)。
由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!