Unterschiede im JAVA-Stack
Autor:Eve Cole
Aktualisierungszeit:2009-11-30 17:08:19
-
1. Stack und Heap sind Orte, die Java zum Speichern von Daten im Ram verwendet. Im Gegensatz zu C++ verwaltet Java den Stapel und den Heap automatisch, und Programmierer können den Stapel oder den Heap nicht direkt festlegen.
2. Der Vorteil des Stapels besteht darin, dass die Zugriffsgeschwindigkeit schneller ist als die des Heaps, gleich nach den Registern, die sich direkt in der CPU befinden. Der Nachteil besteht jedoch darin, dass die Größe und Lebensdauer der im Stapel gespeicherten Daten bestimmt werden muss und es an Flexibilität mangelt. Darüber hinaus können Stack-Daten geteilt werden, siehe Punkt 3 für Details. Der Vorteil des Heaps besteht darin, dass er die Speichergröße dynamisch zuweisen kann und die Lebensdauer dem Compiler nicht im Voraus mitgeteilt werden muss. Der Garbage Collector von Java sammelt automatisch die nicht mehr verwendeten Daten. Der Nachteil besteht jedoch darin, dass die Zugriffsgeschwindigkeit langsam ist, da zur Laufzeit Speicher dynamisch zugewiesen werden muss.
3. In Java gibt es zwei Arten von Datentypen.
Einer sind primitive Typen, es gibt insgesamt 8 Typen, nämlich int, short, long, byte, float, double, boolean, char (beachten Sie, dass es keinen grundlegenden String-Typ gibt). Diese Art der Definition ist in der Form int a = 3; long b = 255L definiert und wird als automatische Variable bezeichnet. Es ist zu beachten, dass automatische Variablen Literalwerte und keine Instanzen von Klassen speichern, das heißt, sie sind keine Referenzen auf Klassen. Hier gibt es keine Klasse. Beispiel: int a = 3; wobei a eine Referenz ist, die auf den Typ int zeigt und auf den Literalwert 3 zeigt. Die Daten dieser Literalwerte sind in Größe und Lebensdauer bekannt (diese Literalwerte sind in einem bestimmten Programmblock fest definiert und die Feldwerte verschwinden aus Geschwindigkeitsgründen nach dem Beenden des Programmblocks). existieren auf dem Stapel.
Darüber hinaus verfügt der Stapel über eine sehr wichtige Besonderheit: Die im Stapel gespeicherten Daten können gemeinsam genutzt werden. Angenommen, wir definieren auch:
int a = 3;
int b = 3;
Der Compiler verarbeitet zunächst int a = 3; zunächst erstellt er einen Verweis auf die Variable a auf dem Stapel und sucht dann nach einer Adresse mit dem Literalwert 3. Wenn er diese nicht findet, öffnet er eine Adresse zum Speichern des Literals Wert von 3, und dann zeigt a auf die Adresse von 3. Als nächstes wird int b = 3 verarbeitet. Nachdem die Referenzvariable von b erstellt wurde, wird b direkt auf die Adresse 3 verwiesen, da auf dem Stapel bereits ein Literalwert von 3 vorhanden ist. Auf diese Weise gibt es eine Situation, in der a und b gleichzeitig auf 3 zeigen.
Es ist wichtig zu beachten, dass sich die Referenz dieses Literalwerts von der Referenz des Klassenobjekts unterscheidet. Gehen Sie davon aus, dass die Referenzen zweier Klassenobjekte gleichzeitig auf dasselbe Objekt verweisen. Wenn eine Objektreferenzvariable den internen Zustand des Objekts ändert, spiegelt die andere Objektreferenzvariable die Änderung sofort wider. Im Gegensatz dazu führt die Änderung des Werts einer Literalreferenz nicht dazu, dass sich auch der Wert einer anderen Referenz auf das Literal ändert. Wie im obigen Beispiel, nachdem wir die Werte von a und b definiert haben, setzen wir dann a=4, dann ist b nicht gleich 4, sondern immer noch gleich 3. Wenn der Compiler auf a = 4; trifft, sucht er erneut, ob sich auf dem Stapel ein Literalwert von 4 befindet. Andernfalls wird eine Adresse erneut geöffnet, um den Wert von 4 zu speichern , es wird direkt auf diese Adresse verweisen. Daher haben Änderungen im Wert von a keinen Einfluss auf den Wert von b.
Das andere sind Wrapper-Klassendaten wie Integer, String, Double und andere Klassen, die die entsprechenden Basisdatentypen umschließen. Alle diese Datentypen sind im Heap vorhanden. Java teilt dem Compiler mithilfe der new()-Anweisung explizit mit, dass sie zur Laufzeit dynamisch erstellt werden. Dies ist zwar flexibler, hat jedoch den Nachteil, dass es mehr Zeit in Anspruch nimmt. 4. String ist ein spezieller Wrapper-Datentyp. Das heißt, es kann in der Form String str = new String( "abc" ); oder in der Form String str = "abc" ; erstellt werden (zum Vergleich: Vor JDK 5.0 war dies noch nie der Fall). gesehen Integer i = 3; Ausdruck, da Klassen und Literalwerte nicht austauschbar sind, außer für String. In JDK 5.0 ist dieser Ausdruck möglich, da der Compiler die Konvertierung von Integer i = new Integer(3) durchführt Hintergrund). Ersteres ist ein standardisierter Klassenerstellungsprozess, das heißt, in Java ist alles ein Objekt und Objekte sind Instanzen von Klassen, die alle in der Form von new () erstellt werden. Einige Klassen in Java, wie z. B. die DateFormat-Klasse, können eine neu erstellte Klasse über die getInstance()-Methode der Klasse zurückgeben, was gegen dieses Prinzip zu verstoßen scheint. Nicht wirklich. Diese Klasse verwendet das Singleton-Muster, um eine Instanz der Klasse zurückzugeben, aber diese Instanz wird innerhalb der Klasse durch new() erstellt und getInstance() verbirgt dieses Detail von außen. Warum wird die Instanz dann in String str = „abc“ nicht durch new () erstellt? Verstößt dies gegen das obige Prinzip? Eigentlich nein.
5. Über das Innenleben von String str = „abc“. Java wandelt diese Anweisung intern in die folgenden Schritte um:
(1) Definieren Sie zunächst eine Objektreferenzvariable mit dem Namen str für die String-Klasse: String str;
(2) Durchsuchen Sie den Stapel, um festzustellen, ob eine Adresse mit dem Wert „abc“ vorhanden ist. Wenn nicht, öffnen Sie eine Adresse mit dem Literalwert „abc“, erstellen Sie ein neues Objekt o der String-Klasse und fügen Sie das hinzu Zeichen von o Der String-Wert zeigt auf diese Adresse und das referenzierte Objekt o wird neben dieser Adresse auf dem Stapel aufgezeichnet. Existiert bereits eine Adresse mit dem Wert „abc“, wird das Objekt o gefunden und die Adresse von o zurückgegeben.
(3) Zeigen Sie str auf die Adresse des Objekts o.
Es ist zu beachten, dass die String-Werte im Allgemeinen direkt in der String-Klasse gespeichert werden. Aber wie String str = "abc"; speichert der String-Wert in diesem Fall einen Verweis auf die auf dem Stack gespeicherten Daten!
Um dieses Problem besser zu veranschaulichen, können wir es mithilfe der folgenden Codes überprüfen.
String str1 = "abc" ;
String str2 = "abc" ;
System.out.println(str1==str2); //true
Beachten Sie, dass wir hier nicht str1.equals(str2); verwenden, da hiermit verglichen wird, ob die Werte der beiden Zeichenfolgen gleich sind. == sign gibt gemäß JDK-Anweisungen nur dann true zurück, wenn beide Referenzen auf dasselbe Objekt verweisen. Was wir hier sehen wollen, ist, ob str1 und str2 beide auf dasselbe Objekt zeigen.
Die Ergebnisse zeigen, dass die JVM zwei Referenzen str1 und str2 erstellt hat, aber nur ein Objekt erstellt hat und beide Referenzen auf dieses Objekt verwiesen haben.
Gehen wir noch einen Schritt weiter und ändern den obigen Code wie folgt:
String str1 = "abc" ;
String str2 = "abc" ;
str1 = "bcd" ;
System.out.println(str1 + "," + str2); //bcd, abc
System.out.println(str1==str2); //false
Dies bedeutet, dass die Änderung der Zuweisung eine Änderung der Klassenobjektreferenz zur Folge hat und str1 auf ein anderes neues Objekt zeigt! Und str2 zeigt immer noch auf das ursprüngliche Objekt. Als wir im obigen Beispiel den Wert von str1 in „bcd“ änderten, stellte die JVM fest, dass es keine Adresse zum Speichern dieses Werts auf dem Stapel gab, also öffnete sie diese Adresse und erstellte ein neues Objekt, dessen String-Wert auf diese Adresse zeigt .
Tatsächlich ist die String-Klasse so konzipiert, dass sie unveränderlich ist. Wenn Sie seinen Wert ändern möchten, können Sie dies tun, aber die JVM erstellt zur Laufzeit stillschweigend ein neues Objekt basierend auf dem neuen Wert und gibt dann die Adresse dieses Objekts an einen Verweis auf die ursprüngliche Klasse zurück. Obwohl dieser Erstellungsprozess vollständig automatisiert ist, nimmt er doch mehr Zeit in Anspruch. In einer Umgebung, die sensibel auf Zeitanforderungen reagiert, kann dies bestimmte nachteilige Auswirkungen haben.
Ändern Sie den ursprünglichen Code erneut:
String str1 = "abc" ;
String str2 = "abc" ;
str1 = "bcd" ;
Zeichenfolge str3 = str1;
System.out.println(str3); //bcd
String str4 = "bcd" ;
System.out.println(str1 == str4); //true
Der Verweis auf das Objekt str3 verweist direkt auf das Objekt, auf das str1 zeigt (beachten Sie, dass str3 kein neues Objekt erstellt). Nachdem str1 seinen Wert geändert hat, erstellen Sie eine String-Referenz str4 und zeigen Sie auf das neue Objekt, das erstellt wurde, weil str1 den Wert geändert hat. Es kann festgestellt werden, dass str4 dieses Mal kein neues Objekt erstellt hat, wodurch die gemeinsame Nutzung von Daten im Stapel erneut realisiert wird.
Schauen wir uns den folgenden Code noch einmal an.
String str1 = new String( "abc" );
String str2 = "abc" ;
System.out.println(str1==str2); //false
Es werden zwei Referenzen erstellt. Es werden zwei Objekte erstellt. Die beiden Referenzen verweisen jeweils auf zwei unterschiedliche Objekte.
String str1 = "abc" ;
String str2 = new String( "abc" );
System.out.println(str1==str2); //false
Es werden zwei Referenzen erstellt. Es werden zwei Objekte erstellt. Die beiden Referenzen verweisen jeweils auf zwei unterschiedliche Objekte.
Die beiden oben genannten Codeteile veranschaulichen, dass ein neues Objekt, solange new () zum Erstellen verwendet wird, im Heap erstellt und sein Zeichenfolgenwert separat gespeichert wird. Auch wenn er mit den Daten im Stapel übereinstimmt , es wird nicht mit den Daten im Stapel geteilt.
6. Der Wert der Datentyp-Wrapper-Klasse kann nicht geändert werden. Nicht nur der Wert der String-Klasse kann nicht geändert werden, auch alle Datentyp-Wrapper-Klassen können ihre internen Werte nicht ändern. 7. Fazit und Vorschläge:
(1) Wenn wir eine Klasse mit einem Format wie String str = „abc“; definieren, gehen wir immer davon aus, dass wir ein Objekt str der String-Klasse erstellen. Sorge vor Fallen! Das Objekt wurde möglicherweise nicht erstellt! Sicher ist nur, dass eine Referenz auf die String-Klasse erstellt wird. Ob diese Referenz auf ein neues Objekt verweist, muss je nach Kontext berücksichtigt werden, es sei denn, Sie erstellen explizit ein neues Objekt über die Methode new (). Eine genauere Aussage besteht daher darin, dass wir eine Referenzvariable str erstellen, die auf ein Objekt der String-Klasse zeigt. Diese Objektreferenzvariable zeigt auf eine String-Klasse mit dem Wert „abc“. Ein klares Verständnis hiervon ist sehr hilfreich, um schwer zu findende Fehler im Programm zu beseitigen.
(2) Die Verwendung von String str = „abc“; kann die Ausführungsgeschwindigkeit des Programms bis zu einem gewissen Grad verbessern, da die JVM anhand der tatsächlichen Situation der Daten im Stapel automatisch bestimmt, ob ein neues Objekt erstellt werden muss . Was den Code von String str = new String("abc"); betrifft, werden immer neue Objekte im Heap erstellt, unabhängig davon, ob ihre String-Werte gleich sind oder ob neue Objekte erstellt werden müssen, wodurch die Belastung erhöht wird auf dem Programm. Diese Idee sollte die Idee des Flyweight-Modus sein, es ist jedoch nicht bekannt, ob die interne Implementierung von JDK diesen Modus anwendet.
(3) Wenn Sie vergleichen, ob die Werte in der Verpackungsklasse gleich sind, verwenden Sie die Methode equal(). Wenn Sie testen, ob die Referenzen zweier Verpackungsklassen auf dasselbe Objekt verweisen, verwenden Sie ==.
(4) Aufgrund der Unveränderlichkeit der String-Klasse sollten Sie die Verwendung der StringBuffer-Klasse in Betracht ziehen, wenn die String-Variable ihren Wert häufig ändern muss, um die Programmeffizienz zu verbessern.