Elterndelegationsmodell
Das Konzept der Klassenbelastung sollte als Innovation in der Java -Sprache angesehen werden. Ziel ist es, den Klassenladeprozess von der virtuellen Maschine zu entkoppeln und den Zweck zu erreichen, "einen binären Byte -Stream zu erhalten, der diese Klasse über den voll qualifizierten Namen der Klasse beschreibt". Das Codemodul, das diese Funktion implementiert, ist der Klassenlader. Das Grundmodell des Klassenladers ist das berühmte Elterndelegationsmodell. Es klingt großartig, aber die Logik ist eigentlich sehr einfach. Wenn wir eine Klasse laden müssen, stellen wir zunächst fest, ob die Klasse geladen wurde. Wenn nicht, stellen wir fest, ob es vom Elternlader geladen wurde. Wenn wir unsere eigene FindClass -Methode nicht angerufen haben, um zu laden. Dies ist das Basismodell (Verstöße gegen Diebstahlsbild löschen):
Es ist auch sehr einfach zu implementieren. Der Schlüssel ist die Lastklasse -Methode der Klasse -Lader -Klasse. Der Quellcode lautet wie folgt:
Protected Class <?> LoadClass (String -Name, boolean Resolve) löst classNotFoundException {synchronized (GetClassLoadingLock (Name)) {// Erstens prüfen, ob die Klasse bereits geladen wurde. if (c == null) {long t0 = system.nanotime (); try {if (parent! = null) {c = parent.loadClass (Name, false); } else {c = findbootstrapCassOrnull (name); }} catch (classNotFoundException e) {// classNotFoundException, wenn die Klasse nicht gefunden wird // aus dem Nicht-Null-Elternklassen-Loader} if (c == null) {// Wenn immer noch nicht gefunden wird, rufen Sie die Fundklasse in der Reihenfolge // auf, um die Klasse zu finden. long t1 = system.nanotime (); C = FindClass (Name); // Dies ist der definierende Klassenlader; Notieren Sie die Statistiken Sun.misc.PerfCounter.getParentDelegationTime (). sun.misc.perfcounter.getFindClasstime (). AddelapsedTimeFrom (T1); sun.misc.perfcounter.getfindclasses (). Increment (); }} if (Resolve) {class (c); } return c; }}Plötzlich fühlte ich mich geärgert, warum habe ich die Ausnahme nur direkt ausgelegt? Tatsächlich liegt es daran, dass die Klasse der Klassenlader eine abstrakte Klasse ist. Tatsächlich wird es bei Verwendung eine Unterklasse schreiben. Diese Methode wird nach Bedarf umgeschrieben, um den vom Unternehmen erforderlichen Ladevorgang abzuschließen.
Benutzerdefinierter Klassenloader
Bei der Anpassung der Unterklasse von Classloader gibt es zwei gängige Methoden: Eine besteht darin, die Lastklasse -Methode neu zu schreiben, und der andere ist die Umschreiben der FindClass -Methode. Tatsächlich sind diese beiden Methoden im Wesentlichen gleich. Schließlich ruft LoadClass auch FindClass auf, aber logischerweise ist es am besten, die interne Logik der Lastklasse nicht direkt zu ändern.
Ich persönlich denke, dass der bessere Weg darin besteht, die Lademethode von benutzerdefinierten Klassen in FindClass nur neu zu schreiben.
Warum ist das besser? Weil ich auch früher gesagt habe, dass die Lastklasse-Methode der Ort ist, um die Logik des übergeordneten Modells zu implementieren. Das Ändern dieser Methode ohne Autorisierung führt dazu, dass das Modell zerstört wird und leicht Probleme verursacht. Daher ist es am besten, kleine Änderungen im Rahmen des übergeordneten Delegationsmodells vorzunehmen, um die ursprüngliche stabile Struktur nicht zu zerstören. Gleichzeitig vermeidet es auch die Notwendigkeit, doppelte Code zu schreiben, die von den Eltern delegiert wurden, um die Lastklasse -Methode neu zu schreiben. Aus Sicht der Wiederverwendbarkeit von Code ist es immer eine bessere Wahl, diese Methode direkt zu ändern.
Natürlich wäre es anders, wenn Sie das Modell der Elternkommission absichtlich zerstören.
Zerstöre das übergeordnete Delegationsmodell
Warum das übergeordnete Delegationsmodell zerstören?
In einigen Fällen müssen wir möglicherweise zwei verschiedene Klassen laden, aber leider sind die Namen dieser beiden Klassen genau gleich. Zu diesem Zeitpunkt kann das übergeordnete Delegationsmodell unsere Anforderungen nicht erfüllen. Wir müssen die Lastklassenmethode umschreiben, um das übergeordnete Delegationsmodell zu zerstören und denselben Klassennamen mehrmals zu laden. Natürlich ist die hier erwähnte Zerstörung nur die Zerstörung im lokalen Sinne.
Aber die Klassennamen sind gleich. Wie kann JVM diese beiden Klassen unterscheiden? Dies wird natürlich keinen Zusammenbruch der Weltanschauung verursachen. Tatsächlich sind Klassen nicht nur durch Klassennamen in JVM begrenzt, sondern gehören auch zum Klassenloader, der sie lädt. Klassen, die von verschiedenen Klassenladern geladen werden, wirken sich tatsächlich nicht aus.
Machen Sie ein Experiment.
Schreiben wir zuerst zwei Klassen:
paket com.mythsman.test; public class Hallo {public void say () {System.out.println ("Dies ist von Hello v1"); }} paket com.mythsman.test; public class Hallo {public void say () {System.out.println ("Dies ist von Hello v2"); }} Die beiden Klassennamen sind gleich. Der einzige Unterschied besteht darin, dass die Implementierung der Methoden unterschiedlich ist. Wir kompilieren zuerst separat und benennen dann die generierten Klassendateien in Hello.class.1 und Hello.class.2 um.
Unser Ziel ist es, Instanzen dieser beiden Klassen in der Testklasse zu erstellen.
Anschließend erstellen wir eine neue Testklasse com.mythsman.test.main und erstellen zwei benutzerdefinierte Klassenloader in der Hauptfunktion:
Classloader classLoader1 = new classloader () {@Override public class <?> LoadClass (String s) löst ClassNotFoundException {try {if (s.Equals ("com.mythsman.test.hello")) {byte [] classBytes = Dateien.ReadallBytes (")). return defincass (s, classBytes, 0, classBytes.length); } else {return Super.loadClass (s); }} catch (ioException e) {neue classNotFoundException (s) werfen; }}}; Classloader classLoader2 = new classloader () {@Override public class <?> LoadClass (String s) löscht classNotFoundException {try {if (s.equals ("com.mythsman.test.hello") {byte [] classBytes = Dateien.readallBytes (paths.get ("/home/mythen/desktop/test/hello.class.2")); return defincass (s, classBytes, 0, classBytes.length); } else {return Super.loadClass (s); }} catch (ioException e) {neue classNotFoundException (s) werfen; }}};
Der Zweck dieser beiden Klassenlader ist es, zwei verschiedene Bytecodes der Hello -Klasse separat zu verbinden. Wir müssen die Bytecode -Datei lesen und sie durch die definitive Methode in die Klasse laden. Beachten Sie, dass wir die Lastklassenmethode überladen. Wenn wir die FindClass -Methode überlasten, wird aufgrund des übergeordneten Delegierten -Verarbeitungsmechanismus der Lastklasse -Methode die FindClass -Methode des zweiten Klassenloaders nicht aufgerufen.
Wie generieren wir Instanzen? Offensichtlich können wir uns nicht direkt auf Klassennamen (Name Konflikte) beziehen, sodass wir nur Reflexion verwenden können:
Object hellov1 = classloader1.loadClass ("com.mythsman.test.hello"). NewInstance (); Objekt hellov2 = classloader2.loadClass ("com.mythsman.test.hello"). newInstance (); hellov1.getClass (). Ausgabe:
Dies ist von Hello V1this von Hello V2
OK, auch wenn Sie zwei Ladungen abgeschlossen haben, gibt es noch einige Punkte, auf die Sie achten können.
Wie ist die Beziehung zwischen zwei Klassen?
Offensichtlich sind diese beiden Klassen nicht die gleiche Klasse, aber ihre Namen sind die gleichen. Was sind also die Ergebnisse von Operatoren wie Issinstance von:
System.out.println ("Klasse:"+hellov1.getClass ()); System.out.println ("Klasse:"+hellov2.getClass ()); System.out.println ("hat hCode: "+hellov2.getClass (). Ausgabe:
Klasse: Klasse com.mythsman.test.helloclass: Klasse com.mythsman.test.hellohashCode: 1581781576HashCode: 1725154839Classloader: com.mythsman.test.main$1@5e2de80cclassloader: com.mythsman.test.Main$2@26474c2
Ihre Klassennamen sind in der Tat gleich, aber die Hashcodes der Klasse sind unterschiedlich, was bedeutet, dass diese beiden im Wesentlichen nicht die gleiche Klasse sind und ihre Klassenlader auch unterschiedlich sind (tatsächlich sind sie die beiden internen Klassen der Hauptklassen).
Wie ist die Beziehung zwischen diesen beiden Klassenladern und dem dreischichtigen Lader des Systems des Systems?
Nehmen Sie den ersten benutzerdefinierten Klassenlader als Beispiel:
System.out.println (classloader1.getParent (). GetParent (). GetParent ()); System.out.println (classloader1.getParent (). GetParent ()); System.out.println (classLo Ader1.getParent ()); System.out.println (classloader1.getParent ()); system.out.println (classloader1); System.out.println (classLaader.getSystemClassLader ());
Ausgabe:
nullsun.misc.launcher $ [email protected] $ [email protected] $ [email protected] $ Appclassloader@18B4AC2
Natürlich ist die hier erwähnte Eltern-Sohn-Beziehung keine Vererbungsbeziehung, sondern eine Kombinationsbeziehung. Der untergeordnete Klassenloader speichert eine Referenz (Eltern) des übergeordneten Klassenloaders.