Zaczerpnięte z tego fajnego samouczka Sun:
Motywacja
Aplikacje napisane w statycznie skompilowanych językach programowania, takich jak C i C ++, są kompilowane w natywne, specyficzne dla maszyny instrukcje i zapisywane jako plik wykonywalny. Proces łączenia kodu w wykonywalny kod natywny nazywa się łączeniem - łączeniem oddzielnie skompilowanego kodu z kodem biblioteki współdzielonej w celu utworzenia wykonywalnej aplikacji. Inaczej jest w przypadku dynamicznie kompilowanych języków programowania, takich jak Java. W języku Java pliki .class generowane przez kompilator języka Java pozostają niezmienione do momentu załadowania do wirtualnej maszyny języka Java (JVM) - innymi słowy, proces łączenia jest wykonywany przez maszynę JVM w czasie wykonywania. Klasy są ładowane do maszyny JVM na zasadzie „w razie potrzeby”. A kiedy załadowana klasa zależy od innej klasy, to ta klasa również jest ładowana.
Po uruchomieniu aplikacji Java pierwszą klasą do uruchomienia (lub punktem wejścia do aplikacji) jest ta z publiczną statyczną metodą void o nazwie main (). Ta klasa zwykle zawiera odwołania do innych klas, a wszystkie próby załadowania klas, do których się odwołuje, są wykonywane przez program ładujący klasy.
Aby zapoznać się z rekurencyjnym ładowaniem klas, a także ogólnie z ideą ładowania klas, rozważ następującą prostą klasę:
public class HelloApp {
public static void main(String argv[]) {
System.out.println("Aloha! Hello and Bye");
}
}
Jeśli uruchomisz tę klasę, podając opcję wiersza polecenia -verbose: class, aby wypisała, jakie klasy są ładowane, otrzymasz dane wyjściowe, które wyglądają następująco. Zauważ, że jest to tylko część wyniku, ponieważ lista jest zbyt długa, aby ją tutaj pokazać.
prmpt>java -verbose:class HelloApp
[Opened C:\Program Files\Java\jre1.5.0\lib\rt.jar]
[Opened C:\Program Files\Java\jre1.5.0\lib\jsse.jar]
[Opened C:\Program Files\Java\jre1.5.0\lib\jce.jar]
[Opened C:\Program Files\Java\jre1.5.0\lib\charsets.jar]
[Loaded java.lang.Object from shared objects file]
[Loaded java.io.Serializable from shared objects file]
[Loaded java.lang.Comparable from shared objects file]
[Loaded java.lang.CharSequence from shared objects file]
[Loaded java.lang.String from shared objects file]
[Loaded java.lang.reflect.GenericDeclaration from shared objects file]
[Loaded java.lang.reflect.Type from shared objects file]
[Loaded java.lang.reflect.AnnotatedElement from shared objects file]
[Loaded java.lang.Class from shared objects file]
[Loaded java.lang.Cloneable from shared objects file]
[Loaded java.lang.ClassLoader from shared objects file]
[Loaded java.lang.System from shared objects file]
[Loaded java.lang.Throwable from shared objects file]
.
.
.
[Loaded java.security.BasicPermissionCollection from shared objects file]
[Loaded java.security.Principal from shared objects file]
[Loaded java.security.cert.Certificate from shared objects file]
[Loaded HelloApp from file:/C:/classes/]
Aloha! Hello and Bye
[Loaded java.lang.Shutdown from shared objects file]
[Loaded java.lang.Shutdown$Lock from shared objects file]
Jak widać, najpierw ładowane są klasy środowiska uruchomieniowego Java wymagane przez klasę aplikacji (HelloApp).
Moduły ładujące klasy na platformie Java 2
Język programowania Java stale ewoluuje, aby codziennie ułatwiać życie programistom aplikacji. Odbywa się to poprzez udostępnianie interfejsów API, które upraszczają życie, umożliwiając skoncentrowanie się na logice biznesowej, a nie na szczegółach implementacji podstawowych mechanizmów. Jest to widoczne po niedawnej zmianie J2SE 1.5 na J2SE 5.0 w celu odzwierciedlenia dojrzałości platformy Java.
Od wersji JDK 1.2 program ładujący klasy ładowania początkowego wbudowany w maszynę JVM jest odpowiedzialny za ładowanie klas środowiska wykonawczego Java. Ten program ładujący klasy ładuje tylko klasy, które znajdują się w ścieżce startowej klasy, a ponieważ są to klasy zaufane, proces sprawdzania poprawności nie jest wykonywany, jak w przypadku klas niezaufanych. Oprócz modułu ładującego klasy ładujące, maszyna JVM ma moduł ładujący klasy rozszerzeń odpowiedzialny za ładowanie klas ze standardowych interfejsów API rozszerzeń oraz moduł ładujący klasy systemowe, który ładuje klasy z ogólnej ścieżki klas, a także klas aplikacji.
Ponieważ istnieje więcej niż jeden program ładujący klasy, są one reprezentowane w drzewie, którego korzeniem jest program ładujący klasy ładowania początkowego. Każdy program ładujący klasy ma odniesienie do swojego programu ładującego klasy nadrzędnej. Kiedy program ładujący klasy jest proszony o załadowanie klasy, konsultuje się z programem ładującym klasę nadrzędną przed próbą załadowania samego elementu. Rodzic z kolei konsultuje się ze swoim rodzicem i tak dalej. Tak więc tylko wtedy, gdy wszystkie programy ładujące klas przodków nie mogą znaleźć klasy, w którą zaangażowany jest bieżący program ładujący klas. Innymi słowy, używany jest model delegacji.
Klasa java.lang.ClassLoader
java.lang.ClassLoader
Jest klasą abstrakcyjną, która może być podklasy przez aplikacje, które muszą rozciągać się, w jaki sposób JVM dynamicznie ładuje klas. Konstruktory w java.lang.ClassLoader
(i jego podklasy) umożliwiają określenie rodzica podczas tworzenia instancji nowego programu ładującego klasy. Jeśli nie określisz jawnie elementu nadrzędnego, moduł ładujący klasy systemowej maszyny wirtualnej zostanie przypisany jako domyślny element nadrzędny. Innymi słowy, klasa ClassLoader używa modelu delegowania do wyszukiwania klas i zasobów. W związku z tym każde wystąpienie klasy ClassLoader ma skojarzony moduł ładujący klasy nadrzędnej, dzięki czemu w przypadku żądania znalezienia klasy lub zasobów zadanie jest delegowane do programu ładującego klasy nadrzędnej przed próbą znalezienia samej klasy lub zasobu. loadClass()
Sposób classloader wykonuje następujące zadania, w porządku, gdy nazwie się załadować klasę:
Jeśli klasa została już załadowana, zwraca ją. W przeciwnym razie deleguje wyszukiwanie nowej klasy do programu ładującego klasy nadrzędnej. Jeśli moduł ładujący klasy nadrzędnej nie znajdzie klasy, loadClass()
wywołuje metodę w findClass()
celu znalezienia i załadowania klasy. W finalClass()
sposób wyszukuje klasy w bieżącym ładującego klasy, jeśli klasa nie została znaleziona przez loader klasy nadrzędnej.
Więcej znajduje się w oryginalnym artykule, który pokazuje również, jak zaimplementować własne programy ładujące klasy sieci, co odpowiada na twoje pytanie, dlaczego (i jak). Zobacz także dokumentację API .