Java-Basis-TutorialSpalte stellt Einblicke in String-Strings vor
Empfohlen (kostenlos): Java-Basis-Tutorial
Implementierung
In Java6 und vor In In dieser Version ist das String-Objekt ein Objekt, das das char-Array kapselt. Es verfügt hauptsächlich über vier Mitgliedsvariablen: char-Array, Offset-Offset, Zeichenanzahl und Hash-Wert-Hash.
Von Java7 bis Java8 gibt es in der String-Klasse keine Offset- und Zählvariablen mehr. Dies hat den Vorteil, dass das String-Objekt etwas weniger Speicher beansprucht.
Ab der Java 9-Version wurde das Feld char[] in das Feld byte[] geändert und ein neuer Attributcoder wurde beibehalten, der eine Kennung des Codierungsformats darstellt.
Ein Zeichen belegt 16 Bit und 2 Byte. In diesem Fall ist das Speichern von Zeichen in einer Einzelbyte-Kodierung (Zeichen, die ein Byte belegen) sehr verschwenderisch. Um Speicherplatz zu sparen, verwendet die String-Klasse von JDK1.9 ein 8-Bit-1-Byte-Byte-Array zum Speichern von Zeichenfolgen.
Die Funktion des neuen Attributcodierers besteht darin, dass wir bei der Berechnung der Zeichenfolgenlänge oder bei Verwendung der Funktion indexOf() bestimmen müssen, wie die Zeichenfolgenlänge basierend auf diesem Feld berechnet wird. Das Coder-Attribut hat standardmäßig zwei Werte: 0 und 1. 0 steht für Latin-1 (Einzelbyte-Kodierung) und 1 für UTF-16. Wenn String feststellt, dass die Zeichenfolge nur Latin-1 enthält, ist der Coder-Attributwert 0, andernfalls ist er 1.
Unveränderlich
Wenn Sie sich den Code der String-Klasse ansehen, können Sie feststellen, dass die String-Klasse durch das letzte Schlüsselwort geändert wird, sodass diese Klasse nicht vererbt werden kann, ebenso wie das variable char-Array in der String-Klasse Durch final geändert, daher das String-Objekt Kann nicht geändert werden.
Die Unveränderlichkeit von String-Objekten hat hauptsächlich die folgenden Vorteile:
Stellen Sie zunächst die Sicherheit von String-Objekten sicher. Unter der Annahme, dass das String-Objekt veränderbar ist, kann das String-Objekt in böswilliger Absicht geändert werden.
Zweitens stellt es sicher, dass sich der Hash-Attributwert nicht häufig ändert, wodurch Einzigartigkeit gewährleistet wird, sodass HashMap-ähnliche Container die entsprechende Schlüsselwert-Caching-Funktion implementieren können.
Drittens kann ein String-Konstantenpool implementiert werden.
In Java gibt es normalerweise zwei Möglichkeiten, String-Objekte zu erstellen:
Die erste besteht darin, sie über eine String-Konstante zu erstellen, wie z. B. String str = "abc"
. String str = "abc"
。
第二种是字符串变量通过new 形式的创建,如 String str = new String("abc")
。
当代码中使用第一种方式创建字符串对象时,在编译类文件时,”abc”常量字符串将会放入到常量结构中,在类加载时,“abc”将会在常量池中创建;然后,str将引用常量池中的字符串对象。这种方式可以减少同一个值的字符串对象的重复创建,节约内存。
String str = new String("abc")
这种方式,首先在编译类文件时,”abc”常量字符串将会放入到常量结构中,在类加载时,“abc”将会在常量池中创建;其次,在调用new时,JVM 命令将会调用 String 的构造函数,String 对象中的 char 数组将会引用常量池中”abc”字符串的char 数组,在堆内存中创建一个 String 对象;最后,str 将引用 String 对象,String对象的引用跟常量池中”abc”字符串的引用是不一样的。
对象与引用:对象的内容存储在内存中,操作系统通过内存地址来找到存储的内容,引用就是指内存的地址。
比如:String str = new String("abc")
,变量str指向的是String对象的存储地址,也就是说 str 并不是对象,而只是一个对象引用。
字符串拼接
常量相加
String str = "ab" + "cd" + "ef";
查看编译后的字节码
0 ldc #2 <abcdef>2 astore_13 return
可以发现编译器将代码优化成如下所示
String str= "abcdef";
变量相加
String a = "ab";String b = "cd";String c = a + b;
查看编译后的字节码
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 return
可以发现,Java在进行字符串相加的时候,底层使用的是StringBuilder,代码被优化成如下所示:
String c = new StringBuilder().append("ab").append("cd").toString();
String.intern
String a = new String("abc").intern();String b = new String("abc").intern();System.out.print(a == b);
输出结果:
true
在字符串常量中,默认会将对象放入常量池。例如:String a = "123"
String str = new String("abc")
. 🎜🎜Wenn die erste Methode zum Erstellen eines Zeichenfolgenobjekts im Code verwendet wird, wird beim Kompilieren der Klassendatei die Konstantenzeichenfolge „abc“ in die Konstantenstruktur eingefügt. Wenn die Klasse geladen wird, befindet sich „abc“. Konstantenpool wird erstellt in; str verweist dann auf ein String-Objekt im Konstantenpool. Diese Methode kann die wiederholte Erstellung von Zeichenfolgenobjekten mit demselben Wert reduzieren und Speicherplatz sparen. 🎜🎜String str = new String("abc")
Auf diese Weise wird beim Kompilieren der Klassendatei zunächst die konstante Zeichenfolge „abc“ in die Konstantenstruktur eingefügt. „abc“ wird im Konstantenpool erstellt. Zweitens ruft der JVM-Befehl beim Aufruf von new den Konstruktor von String auf, und das char-Array im String-Objekt verweist auf das char-Array der Zeichenfolge „abc“ im Konstantenpool 🎜 Erstellen Sie schließlich ein String-Objekt im Heap-Speicher. str verweist auf das String-Objekt, und die Referenz des String-Objekts unterscheidet sich von der Referenz der Zeichenfolge „abc“ im Konstantenpool. 🎜🎜Objekte und Referenzen: Der Inhalt von Objekten wird im Speicher gespeichert. Das Betriebssystem findet den gespeicherten Inhalt über die Speicheradresse. Die Referenz bezieht sich auf die Speicheradresse. 🎜🎜Zum Beispiel: String str = new String("abc")
, die Variable str zeigt auf die Speicheradresse des String-Objekts, was bedeutet, dass str kein Objekt, sondern nur eine Objektreferenz ist . 🎜🎜🎜🎜🎜String-Verkettung🎜🎜🎜🎜🎜🎜Konstante Addition🎜🎜// 运行环境 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🎜Sehen Sie sich den kompilierten Bytecode an🎜
// 运行环境 JDK1.8String s1 = new String("1") + new String("1");s1.intern();String s2 = "11";System.out.println(s1 == s2); // true , 如果不执行1.intern(),则返回false🎜Sie können feststellen, dass der Compiler den Code wie folgt optimiert: 🎜
String s1 = new String("1") + new String("1");System.out.println( s1.intern()==s1);🎜 🎜🎜🎜Variable Phase 🎜🎜rrreee hinzufügen 🎜 um den kompilierten Bytecode anzuzeigen 🎜rrreee🎜 Wenn Java Zeichenfolgen hinzufügt, verwendet die unterste Ebene StringBuilder und der Code wird wie folgt optimiert: 🎜rrreee🎜🎜rrreee🎜Ausgabeergebnis:🎜 rrreee🎜In string🎜constant🎜 wird das Objekt standardmäßig in den Konstantenpool eingefügt. Zum Beispiel:
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 类。
Das obige ist der detaillierte Inhalt vonInsight String-Zeichenfolge. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!