Initialisierung der Klasse
In der Initialisierungsphase führt die virtuelle Java -Maschine die Initialisierungsanweisung der Klasse aus und weist den statischen Variablen der Klasse den Anfangswert zu.
In einem Programm gibt es zwei Möglichkeiten, statische Variablen zu initialisieren:
1. Initialisieren Sie bei der Erklärung statischer Variablen;
2. Initialisieren Sie in einem statischen Codeblock.
Statische Variablen, die nicht explizit initialisiert wurden, haben ihren ursprünglichen Wert.
Ein seltsames Beispiel:
Paket com.mengdd.classloader; Class Singleton {// private statische Singleton minstance = new Singleton (); // Position 1 // Position 1 Ausgabe: // counter1: 1 // counter2: 0 public stati c int coptic; int counter 2 = 0; ) {return minstance; out.Es ist ersichtlich, dass die Anweisung, die das Objekt erzeugt, in zwei Positionen platziert ist und die Ausgabe unterschiedlich ist (die Ausgabe der entsprechenden Position wurde im Programmkommentar angegeben).
Dies liegt daran, dass die Initialisierungsanweisung in der Reihenfolge ausgeführt wird.
Deklarationsanweisungen von statischen Variablen und statischen Codeblöcken werden als Klasseninitialisierungsanweisungen angesehen.
Initialisierungsschritte der Klasse
1. Wenn diese Klasse nicht geladen und angeschlossen wurde, laden Sie zuerst und verbinden Sie sie.
2. Wenn die Klasse eine direkte übergeordnete Klasse hat und die übergeordnete Klasse noch nicht initialisiert wurde, wird die direkte übergeordnete Klasse zuerst initialisiert.
3. Wenn in der Klasse Initialisierungsanweisungen vorhanden sind, führen Sie diese Initialisierungsanweisungen nacheinander aus.
Initialisierungszeitpunkt des Unterrichts
Java -Programme können in zwei Möglichkeiten unterteilt werden, Klassen zu verwenden:
1. Aktive Verwendung
2. Passive Verwendung
Alle Implementierungen von Java Virtual Machine müssen jede Klasse oder Schnittstelle initialisieren, wenn sie zuerst von Java -Programmen aktiv verwendet werden.
Sechs Situationen der aktiven Verwendung:
1. Erstellen Sie eine Instanz der Klasse.
neuer Test ();
2. Zugriff auf eine statische Variable einer Klasse oder Schnittstelle oder weisen Sie der statischen Variablen einen Wert zu.
int b = test.a; test.a = b;
3.. Aufrufen der statischen Methode der Klasse
Test.dosomething ();
4. Reflexion
Class.forname ("com.mengdd.test");5. Initialisieren Sie eine Unterklasse einer Klasse
Klasse Eltern {} Klasse Child erweitert Eltern {public static int a = 3;} child.a = 4;6. Die als Startup -Klasse gekennzeichnete Klasse, wenn Java Virtual Machine gestartet wird
Java com.mengdd.test
Zusätzlich zu den oben genannten sechs Fällen werden andere Möglichkeiten zur Verwendung von Java -Klassen als passive Verwendung von Klassen angesehen und führen nicht zur Initialisierung von Klassen.
Die Spezialität der Schnittstelle
Wenn eine java -virtuelle Maschine eine Klasse initialisiert, müssen alle übergeordneten Klassen initialisiert werden, diese Regel gilt jedoch nicht für Schnittstellen.
Bei der Initialisierung einer Klasse wird die implementierende Schnittstelle nicht zuerst initialisiert.
Wenn eine Schnittstelle initialisiert wird, wird die übergeordnete Schnittstelle nicht zuerst initialisiert.
Daher wird eine übergeordnete Schnittstelle aufgrund ihrer untergeordneten Schnittstelle oder implementierenden Klasseninitialisierung nicht initialisiert.
Statische Variablen des endgültigen Typs
Unabhängig davon, ob eine statische Variable des endgültigen Typs konstant oder variabel kompiliert wird, wirkt sich die Ausführung des Initialisierungsanweisungsblocks aus.
Wenn der Wert einer statischen Variablen eine Kompilierungszeitkonstante ist, wird der Typ nicht initialisiert (der statische Block der Klasse wird nicht ausgeführt).
Wenn der Wert einer statischen Variablen eine nicht kompilierte Konstante ist, dh nur die Laufzeit hat einen bestimmten Initialisierungswert, wird dieser Typ initialisiert (der statische Block der Klasse wird ausgeführt).
Beispielcode:
Paket com.mengdd.classloader; import Java.util.Random; Der Typ muss nicht initialisiert werden. int x = new random (). NextInt (100); // Nur wenn Sie ausführen können, können Sie den Wert statisch {System.out.println ("statischer Block in FinalTest2") erhalten. // Die Klasse wird initialisiert, das heißt, Der statische Anweisungsblock wird mit Ausgabe}} öffentlicher Klasse intest {public static void main (String [] args) {System.out.println ("FinalTest1:" + FinalTest1.x); "FinalTest2:" + FinalTest2.x); Zuschreibungsklarheit der aktiven Verwendung
Eine statische Variable oder eine statische Methode, auf die ein Programm zugegriffen wird, kann nur als aktive Verwendung einer Klasse oder Schnittstelle betrachtet werden, wenn sie tatsächlich in der aktuellen Klasse oder Schnittstelle definiert ist.
Paket com.mengdd.classloader; Klasse übergeordnet {static int a = 3; ;}} Class Child erweitert übergeordnetes {static {System.out.println ("Child Static Block"); A: " + Child.A); Child.Dosomething (); // Der statische Codeblock der untergeordneten Klasse wird nicht ausgeführt, was bedeutet, dass die untergeordnete Klasse nicht initialisiert wird // Dies liegt daran, dass die Variablen und Methoden, die aktiv sind verwendet werden in der übergeordneten Klasse}} definiert Klassenloaderklasse
Das Aufrufen der LOADCLASS () -Methode der Klassenloader -Klasse zum Laden einer Klasse ist keine aktive Verwendung der Klasse und führt nicht dazu, dass die Klasse initialisiert wird.
Paket com.mengdd.classloader; Klasse cl {static {System.out.println ("statischer Block in CL"); GetSystemClassloader (); Initialisierte System.o ut .println ("----------------------"); .classloader.cl ");}}Elterndelegiertermechanismus des Klassenladers
Klassenlader
Der Klassenlader wird verwendet, um Klassen in Java -virtuelle Maschinen zu laden.
Arten des Klassenladers
Es gibt zwei Arten von Klassenladern:
1. Der Lader, der mit JVM ausgestattet ist:
Root Class Loader (Bootstrap)
Verlängerungsklassenlader (Erweiterung)
Systemklassenlader (System)
2. Benutzerdefinierter Klassenloader:
Eine Unterklasse von Java.lang.Classloader kann die Lademethode der Klasse anpassen.
Der Lader, der mit JVM geliefert wird
Die Java Virtual Machine ist mit den folgenden Ladern ausgestattet.
1. Bootstrap -Klasse Loader:
Dieser Loader hat keinen übergeordneten Loader.
Es ist verantwortlich für das Laden der Kernklassenbibliotheken virtueller Maschinen wie Java.lang.*Usw.
Der Stammklassenlader lädt die Klassenbibliothek aus dem von der System Eigenschaft Sun.boot.class.Path angegebenen Verzeichnis.
Die Implementierung des Root -Class -Laders hängt vom zugrunde liegenden Betriebssystem ab und ist Teil der Implementierung der virtuellen Maschine.
2. Verlängerungsklassenloa:
Der übergeordnete Loader ist der Root -Class -Loader.
Sie lädt die Klassenbibliothek aus dem von der Eigenschaft java.ext.dirs festgelegten Verzeichnis oder lädt die Klassenbibliothek von JRE/LIB/Ext -Subverzeichnis (EXT -Verzeichnis) des Installationsverzeichnisses des JDK. Der Benutzer wird in der JAR -Datei in diesem Verzeichnis platziert und wird auch automatisch vom Lader der Erweiterungsklasse geladen.
Der Verlängerungsklassenloader ist eine reine Java -Klasse und eine Unterklasse der Klasse java.lang.classloader.
3. Systemklassenlader:
Der übergeordnete Loader ist auch als Anwendungsklassenloader bekannt und ist ein Lader der Erweiterungsklasse.
Es lädt die Klasse aus dem Verzeichnis, das durch die Umgebungsvariablenklasse oder die Systemeigenschaft Java.Class.Path angegeben ist.
Der Systemklassenlader ist eine reine Java -Klasse und eine Unterklasse der Klasse von Java.lang.Classader.
Hinweis: Das Konzept des Elternladers bezieht sich hier nicht auf die Erbschaftsbeziehung der Klasse, und der untergeordnete Lader erbt nicht unbedingt den übergeordneten Loader (tatsächlich eine Kombinationsbeziehung).
Benutzerdefinierter Klassenlader
Zusätzlich zum Klassenlader, der mit den oben genannten virtuellen Maschinen geliefert wird, können Benutzer auch ihren eigenen Klassenlader (benutzerdefinierter Klassenlader) anpassen.
Java stellt die abstrakte Klasse Java.lang.Classloader bereit, und alle benutzerdefinierten Klassenlader sollten die Klasse der Klassenlader erben.
Elterndelegiertermechanismus für die Klassenbelastung
Ausgehend von JDK Version 1.2 übernimmt der Klassenbeladungsprozess den Vater -Delegationsmechanismus, der die Sicherheit der Java -Plattform besser gewährleisten kann.
Im übergeordneten Delegiertenmechanismus, mit Ausnahme des Laders der Stammklassen, der mit der virtuellen Java-Maschine geliefert wird, haben der Rest der Klassenlader und nur einen übergeordneten Lader, und jeder Lader bildet eine Baumstruktur gemäß der Eltern-Kind-Beziehung.
Wenn ein Java -Programm den Loader Loader1 zum Laden der Probenklasse anfordert, verpflichtet Loader1 zuerst seinen übergeordneten Loader zum Laden der Probenklasse. lader1 selbst.
Ein Beispiel für einen bestimmten Prozess:
Lader2 stellt zuerst fest, ob die Beispielklasse aus ihrem Namespace geladen wurde.
Wenn die Probenklasse noch nicht geladen wurde, fordert Loader2 zuerst Loader1 zum Laden an, ladern Sie sie erneut, laden Sie sie erneut, und fordert den Systemklassen -Loader erneut auf, den Systemklassenlader erneut zu laden, und fordert den Verlängerungsklassen -Loader erneut an, um ihn erneut zu laden, und fordert sie an und fordert sie erneut an, und fordert sie erneut an und fordert sie erneut an und fordert sie erneut an und fordert sie erneut auf. Die Verlängerungsklasse Loader fordert dann den Root -Class -Loader an, ihn erneut zu laden.
Wenn weder der Lader der Root -Klasse noch der Lader der Erweiterungsklasse geladen werden können, versucht der Lader der Systemklasse zu laden. Zurück zu Loader2 zurückgegeben und die Probenklasse erfolgreich in die virtuelle Maschine laden.
Wenn der Systemlader die Probenklasse nicht laden kann, versucht Loader1 die Probenklasse zu laden.
Wenn alle Elternlader und Loader2 selbst nicht laden können, wird eine Ausnahme von ClassNotFoundException ausgelöst.
Um zusammenzufassen:
Jeder Loader versucht vorzugsweise mit der übergeordneten Klasse. Unterklasse, damit sich die Unterklasse laden lässt. Wenn alles fehlschlägt, wird eine Ausnahme ausgelöst.
Definieren Sie Klassenlader und anfängliche Klassenlader
Wenn ein Klassenlader die Beispielklasse erfolgreich laden kann, wird dieser Klassenlader als Definitionsklassenlader bezeichnet.
Alle Klassenlader, die Referenzen auf Klassenobjekte erfolgreich zurückgeben können (einschließlich des Definitionsklassenladers, einschließlich des Definitionsklassenladers und alle Subloader darunter), werden als anfängliche Klassenlader bezeichnet.
Unter der Annahme, dass Loader1 die Beispielklasse tatsächlich lädt, ist Loader1 der Definitionsklassenlader der Stichprobenklasse, und Loader2 und Loader1 sind die anfänglichen Klassenlader der Stichprobenklasse.
Vater-Sohn-Beziehung
Es sollte darauf hingewiesen werden, dass sich die Eltern-Kind-Beziehung zwischen Ladern tatsächlich auf die Wrapper-Beziehung zwischen Laderobjekten und nicht auf die Vererbungsbeziehung zwischen Klassen bezieht.
Ein Eltern-Kind-Loaderpaar kann zwei Instanzen derselben Laderklasse sein oder nicht.
Ein übergeordnetes Loader -Objekt ist in das untergeordnete Laderobjekt eingewickelt.
Beispielsweise sind Loader1 und Loader2 beide Instanzen der MyClassloader -Klasse und Loader2 Wraps Loader1, dem übergeordneten Loader von Loader2.
Wenn eine benutzerdefinierte Klassenladerinstanz generiert wird, wird der Systemklassenlader zum übergeordneten Loader des Klassenladers.
Vorteile des übergeordneten Delegationsmechanismus
Der Vorteil des Delegationsmechanismus des Vaters besteht darin, dass er die Sicherheit des Softwaresystems verbessern kann.
Da es bei diesem Mechanismus für einen benutzerdefinierten Klassenlader zuverlässigen Klassen lädt, die vom übergeordneten Loader geladen werden sollten, ist es unmöglich, zuverlässigen Klassen zu laden, wodurch unzuverlässiger oder sogar böswilliger Code zu einem zuverlässigen Code ersetzt wird, der vom Elternlader geladen wird.
Beispielsweise wird die Klasse java.lang.object immer vom Root-Class-Loader geladen, und kein anderer benutzerdefinierter Klassenlader kann die Java.lang.Object-Klasse mit bösartigem Code laden.
Namespace
Jeder Klassenlader hat einen eigenen Namespace, der aus der Klasse besteht, die vom Lader und allen Elternladern geladen wird.
Im gleichen Namespace werden zwei Klassen mit demselben vollständigen Namen (einschließlich des Paketnamens der Klasse) nicht angezeigt.
In verschiedenen Namespaces ist es möglich, dass zwei Klassen mit demselben vollständigen Namen (einschließlich des Paketnamens der Klasse) erscheinen können.
Laufzeitpaket
Das Laufzeitpaket besteht aus Klassen, die zu demselben Paket gehören, das von demselben Klassenlader geladen wird.
Um festzustellen, ob zwei Klassen zum gleichen Laufzeitpaket gehören, hängt es nicht nur davon ab, ob ihre Paketnamen gleich sind, sondern auch, ob der Definitionsklassenloader gleich ist.
Nur Klassen, die zu demselben Laufzeitpaket gehören, können sich auf die sichtbaren (d. H. Die Standardzugriffsstufe) und die Klassenmitglieder zugreifen.
Solche Einschränkungen können verhindern, dass benutzerdefinierte Klassen die Kernklassenbibliotheksklassen imitieren und auf sichtbare Mitglieder des Kernklassenbibliothekspakets zugreifen.
Angenommen, der Benutzer definiert eine Klasse java.lang.spy und wird von einem benutzerdefinierten Klassenlader geladen. .lang.spy kann im Paket der Kernklassenbibliothek nicht auf die sichtbaren Mitglieder des Pakets zugreifen.