Różnica między ładowaniem klasy za pomocą ClassLoader I Class.forName

Poniżej znajdują się 2 fragmenty kodu

Pierwszy używa ClassLoader class do załadowania określonej klasy

ClassLoader cls = ClassLoader.getSystemClassLoader(); Class someClass = cls.loadClass("TargetClass");

Drugi używa klasy.forName() to load a specified class

Class cls = Class.forName("TargetClass");

Jaka jest różnica między wyżej wymienionymi podejściami. Który służy do jakiego celu?

Author: Cœur, 2010-11-26

9 answers

Szybka Odpowiedź (bez próbek kodu)

Z jawnym podejściem ClassLoader cls = <a ClassLoader>; masz elastyczność ładowania klasy z Classloadera, który jest , a nie Twoim domyślnym Classloaderem. W Twoim przypadku używasz domyślnego systemowego Classloadera, więc daje on podobny ogólny wynik (z instancją ostatecznej różnicy obiektów) jak wywołanie Class.forName(String name), ale możesz zamiast tego odwołać się do innego Classloadera.

To powiedziawszy, możesz również używać Class.forName(String name, boolean initialize, ClassLoader loader) tak długo, jak długo wiedz, co to za Klasa.

Na przykład, Twoja aplikacja oparta na uchu ma swój własny ClassLoader z wersją biblioteki parsowania XML owiniętej wewnątrz niego. Twój kod zwykle używa tych klas, ale w jednym przypadku musisz pobrać klasę deserializacji ze wcześniejszej wersji biblioteki(którą serwer aplikacji przechowuje w jej ). Możesz więc odwołać się do tego programu ClassLoader serwera aplikacji.

Niestety do dostajemy project Jigsaw (JDK 8) to jest używane częściej niż byśmy chcieli :-)

 13
Author: Martijn Verburg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-26 14:29:51

Inne odpowiedzi są bardzo kompletne, ponieważ badają inne przeciążenia Class.forName(...) i mówią o możliwości korzystania z różnych Classloaderów.

[10]}jednak nie odpowiadają na twoje bezpośrednie pytanie: "Jaka jest różnica między wyżej wymienionymi podejściami?", która dotyczy jednego specyficznego przeciążenia Class.forName(...). I brakuje im jednej bardzo ważnej różnicy. Inicjalizacja klasy.

Rozważmy następującą klasę:

public class A {
  static { System.out.println("time = " + System.currentTimeMillis()); }
}

Rozważmy teraz następujące dwa metody:

public class Main1 {
  public static void main(String... args) throws Throwable {
    final Class<?> c = Class.forName("A");
  }
}

public class Main2 {
  public static void main(String... args) throws Throwable {
    ClassLoader.getSystemClassLoader().loadClass("A");
  }
}

Pierwsza klasa, Main1, Po uruchomieniu, wytworzy wyjście takie jak

time = 1313614183558

Drugi, jednak, nie będzie produkować wyjście w ogóle. Oznacza to, że klasa A, mimo że załadowana, nie została zainicjalizowana (tzn. nie została wywołana). W rzeczywistości, możesz nawet odpytywać członków klasy poprzez refleksję przed inicjalizacją!

Dlaczego miałoby cię to obchodzić?

Są klasy, które wykonują jakąś ważną inicjalizację lub Rejestracja po inicjalizacji.

Na przykład, JDBC określa interfejsy, które są implementowane przez różnych dostawców. Aby korzystać z MySQL, zwykle robisz Class.forName("com.mysql.jdbc.Driver");. Oznacza to, że ładujesz i inicjalizujesz klasę. Nigdy nie widziałem tego kodu, ale oczywiście statyczny konstruktor tej klasy musi zarejestrować klasę (lub coś innego) gdzieś z JDBC.

Jeśli to zrobiłeś ClassLoader.getSystemClassLoader().loadClass("com.mysql.jdbc.Driver");, nie będziesz mógł użyć JDBC, ponieważ Klasa, o ile załadowana, nie została zainicjalizowana (i wtedy JDBC nie wiedziałby, której implementacji użyć, tak jak gdybyś nie załadował klasy).

Więc to jest różnica między tymi dwoma metodami, o które prosiłeś.

 41
Author: Bruno Reis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-17 21:04:30

W twoim konkretnym przypadku:

ClassLoader cls = ClassLoader.getSystemClassLoader();
Class someClass = cls.loadClass("TargetClass");

Powyższy kod załaduje się TargetClass zawsze z system classloader .

Class cls = Class.forName("TargetClass");

Drugi fragment kodu załaduje (i zainicjalizuje) TargetClass za pomocą classloader, który został użyty do załadowania klasy wykonującej tę linię kodu. Jeśli ta klasa została załadowana Z system classloader, dwa podejścia są identyczne (z wyjątkiem inicjalizacji klasy, jak wyjaśniono w doskonałej odpowiedzi przez Bruno).

