2011-12-01 13 views
38

Niedawno natrafiłem na kod, który skłonił mnie do myślenia. Jaka jest różnica pomiędzy:Java: Różnica między Class.forName a ClassLoader.loadClass

Class theClass = Class.forName("SomeImpl"); 
SomeImpl impl = (SomeImpl)theClass.newInstance(); 

oraz:

Class theClass = ClassLoader.loadClass("SomeImpl"); 
SomeImpl impl = (SomeImpl)theClass.newInstance(); 

Są one synonimami? Czy jeden jest lepszy od drugiego w pewnych okolicznościach? Do czego służą i nie używają tych dwóch metod?

Z góry dziękuję.

+0

Osobiście staram się nie używać literalnych ciągów znaków w moim kodzie ... Zastanów się nad wpisaniem 'SomeImpl.class.getSimpleName()' zamiast literału o napiętym kodzie (oszczędza głowy podczas zmiany nazwy klasy). – Yuval

+8

@ Yuval: Zazwyczaj zgadzam się z tobą, ale w przypadku 'Class.forName()' * cały punkt * jest taki, że przekazujesz mu ciąg znaków. Jeśli masz instancję 'Class ', nie musisz wywoływać 'Class.forName()'. A jeśli możesz napisać 'SomeImpl.class', możesz napisać' new SomeImpl() 'zamiast używać w ogóle refleksji. –

+2

@Jeval, dynamiczne ładowanie klas jest podstawową funkcją w Javie i nie ma nic wspólnego z użyciem łańcucha zamiast klasy. [RE. Pryden już to obejmuje] – bestsss

Odpowiedz

18

Class.forName() zawsze będzie używać ClassLoader dzwoniącego, natomiast ClassLoader.loadClass() może określać inną klasę ClassLoader. Wierzę, że Class.forName inicjuje również załadowaną klasę, podczas gdy metoda ClassLoader.loadClass() nie robi tego od razu (nie jest inicjowana, dopóki nie zostanie użyta po raz pierwszy).

Po prostu znalazłem ten artykuł podczas próby potwierdzenia mojego podsumowania zachowania podczas inicjalizacji. Wygląda to tak ma większość informacji szukasz:

http://www.javaworld.com/javaworld/javaqa/2003-03/01-qa-0314-forname.html

takie wykorzystanie jest całkiem fajny, chociaż nigdy nie używałem go przed:

Class.forName(String, boolean, ClassLoader) 

to pozwala określ klasę ClassLoader, a parametr boolowski określa, czy klasa powinna zostać zainicjowana po załadowaniu, czy nie.

+1

Użyje on tylko programu ładującego klasy, jeśli go nie dostarczysz. Z tego samego artykułu: "... Jeśli wybranie konkretnego programu ładującego do załadowania klasy jest ważne dla twojego projektu, powinieneś użyć' ClassLoader.loadClass() '** lub 3-parametrowa wersja forName() ** dodana w Java 2 Platform, Standard Edition (J2SE):' Class.forName (String, boolean, ClassLoader) '." – phs

10

odpowiedź Shauna jest mniej lub bardziej poprawne z wyjątkiem kilku przeoczeń/small błędów:

  • Class.forName współpracownicy klasy w/ClassLoader (niezależnie od tego czy jakichkolwiek innych obciążeń macierzystej za prawdziwe), stąd ClassLoader.findLoadedClass powiedzie następnym razem . To bardzo, bardzo ważny punkt, większość ClassLoader spróbuje Class c = findLoadedClass(name); if (c!=null) return c; jako pierwszych instrukcji omijając całą część znajdź/zobacz. Wywołanie bezpośrednio ClassLoader.load nie doda klasy do załadowanych.

Sprawa ma implikacje po wczytaniu za pośrednictwem struktury podobnej do wykresu ClassLoader, tj. Bez użycia rodzica tylko do pierwszego wyszukiwania.

  • inicjalizacji klasy jest wykonywana w loadClass z classloader w/kodu tak: if (resolve) resolveClass(c); i ClassLoader może faktycznie pominąć go rozwiązać to uczucie, Unrecommended ale możliwe.

Co należy robić i nie używać tych dwóch metod?

Jeśli nie masz bardzo mocnego pomysłu, dlaczego chcesz ClassLoader.loadClass(String), nie używaj go bezpośrednio. We wszystkich innych przypadkach zawsze należy polegać na Class.forName(name, true, classLoader).

Ogólnie ładowanie klasy jest obok sztuki i nie mogą być pokryte w prostej odpowiedzi (nie żartuje o części artystycznej)

3

Kiedy używać użyć Class.forName("SomeImpl"), jesteś uzyskania klasę poprzez bieżącego classloader (tj program ładujący klasy, do którego wywoływana jest metoda).Będzie także initialize klasa. Jest to skutecznie to samo, co wywołanie Class.forName("SomeImpl", true, currentLoader), gdzie currentLoader byłby programem ładującym klasy wywołującego. Zobacz szczegóły here.

Druga metoda wymaga najpierw wybrania modułu ładującego klasy. Nie pisz tego jako ClassLoader.loadClass("SomeImpl"), ponieważ jest to metoda statyczna. Można by wymagać czegoś podobnego

final ClassLoader cl = this.getClass().getClassLoader(); 
Class theClass = cl.loadClass("SomeImpl"); 

pamiętać, że podklasy ClassLoader powinien zastąpić metodę findClass zamiast loadClass. Jest to to samo, co wywołanie metody (chronionej), gdzie drugi argument wskazuje, czy należy wykonać łączenie, czy nie.

Są bardziej subtelne różnice ... Metoda loadClass oczekuje binarnego nazwa klasy określone przez specyfikację języka Java, a forName może być również stosowana z ciągami reprezentujących prymitywnych typów lub klas tablicy.

Kilka, najlepiej jest użyć Class.forName, w razie potrzeby określając konkretny moduł ładujący klasy i czy musi on być intializowany, czy nie, pozwól implementacji wykombinować resztę. Używanie programów ładujących klasy bezpośrednio jest dobre do znajdowania zasobów w słoiku lub ścieżce klas.

1

Linia ta nie zostanie skompilowany:

Class theClass = ClassLoader.loadClass("SomeImpl"); 

ponieważ loadClass nie jest statyczna metoda ClassLoader.

Aby rozwiązać ten problem, należy utworzyć obiekt classloader jak następuje w jednej z 3 możliwych sposobów:

ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 
ClassLoader classLoader = Main.class.getClassLoader();  // Assuming in class Main 
ClassLoader classLoader = getClass().getClassLoader();  // works in any class 

następnie zadzwonić:

Class theClass = classLoader.loadClass("SomeImpl"); 

-dbednar

0

Sposób loadClass() może” można go nazwać jako static. Utwórz podklasę dla ClassLoader i wykonaj kilka dodatkowych metod. Można utworzyć własny program ładujący klasy, rozszerzając klasę ClassLoader. W funkcjonalny obie strony są takie same.