Während der Wochentage, an denen ich etwas Zeit hatte, war ich mit der logischen Umsetzung des Projekts beschäftigt, also holte ich die dicke englische Version von Thinking In Java aus dem Bücherregal und las über das Spleißen von String-Objekten. Erstellen Sie eine Übersetzung unter Bezugnahme auf dieses Buch, fügen Sie Ihre eigenen Gedanken hinzu und schreiben Sie diesen Artikel, um ihn festzuhalten.
Unveränderliches String-Objekt
In Java sind String-Objekte unveränderlich. Im Code können Sie mehrere Aliase für ein String-Objekt erstellen. Aber diese Aliase beziehen sich alle auf dasselbe.
Beispielsweise sind s1 und s2 beide Aliase des Objekts „droidyue.com“, und die Aliase speichern Verweise auf die realen Objekte. Also s1 = s2
Kopieren Sie den Codecode wie folgt:
String s1 = "droidyue.com";
Zeichenfolge s2 = s1;
System.out.println("s1 und s2 haben die gleiche Referenz =" + (s1 == s2));
Der einzige überladene Operator in Java
In Java bezieht sich der einzige überladene Operator auf die Zeichenfolgenverkettung. +,+=. Darüber hinaus erlauben Java-Designer keine Überladung anderer Operatoren.
Spleißanalyse
Gibt es wirklich Leistungskosten?
Nachdem Sie die beiden oben genannten Punkte verstanden haben, haben Sie möglicherweise diesen Gedanken: Da Sting-Objekte unveränderlich sind, führt das Zusammenfügen mehrerer (drei oder mehr) Strings zwangsläufig zu redundanten String-Zwischenobjekten.
Kopieren Sie den Codecode wie folgt:
String userName = „Andy“;
String-Alter = „24“;
String job = "Developer";
String info = Benutzername + Alter + Job;
Um die oben genannten Informationen zu erhalten, werden Benutzername und Alter zusammengefügt, um ein temporäres String-Objekt t1 zu generieren, dessen Inhalt Andy24 ist, und dann werden t1 und job zusammengefügt, um das endgültige Informationsobjekt zu generieren, das wir benötigen Zwischenprodukt t1 wird generiert und t1 wird erstellt. Wenn danach kein aktives Recycling erfolgt, wird es zwangsläufig eine gewisse Menge an Platz beanspruchen. Wenn es sich um ein Zusammenfügen vieler Strings handelt (vorausgesetzt es sind Hunderte, meist bei Aufrufen von toString von Objekten), sind die Kosten noch höher und die Leistung wird stark reduziert.
Compiler-Optimierungsverarbeitung
Gibt es oben wirklich Leistungseinbußen? Gibt es keine spezielle Verarbeitungsoptimierung für die so häufig verwendete Zeichenfolge? Die Antwort lautet: Ja. Diese Optimierung wird durchgeführt, wenn der Compiler .java in Bytecode kompiliert.
Wenn ein Java-Programm ausgeführt werden soll, muss es zwei Zeiträume durchlaufen: Kompilierungszeit und Laufzeit. Während der Kompilierung wandelt der Java-Compiler (Compiler) die Java-Datei in Bytecode um. Zur Laufzeit führt die Java Virtual Machine (JVM) den zur Kompilierzeit generierten Bytecode aus. Durch diese beiden Perioden erreichte Java die sogenannte Kompilierung an einem Ort und konnte überall ausgeführt werden.
Experimentieren wir mit den Optimierungen, die während der Kompilierung vorgenommen wurden, und wir können einen Codeabschnitt erstellen, der möglicherweise eine Leistungseinbuße mit sich bringt.
Kopieren Sie den Codecode wie folgt:
öffentliche Klassenverkettung {
public static void main(String[] args) {
String userName = „Andy“;
String-Alter = „24“;
String job = "Developer";
String info = Benutzername + Alter + Job;
System.out.println(info);
}
}
Kompilieren Sie Concatenation.java. getConcatenation.class
Kopieren Sie den Codecode wie folgt:
javacConcatenation.java
Dann verwenden wir Javap, um die kompilierte Datei Concatenation.class zu dekompilieren. javap -c Verkettung. Wenn der Befehl javap nicht gefunden wird, sollten Sie erwägen, das Verzeichnis, in dem sich javap befindet, zur Umgebungsvariablen hinzuzufügen oder den vollständigen Pfad zu javap zu verwenden.
Kopieren Sie den Codecode wie folgt:
17:22:04-androidyue~/workspace_adt/strings/src$ javap -c Verkettung
Zusammengestellt aus „Concatenation.java“
öffentliche Klassenverkettung {
öffentliche Verkettung();
Code:
0: aload_0
1: invokespecial #1 // Methode java/lang/Object."<init>":()V
4: Rückkehr
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String Andy
2: astore_1
3: ldc #3 // String 24
5: astore_2
6: ldc #4 // String Developer
8: astore_3
9: neue #5 // Klasse java/lang/StringBuilder
12: dup
13: invokespecial #6 // Methode java/lang/StringBuilder."<init>":()V
16: aload_1
17: invokevirtual #7 // Methode java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
20: aload_2
21: invokevirtual #7 // Methode java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24: aload_3
25: invokevirtual #7 // Methode java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28: invokevirtual #8 // Methode java/lang/StringBuilder.toString:()Ljava/lang/String;
31: astore 4
33: getstatic #9 // Feld java/lang/System.out:Ljava/io/PrintStream;
36: Ladung 4
38: invokevirtual #10 // Methode java/io/PrintStream.println:(Ljava/lang/String;)V
41: Rückkehr
}
Darunter sind ldc, astore usw. Java-Bytecode-Anweisungen, ähnlich wie Assembleranweisungen. Die folgenden Kommentare verwenden zur Erläuterung Java-bezogene Inhalte. Wir können sehen, dass es oben viele StringBuilder gibt, aber wir rufen sie nicht explizit im Java-Code auf. Dies ist die Optimierung, die der Java-Compiler auf String-Splicing stößt, und Folgendes Beim Spleißen wird tatsächlich die Append-Methode des StringBuilder-Objekts aufgerufen. Auf diese Weise wird es keine Probleme geben, über die wir uns oben Sorgen gemacht haben.
Compiler-Optimierung allein?
Da der Compiler die Optimierung für uns durchgeführt hat, reicht es aus, sich nur auf die Optimierung des Compilers zu verlassen? Natürlich nicht.
Unten sehen wir uns einen nicht optimierten Code mit geringerer Leistung an
Kopieren Sie den Codecode wie folgt:
public void implicitUseStringBuilder(String[] Values) {
String result = "";
for (int i = 0; i < Werte.Länge; i ++) {
Ergebnis += Werte[i];
}
System.out.println(result);
}
Verwenden Sie Javac zum Kompilieren und Javap zum Anzeigen
Kopieren Sie den Codecode wie folgt:
public void implicitUseStringBuilder(java.lang.String[]);
Code:
0: ldc #11 // String
2: astore_2
3: iconst_0
4: istore_3
5: iload_3
6: aload_1
7:Array-Länge
8: if_icmpge 38
11: neue #5 // Klasse java/lang/StringBuilder
14: dup
15: invokespecial #6 // Methode java/lang/StringBuilder."<init>":()V
18: aload_2
19: invokevirtual #7 // Methode java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: aload_1
23: iload_3
24: aaload
25: invokevirtual #7 // Methode java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28: invokevirtual #8 // Methode java/lang/StringBuilder.toString:()Ljava/lang/String;
31: astore_2
32: iinc 3, 1
35: Gehe zu 5
38: getstatic #9 // Feld java/lang/System.out:Ljava/io/PrintStream;
41: aload_2
42: invokevirtual #10 // Methode java/io/PrintStream.println:(Ljava/lang/String;)V
45: Rückkehr
Darunter bilden 8: if_icmpge 38 und 35: goto 5 eine Schleife. 8: if_icmpge 38 bedeutet, dass, wenn der ganzzahlige Vergleich des JVM-Operandenstapels größer oder gleich ist (das entgegengesetzte Ergebnis von i <values.length), zu Zeile 38 (System.out) springen. 35: Gehe zu 5 bedeutet, direkt zu Zeile 5 zu springen.
Aber eine sehr wichtige Sache hier ist, dass die Erstellung von StringBuilder-Objekten zwischen Schleifen erfolgt, was bedeutet, wie viele StringBuilder-Objekte in ebenso vielen Schleifen erstellt werden, was offensichtlich nicht gut ist. Nackter Low-Level-Code.
Eine kleine Optimierung kann Ihre Leistung sofort verbessern.
Kopieren Sie den Codecode wie folgt:
public void explizitUseStringBuider(String[]-Werte) {
StringBuilder-Ergebnis = new StringBuilder();
for (int i = 0; i < Werte.Länge; i ++) {
result.append(values[i]);
}
}
Entsprechende zusammengestellte Informationen
Kopieren Sie den Codecode wie folgt:
public void explizitUseStringBuider(java.lang.String[]);
Code:
0: neu #5 // Klasse java/lang/StringBuilder
3: dup
4: invokespecial #6 // Methode java/lang/StringBuilder."<init>":()V
7: astore_2
8: iconst_0
9: istore_3
10: iload_3
11: aload_1
12:Arraylänge
13: if_icmpge 30
16: aload_2
17: aload_1
18: iload_3
19: aaload
20: invokevirtual #7 // Methode java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
23: Pop
24: iinc 3, 1
27: Gehe zu 10
30: Rückkehr
Aus dem Obigen ist ersichtlich, dass 13: if_icmpge 30 und 27: goto 10 eine Schleife bilden und 0: new #5 außerhalb der Schleife liegt, sodass StringBuilder nicht mehrmals erstellt wird.
Im Allgemeinen müssen wir versuchen, die implizite oder explizite Erstellung von StringBuilder im Schleifenkörper zu vermeiden. Daher können diejenigen, die verstehen, wie der Code kompiliert und intern ausgeführt wird, Code auf höherer Ebene schreiben.
Sollten im obigen Artikel Fehler enthalten sein, kritisieren und korrigieren Sie diese bitte.