Class.forName() vs ClassLoader.loadClass() - что использовать для динамической загрузки?

При динамической загрузке класса, когда целесообразно использовать

Class.forName("SomeClass");

и когда следует использовать

ClassLoader.getSystemClassLoader().loadClass("SomeClass");

Или это два способа сделать одно и то же?

Ответ 1

Они совершенно разные!

Как указано в документации для Class.forName(String),

Возвращает объект класса, связанный с классом или интерфейсом, с заданным именем строки. Вызов этого метода эквивалентен: Class.forName(className, true, currentLoader)

(true здесь означает, хотите ли вы инициализировать класс?)

С другой стороны, ClassLoader.loadClass(String):

Вызов этого метода эквивалентен вызову loadClass(name, false).

(здесь логическое значение не имеет ничего общего с инициализацией, но если вы проверите документацию loadClass (String, boolean), вы увидите, что все, что он делает, это загрузить класс, а не инициализировать его).

Первый (Class.forName("SomeClass");) будет:

  • используйте загрузчик классов, загружающий класс, который вызывает этот код
  • инициализировать класс (то есть все статические инициализаторы будут выполняться)

Другой (ClassLoader.getSystemClassLoader().loadClass("SomeClass");) будет:

  • используйте загрузчик класса "system" (который является переопределяемым)
  • не инициализировать класс (скажем, если вы используете его для загрузки драйвера JDBC, он не будет зарегистрирован, и вы не сможете использовать JDBC!)

Предположим, что вы кодируете веб-приложение, которое будет выполняться в контейнере, таком как Tomcat. То, что делает Tomcat, создает загрузчик классов для каждого веб-приложения (чтобы он мог выгрузить Webapps позже и освободить память - для этого вам нужен специальный загрузчик классов!). В этой ситуации вы можете видеть, что оба вызова дают совершенно разные результаты!

Для получения более подробной (и достоверной) информации о загрузке и инициализации классов, проверьте разделы 12.2 и 12.4 последнего (третьего) выпуска Спецификации языка Java.

Ответ 2

Class.forName() использует загрузчик классов caller и инициализирует класс (запускает статические инициализаторы и т.д.)

loadClass - это метод ClassLoader, поэтому он использует явно загруженный загрузчик и инициализирует класс лениво (при первом использовании).

Обратите внимание, что существует Class.forName(), который также принимает ClassLoader.

Ответ 3

Они в основном делают то же самое. Используемый ClassLoader может быть другим. Class.forName использует ClassLoader, который вы получаете из this.getClass(). GetClassLoader(), тогда как ваш другой код указывает на загрузку системного класса.

В большинстве приложений это будет тот же загрузчик классов, но в более сложных средах, таких как приложение J2EE или апплет, это может быть не так.

Ответ 4

ClassLoader - абстрактный класс, однако ваше приложение всегда загружается загрузчиком классов, могут быть пользовательские загрузчики классов, такие как сетевой загрузчик классов или любой другой источник.

С другой стороны, класс сам по себе представляет классы и интерфейсы, а класс Class имеет функцию forName, которая использует текущий загрузчик классов, в котором ваше приложение работает по умолчанию для загрузки класса.

Вот источник для Class.forName, который, в свою очередь, вызывает вызывающий загрузчик классов.

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

http://docs.oracle.com/javase/1.4.2/docs/api/java/lang/ClassLoader.html

http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Class.html#forName(java.lang.String)

Подсказка: Первоначальный загрузчик классов http://docs.oracle.com/javase/1.4.2/docs/guide/security/spec/security-spec.doc5.html

Ответ 5

  • Class.forName() загрузить и инициализировать класс. В подсистеме загрузчика классов он выполняет все три фазы, то есть нагрузки, связи и инициализации фаз.

  • ClassLoader.loadClass(), которое задерживает инициализацию до тех пор, пока класс не будет использоваться в первый раз. В подсистеме загрузчика классов он выполняет только две фазы, то есть нагрузки и фазы связи.

Например:

class MyClass {
    static {
        System.out.println("static block in MyClass");
    }
}

public class TestCase1 {
    public static void main(String... args) throws Throwable {
        Class.forName("A");
    }
} //The above TestCase1 produce output: static block in MyClass

public class TestCase2 {
    public static void main(String... args) throws Throwable {
        ClassLoader.getSystemClassLoader().loadClass("MyClass");
    }
} //The above TestCase2 not produce any output

Ответ 6

Мне нравится загрузка класса в java...

Это зависит от того, в каком контексте выполняется приложение. Вы получите разные результаты, если используете его из веб-контекста, а не только из командной строки.

У меня также возникают проблемы в зависимости от того, как выглядит ваш ClassPath и чего я ожидал.

В этом статья JavaWorld подробно описывает это.