Którego użyć? do ładowania i sprawdzania klas z odbiciem, zalecam użycie specific class loader (ClassLoader.loadClass()) - daje Ci to kontrolę i pomaga uniknąć potencjalnie niejasnych problemów między różnymi środowiskami.

Jeśli chcesz załadować i zainicjalizować, użyj Class.forName(String, true, ClassLoader).

Jak znaleźć odpowiednią ładowarkę klasową?To zależy od Twojego środowiska:

  • jeśli uruchamiasz aplikację wiersza poleceń, możesz po prostu użyj system classloader lub class loader, który załadował Twoje klasy aplikacji (Class.getClassLoader()).
  • jeśli uruchamiasz się w zarządzanym środowisku (JavaEE, servlet container, itp.), najlepiej będzie najpierw sprawdzić current thread context class loader , a następnie wrócić do opcji podanych w poprzednim punkcie.
  • Możesz też użyć własnego loadera klasowego (jeśli lubisz takie rzeczy).]}

Ogólnie rzecz biorąc, najbardziej niezawodne i przetestowane byłoby użycie ClassUtils.forName() od wiosny (zobacz JavaDoc).

Bardziej szczegółowe wyjaśnienie:


Najczęstsza forma Class.forName(), ta, która przyjmuje pojedynczy parametr String, zawsze używa classloadera wywołującego. Jest to classloader, który ładuje kod wykonujący metodę forName(). Dla porównania, ClassLoader.loadClass() jest metodą instancyjną i wymaga wybrania konkretnego classloadera, który może, ale nie musi być loaderem, który ładuje ten kod. Jeśli wybór konkretnego loadera do załadowania klasy jest ważny dla Twojego projektu, powinieneś użyć ClassLoader.loadClass() lub trzy-parametrowej wersji forName() dodanej w Java 2 Platform, Standard Edition (J2SE): Class.forName(String, boolean, ClassLoader).

Źródło: Jaka jest różnica między Class.forName() a ClassLoader.loadClass()?


Również, SPR-2611 podkreśla jedną ciekawą niejasną obudowę narożną podczas używania Class.forName(String, boolean, ClassLoader).

Jak widać w tym wiosennym wydaniu, za pomocą ClassLoader.loadClass () jest zalecanym podejściem (gdy trzeba załadować klasy z określonej klasy loader).

 12
Author: Neeme Praks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-09 08:07:45

ClassLoader.loadClass() używa podanego classloadera (system classloader w Twoim przypadku), podczas gdy Class.forName() używa classloadera bieżącej klasy.

Class.forName() może być używany, gdy nie zależy ci na konkretnym classloaderze i chcesz mieć takie samo zachowanie classloading jak dla klas odwołujących się statycznie.

 1
Author: axtavt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-26 14:07:19

Z API doc:

Wywołanie tej metody jest równoważne:

  Class.forName(className, true, currentLoader)

Gdzie currentLoader oznacza definiującą klasę loader bieżącego klasy.

Więc główna różnica polega na tym, który classloader będzie używany(Może być taki sam, jak system classloader). Metoda przeciążona pozwala również na jawne określenie classloadera, którego ma używać.

 1
Author: Michael Borgwardt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-26 14:22:37

ClassLoader.loadclass () zawsze ładuje system classloader, podczas gdy Class.forName() ładuje dowolną klasę. Zobaczmy ten przykład,

package com;
public class TimeA {
      public static void main (String args[]) {
            try {
                final Class c = Class.forName("com.A");
                ClassLoader.getSystemClassLoader().loadClass("com.A");
            }catch(ClassNotFoundException ex) {
                System.out.println(ex.toString());
            }
      }
}

class A {
      static {
          System.out.println("time = " + System.currentTimeMillis()); 
      }
}

Kiedy yoy uruchamia ten program, otrzymasz wyjątek w ClassLoader.getSystemClassLoader().loadClass("com.A");

Wyjście może być:

time = 1388864219803
java.lang.ClassNotFoundException: com.A
 1
Author: Akhilesh Dhar Dubey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-04 19:50:14

Drugie podejście ładuje klasę za pomocą ClassLoader

 public static Class<?> forName(String className) 
                throws ClassNotFoundException {
        return forName0(className, true, ClassLoader.getCallerClassLoader());

Tak mówi JavaDoc:

forName(String name, boolean initialize, ClassLoader loader)

Określona Klasa loader jest używana do załaduj klasę lub interfejs. Jeśli parametr loader jest null, klasa jest ładowane przez klasę bootstrap ładowacz.

Tak więc, druga opcja wykorzystuje system ClassLoader(który jest w istocie tym, co robi w pierwszej opcji).

 0
Author: Buhake Sindi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-26 14:10:22

Jest też różnica podczas wczytywania typów tablic. Myślę, że {[0] } nie może obsługiwać typów tablic, ale Class.forName(clazz,true,classloader) może.

 0
Author: Gerhard Presser,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-23 14:03:02

Ale statyczny blok inicjalizacji jest wykonywany tylko wtedy, gdy używamy klasy.forname("...");

Właśnie testowałem.

 0
Author: Argho Chatterjee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-01 09:34:42