1. Wissenspunkte über Objekt- und Gedächtnissteuerung
1. Der Initialisierungsprozess von Java -Variablen, einschließlich lokaler Variablen, Mitgliedsvariablen (Instanzvariablen und Klassenvariablen).
2. In der Vererbungsbeziehung unterscheiden sich der Kompilierungs-Zeit-Typ und die Laufzeittyp vom Kompilierungs-Zeit-Typ der verwendeten Objektreferenzvariablen, es gibt einen Unterschied in den Eigenschaften und Methoden zum Zugriff auf das Objekt.
3. Merkmale der endgültigen Modifikatorin.
2. Die Aufteilung und Initialisierungsprozess von Java -Variablen
Variablen der Java -Programme können grob in Mitgliedsvariablen und lokale Variablen unterteilt werden. Mitgliedervariablen können in Instanzvariablen (nicht statische Variablen) und Klassenvariablen (statische Variablen) unterteilt werden. Im Allgemeinen werden in den folgenden Situationen die lokalen Variablen erscheinen, auf die wir stoßen:
(1) formaler Parameter: Lokale Variablen, die in der Methodensignatur definiert sind, werden vom Anrufer zugewiesen und verschwinden, wenn die Methode endet.
(2) Lokale Variablen innerhalb der Methode: Lokale Variablen, die in der Methode definiert sind, müssen in der Methode initialisiert werden (zuweisen einen Anfangswert) und verschwindet, wenn die variable Initialisierung beginnt und endet.
(3) Lokale Variablen im Codeblock: Lokale Variablen, die im Codeblock definiert wurden, müssen initialisiert werden (Zuwenden von Anfangswerten), die in Codeblöcken angezeigt werden müssen. Sie werden wirksam, wenn die Initialisierung abgeschlossen ist und stirbt, wenn der Codeblock endet.
Paket com.zlc.array; public class testfield {{string b; // Wenn nicht initialisiert, meldet der Compiler die lokale Variable B möglicherweise nicht initialisiert. } public static void main (String [] args) {int a; // Wenn nicht initialisiert, meldet der Compiler die lokale Variable A möglicherweise nicht initialisiertes System.out.println (a); }} Die mit statischen Modifikationen modifizierten Mitgliedsvariablen sind Klassenvariablen, die zur Klasse selbst gehören. Mitgliedervariablen, die nicht mit statischer Modifizierung modifiziert werden, sind Instanzvariablen. Instanzen, die zu dieser Klasse gehören, kann jede Klasse in derselben JVM nur einem Klassenobjekt entsprechen, aber jede Klasse kann mehrere Java -Objekte erstellen. (Das heißt, eine Klassenvariable erfordert nur ein Stück Speicherraum, und jedes Mal, wenn die Klasse eine Instanz schafft, muss sie der Instanzvariablen ein Stück Platz zuordnen.)
Initialisierungsprozess von Instanzvariablen: Aus Sicht der Syntax kann das Programm die Initialisierung von Instanzvariablen an drei Stellen durchführen:
(1) Geben Sie den Anfangswert an, wenn Sie eine Instanzvariable definieren.
(2) Geben Sie den Anfangswert für Instanzvariablen in nicht-statischen Blöcken an.
(3) Geben Sie den Anfangswert für Instanzvariablen im Konstruktor an.
Unter ihnen ist die Initialisierungszeit der beiden Methoden (1) und (2) früher als die von (3) im Konstruktor, und die beiden Initialisierungsordnungen (1) und (2) werden in der Reihenfolge bestimmt, in der sie im Quellcode angeordnet sind.
Paket com.zlc.array; public class testfield {public testfield (int age) {System.out.println ("Initialisieren Sie diese.age im Constructor ="+this.age); this.age = Alter; } {System.out.println ("in nicht statischen Blöcken initialisieren"); Alter = 22; } // initialisieren int alter = 15; public static void main (String [] args) {Testfield Field = New Testfield (24); System.out.println ("Final Age ="+field.age); }} Das Laufergebnis lautet: Initialisieren Sie dies.age = 15 im Initialisierungskonstruktor im nicht statischen Block
Endgültiges Alter = 24
Wenn Sie wissen, wie Sie Javap verwenden, können Sie Javap -c xxxx (Klassendatei) verwenden, um zu sehen, wie die Java -Klasse zusammengestellt wird.
Geben Sie beim Definieren einer Instanzvariablen den Anfangswert an. Im Initialisierungsblock ist der Status der Anweisung, die den Anfangswert für die Instanzvariable angibt, gleich. Nachdem der Compiler zusammengestellt und verarbeitet wurde, werden sie alle im Konstruktor erwähnt. Das oben genannte int-Alter = 15 wird in die folgenden zwei Schritte unterteilt, um auszuführen:
1) int Alter; Beim Erstellen eines Java -Objekts verteilt das System dem Objekt gemäß der Anweisung Speicher.
2) Alter = 15; Diese Aussage wird in den Konstruktor der Java -Klasse extrahiert und ausgeführt.
Initialisierungsprozess von Klassenvariablen: Aus Sicht der Syntax kann ein Programm Klassenvariablen von zwei Stellen initialisieren und zuweisen.
(1) Geben Sie den Anfangswert an, wenn Sie eine Klassenvariable definieren.
(2) Geben Sie den Anfangswert für Klassenvariablen in einem statischen Block an.
Die beiden Ausführungsaufträge entsprechen ihrer Anordnung im Quellcode. Lassen Sie uns ein kleines abnormales Beispiel geben:
Paket com.zlc.array; Klasse teststatic {// Klassenmitglied Demo Teststatic Instance endgültige statische teststatische Demo = new Teststatic (15); // Klassenmitglied statisches int Alter = 20; // Instanzvariable Curage Int Curage; public testStatic (int Jahre) {// Todo automatisch generierter Konstruktorstub Curage = Alter - Jahre; }} public class test {public static void main (String [] args) {System.out.println (teststatic.demo.curage); Teststatic staticDemo = neuer Teststatic (15); System.out.println (staticDemo.curage); }} Das Ausgabeergebnis wird in zwei Zeilen gedruckt. Einer besteht darin, die Instanzvariable der Teststatic Class -Attribut -Demo zu drucken, und die zweite besteht darin, das Instanzattribut von Teststatic über das staticDemo des Java -Objekts auszugeben. Gemäß dem Initialisierungsprozess der Instanzvariablen und der oben analysierten Klassenvariablen können wir sie schließen:
1) In der ersten Stufe der Initialisierung geben beim Laden der Klasse Speicherplatz für die Klassenvariablen Demo und Alter zu. Zu diesem Zeitpunkt sind die Standardwerte von Demo und Alter null bzw. 0.
2) In der zweiten Stufe der Initialisierung weist das Programm Demo und Alter in Sequenz Anfangwerte zu. Teststatic (15) muss den Konstruktor von Teststatic aufrufen. Zu diesem Zeitpunkt ist Alter = 0, so dass das Druckergebnis -15. Wenn staticDemo initialisiert wird, wurde das Alter 20 zugewiesen, sodass das Ausgangsergebnis 5 ist.
3. Der Unterschied zwischen den Erbungsvariablen und den Erben von Mitgliedsmethoden in Vererbungsbeziehungen
Beim Erstellen eines Java-Objekts ruft das Programm immer den Konstruktor für nicht statische Block- und Elternklassen der übergeordneten Klasse zuerst auf und ruft schließlich den nicht statischen Block und den Konstruktor dieser Klasse auf. Das Aufrufen des Konstruktors der übergeordneten Klasse über den Konstruktor der Unterklasse ist im Allgemeinen in zwei Situationen unterteilt: Der eine ist ein impliziter Anruf, und der andere ist eine Superanzeige, um den Konstruktor der übergeordneten Klasse zu nennen.
Die Methode für untergeordnete Klassen kann die Instanzvariable der übergeordneten Klasse aufrufen. Dies liegt daran, dass die untergeordnete Klasse die übergeordnete Klasse erbt und die Mitgliedsvariablen und Methoden der übergeordneten Klasse erhält. Die übergeordnete Klassenmethode kann jedoch nicht auf die Instanzvariable der untergeordneten Klasse zugreifen, da die übergeordnete Klasse nicht weiß, welche Klasse sie erbt und welche Art von Mitgliedsvariablen ihre Unterklasse hinzufügen wird. In einigen extremen Beispielen kann die übergeordnete Klasse natürlich die Variable der untergeordneten Klasse weiterhin aufrufen. Zum Beispiel: Die untergeordnete Klasse schreibt die übergeordnete Klassenmethode um und druckt im Allgemeinen den Standardwert, da die Instanzvariable der untergeordneten Klasse zu diesem Zeitpunkt nicht initialisiert wurde.
Paket com.zlc.array; Klasse Vater {int age = 50; public father () {// todo automatisch generatter Konstruktor stub system.out.println (this.getClass ()); //this.sonMethod (); info () kann nicht anrufen; } public void info () {System.out.println (Alter); }} public Class Son erweitert Vater {int age = 24; öffentlicher Sohn (int Alter) {// Todo automatisch generierter Konstruktor Stub this.age = Alter; } @Override public void info () {// Todo automatisch erzeugte Methode Stub System.err.println (Alter); } public static void main (String [] args) {neuer Sohn (28); } // subklassenspezifische Methode public void Sonmethod () {System.out.println ("Son-Methode"); }} Nach unserer normalen Inferenz wird der Konstruktor der Elternklassen implizit durch die Unterklasse aufgerufen, und die Info () -Methode wird im übergeordneten Klassenkonstruktor aufgerufen (Hinweis: Ich habe nicht gesagt, dass die übergeordnete Klasse aufgerufen wird). Theoretisch gibt es die Altersinstanzvariable der übergeordneten Klasse aus. Das Druckergebnis wird voraussichtlich 50 betragen, aber das tatsächliche Ausgangsergebnis beträgt 0. Analyse des Grundes:
1) Die Speicherzuweisung von Java -Objekten ist im Konstruktor nicht abgeschlossen. Der Konstruktor vervollständigt nur den Initialisierungszuweisungsprozess. Das heißt, vor dem Aufrufen des Konstruktors der übergeordneten Klasse hat JVM den Speicherraum für das SON -Objekt klassifiziert. Dieser Raum speichert zwei Altersattribute, eines ist das Alter der Unterklasse und das andere ist das Alter der Elternklasse.
2) Wenn man einen neuen Sohn nennt (28), repräsentiert der Strom dieses Objekts ein Objekt, das ein Subklassensohn ist. Wir können das Object.getClass () drucken und das Ergebnis von Class Com.zlc.array.son erhalten. Der aktuelle Initialisierungsprozess wird jedoch im Konstruktor der übergeordneten Klasse durchgeführt und kann nicht durch diese aufgerufen werden.
3) Wenn sich der Kompilierungs-Zeit-Typ der Variablen vom Laufzeittyp unterscheidet, wird der Wert der Instanzvariablen beim Zugriff auf die Instanzvariable ihres Referenzobjekts durch die Variable unterscheidet. Wenn jedoch die Instanzmethode des Objekts durch die Variable verweist, wird das Verhalten der Methode durch das Objekt bestimmt, das es tatsächlich bezieht. Daher wird hier die Info -Methode der Unterklasse aufgerufen, sodass das Alter der Unterklasse gedruckt wird. Da das Alter noch nicht dringend initialisiert wurde, beträgt der Standardwert 0.
Wenn der deklarierte Typ in Laien den realen neuen Typ nicht übereinstimmt, ist das verwendete Attribut die übergeordnete Klasse und die genannte Methode die untergeordnete Klasse.
Durch Javap -c können wir direkter verstehen, warum es einen großen Unterschied zwischen den Erbattributen und Methoden gibt. Wenn wir die Info -Rewrite -Methode des Subklassensohns im obigen Beispiel entfernen, wird die Info -Methode der übergeordneten Klasse zu diesem Zeitpunkt aufgerufen, da beim Kompilieren die Info -Methode der übergeordneten Klasse in die Unterklasse übertragen wird und die Variable der Reputation -Mitgliedsklasse in der übergeordneten Klasse gelassen und nicht übertragen wird. Auf diese Weise haben die Unterklasse und die übergeordnete Klasse Instanzvariablen mit demselben Namen. Wenn die Unterklasse die Methode der übergeordneten Klasse mit demselben Namen umschreibt, überschreibt die Unterklasse -Methode die Methode der Elternklasse vollständig (wie Java so gestaltet ist, dass ich nicht sehr klar bin). Variablen mit demselben Namen können existieren und nicht gleichzeitig überschreiben. Unterklassen von Methoden mit demselben Namen überschreiben die gleichnamige Methode der Elternklasse vollständig.
Im Allgemeinen hängt für eine Referenzvariable beim Zugriff auf eine Instanzvariable des Objekts durch die Variable der Wert der Instanzvariablen vom Typ ab, wenn die Variable deklariert wird. Wenn Sie auf die Methode des Objekts zugreifen, bezieht sich das Methodenverhalten von der Art des Objekts, das sie tatsächlich referenziert.
Schließlich werde ich es mit einem kleinen Fall überprüfen:
Paket com.zlc.array; Class Animal {int ay; public Animal () {} public Animal (int Alter) {// Todo automatisch generierter Konstruktor Stub This.age = Age; } void run () {System.out.println ("Animal Run"+Age); }} Class Dog erweitert das Tier {int age; Zeichenfolge Name; öffentlicher Hund (int Alter, String-Name) {// Todo automatisch generierter Konstruktor Stub this.age = Age; this.name = name; } @Override void run () {System.out.println ("Hundelauf"+AGE); }} public class testextends {public static void main (String [] args) {Animal Animal = New Animal (5); System.out.println (Animal.age); Animal.run (); Hund Hund = neuer Hund (1, "Xiaobai"); System.out.println (hunde.age); Dog.run (); Animal Animal2 = neuer Hund (11, "Wangcai"); System.out.println (Animal2.age); Animal2.run (); Tiertiere3; Animal3 = Hund; System.out.println (Animal3.age); Animal3.run (); }} Wenn Sie die übergeordnete Klassenmethode aufrufen möchten: Sie können sie über Super aufrufen, aber das Super -Keyword bezieht sich nicht auf ein Objekt und kann nicht als reale Referenzvariable verwendet werden. Interessierte Freunde können es selbst studieren.
Die obigen sind beispielsweise Variablen und Methoden. Klassenvariablen und Klassenmethoden sind viel einfacher, also direkte Klassennamen. Die Methoden sind viel bequemer und Sie werden nicht so viel Ärger begegnen.
4. Verwendung von endgültigen Modifikatoren (insbesondere Makroersatz)
(1) Inal kann Variablen ändern. Nachdem die durch den endgültigen geänderte Variable ein Anfangswert zugewiesen wurde, kann sie nicht erneut zugewiesen werden.
(2) Inal kann die Methode ändern und die endgültige modifizierte Methode kann nicht umgeschrieben werden.
(3) Inal kann Klassen ändern, und Klassen, die durch die endgültige Änderung geändert werden, können keine Unterklassen abgeben.
Der angegebene Anfangswert, den die durch den endgültigen modifizierte Variable angezeigt hat, muss angezeigt werden:
Zum Beispiel, die endgültig modifiziert sind, kann der Anfangswert nur an den folgenden drei angegebenen Positionen zugewiesen werden.
(1) Geben Sie den Anfangswert an, wenn Sie die endgültige Instanzvariable definieren.
(2) Geben Sie den Anfangswert für die endgültige Instanzvariable in einem nicht statischen Block an.
(3) Geben Sie den Anfangswert für die endgültige Instanzvariable im Konstruktor an.
Sie werden schließlich im Konstruktor zur Initialisierung erwähnt.
Für Klassenvariablen, die mit endgültig angegeben sind: Die Anfangswerte können nur an zwei angegebenen Stellen zugewiesen werden.
(1) Geben Sie den Anfangswert an, wenn Sie die endgültige Klassenvariable definieren.
(2) Geben Sie den Anfangswert für die endgültige Klassenvariable in einem statischen Block an.
Im Gegensatz zu Instanzvariablen werden auch Klassenvariablen vom Compiler verarbeitet, um die Anfangswerte in statischen Blöcken zuzuweisen, während Instanzvariablen den Konstruktoren erwähnt werden.
Es gibt ein weiteres Merkmal von Klassenvariablen, die durch endgültig geändert wurden, nämlich "Makroersatz". Wenn die modifizierte Klassenvariable den Anfangswert bei der Definition der Variablen erfüllt, kann der Anfangswert während der Kompilierung bestimmt werden (z. B. 18, "AAAA", 16.78 und andere direkte Größen), dann ist die durch das Finale modifizierte Klassenvariable keine Variable, und das System wird als "Makrovariable" (wie wir oft als Konstante) behandelt werden. Wenn der Anfangswert während der Kompilierung ermittelt werden kann, wird er im statischen Block für die Initialisierung nicht erwähnt, und der Anfangswert wird direkt durch die endgültige Variable in der Klassendefinition ersetzt. Lassen Sie uns ein Beispiel für das Alter minus Jahr geben:
Paket com.zlc.array; Klasse teststatic {// Klassenmitglied Demo Teststatic Instance endgültige statische teststatische Demo = new Teststatic (15); // Klassenmitglied Alter endgültig statisches int Alter = 20; // Instanzvariable Curage Int Curage; public testStatic (int Jahre) {// Todo automatisch generierter Konstruktorstub Curage = Alter - Jahre; }} public class test {public static void main (String [] args) {System.out.println (teststatic.demo.curage); Teststatic static1 = neuer Teststatic (15); System.out.println (static1.curage); }} Zu diesem Zeitpunkt wird das Alter nach endgültig geändert. Bei der Zusammenstellung werden alle Altersgruppen in der Elternklasse 20, nicht eine Variable, damit das Ausgabeergebnis unsere Erwartungen entsprechen kann.
Besonders beim Vergleich von Saiten kann es mehr angezeigt werden
Paket com.zlc.array; public class testString {static String static_name1 = "java"; statische String static_name2 = "me"; statische Zeichenfolge statische StatCi_Name3 = STATIC_NAME1+STATIC_NAME2; endgültige statische String final_static_name1 = "java"; endgültige statische String final_static_name2 = "me"; // Finale hinzufügen oder nicht, kann es durch ein Makro vorne ersetzt werden. Final String Final_Statci_Name3 = Final_static_Name1+Final_static_name2; public static void main (String [] args) {String name1 = "java"; String name2 = "me"; String name3 = name1+name2; // (1) system.out.println (name3 == "javame"); // (2) system.out.println (teststring.statci_name3 == "Javame"); // (3) system.out.println (teststring.final_statci_name3 == "Javame"); }} Es gibt nichts zu sagen über die Verwendung der endgültigen Modifikationsmethoden und -klassen, nur dass einer nicht durch Unterklassen (wie privat) umgeschrieben werden kann und der andere keine Unterklassen ableiten kann.
Bei der Änderung lokaler Variablen mit endgültigem Java verlangt Java, dass auf lokale Variablen, auf die interne Klassen zugegriffen werden, mit dem endgültigen geändert werden. Es gibt einen Grund. Für gewöhnliche lokale Variablen bleibt ihr Umfang innerhalb der Methode. Wenn die Methode endet, verschwindet die lokale Variable, aber die interne Klasse kann einen impliziten "Verschluss" erzeugen, wodurch die lokale Variable von der Methode getrennt bleibt, in der sie sich befindet.
Manchmal ist ein Thread in einer Methode neu und dann wird die lokale Variable der Methode aufgerufen. Zu diesem Zeitpunkt muss die Änderungsvariable als endgültig modifiziert deklariert werden.
5. Berechnungsmethode des Objektbelegungsgedächtnisses
Verwenden Sie die Methoden Freememory (), TotalMemory () und MaxMemory () in den Klassen von Java.lang.Runtime, um die Größe eines Java -Objekts zu messen. Diese Methode wird normalerweise verwendet, wenn viele Ressourcen genau bestimmt werden müssen. Diese Methode ist fast nutzlos, um den Produktionssystem -Cache zu implementieren. Der Vorteil dieser Methode besteht darin, dass der Datentyp unabhängig von der Größe ist und verschiedene Betriebssysteme den besetzten Speicher erhalten können.
Es verwendet die Reflexions -API, um die Hierarchie von Elementvariablen eines Objekts zu durchqueren und die Größe aller ursprünglichen Variablen zu berechnen. Dieser Ansatz erfordert nicht so viele Ressourcen und kann für zwischengespeicherte Implementierungen verwendet werden. Der Nachteil ist, dass die ursprüngliche Typengröße unterschiedlich ist und unterschiedliche JVM -Implementierungen unterschiedliche Berechnungsmethoden haben.
Nach JDK5.0 liefert die Instrumentierungs -API die GetObjectSize -Methode, um die vom Objekt besetzte Speichergröße zu berechnen.
Standardmäßig wird die Größe des referenzierten Objekts nicht berechnet. Um das referenzierte Objekt zu berechnen, können Sie Reflection verwenden, um es zu erhalten. Die folgende Methode ist eine Implementierung im obigen Artikel, in dem die Größe des Referenzobjekts berechnet wird:
öffentliche Klasse sizeofagent {statische Instrumentierungsinst; / ** initialisiert Agent*/ public static void Premain (String agentargs, Instrumentation Instp) {inst = instp; } /*** Gibt die Objektgröße ohne Mitgliedsobjekte zurück. * @param o Objekt, um die Größe von * @return Objektgröße zu erhalten } return Inst.GetObjectSize (o); } /** * berechnet die volle Größe des Objekts über * seine Hierarchie -Grafik. * @param Objekt zur Berechnung der Größe von * @return Objektgröße */ public static long fullSizeof (Object obj) {map <Objekt, Objekt> besucht = new identityHashMap <Objekt, Objekt> (); Stack <Object> Stack = New Stack <Object> (); langes Ergebnis = InternaSizeof (OBJ, Stack, besucht); while (! stack.isempty ()) {result += InternalSizeof (stack.pop (), stapel, besucht); } besucht.clear (); Rückgabeergebnis; } private statische boolean SkipObject (Objekt obj, map <Objekt, Objekt> besucht) {if (obj instanceof string) {// Überspringen Sie die interned String überspringen if (obj == ((String) obj) .intern ()) {return true; }} return (obj == null) // besuchte Objekt überspringen || besucht.containsKey (OBJ); } private statische lange interne InternationSizeof (Objekt OBJ, Stack <Object> Stack, Karte <Objekt, Objekt> besucht) {if (SkipObject (obj, besucht)) {return 0; } besucht.put (obj, null); langes Ergebnis = 0; // Größe von Objekt + primitive Variablen + Elementpunkte Ergebnis + = sizeofagent.sizeof (OBJ); // Verarbeiten Sie alle Array -Elemente -Klasse clazz = obj.getClass (); if (clazz.isarray ()) {if (clazz.getName (). Länge ()! für (int i = 0; i <länge; i ++) {stack.add (array.get (obj, i)); }} Rückgabeergebnis; } // Alle Felder des Objekts verarbeiten (clazz! = null) {field [] fields = clazz.getDeclaredfields (); für (int i = 0; i <fields.length; i ++) {if (! modifier.isstatic (fields [i] .getModifiers ())) {if (fields [i] .getype (). // primitive Felder überspringen} else {fields [i] .setAccessible (true); Versuchen Sie, geschätzt zu werden. if (ObjectToadd! = null) {stack.add (ObjectToadd); }} catch (illegalAccessException ex) {assert false; }}}}} clazz = clazz.getSuperClass (); } Rückgabeergebnis; }}