Другие ответы очень полны, поскольку они исследуют другие перегрузки Class.forName(...)
и говорят о возможности использования различных загрузчиков классов.
Однако они не отвечают на ваш прямой вопрос: «В чем разница между вышеупомянутыми подходами?», Который имеет дело с одной конкретной перегрузкой Class.forName(...)
. И они упускают одно очень важное отличие. Класс инициализации .
Рассмотрим следующий класс:
public class A {
static { System.out.println("time = " + System.currentTimeMillis()); }
}
Теперь рассмотрим следующие два метода:
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");
}
}
Первый класс Main1
при запуске выдаст вывод, такой как
time = 1313614183558
Другой, однако, не будет выводить вообще. Это означает, что класс A
, хотя и загружен, не был инициализирован (т. Е. Он <clinit>
не был вызван). На самом деле, вы даже можете запросить членов класса через отражение перед инициализацией!
Почему тебя это волнует?
Существуют классы, которые выполняют какую-то важную инициализацию или регистрацию при инициализации.
Например, JDBC определяет интерфейсы, которые реализуются разными провайдерами. Чтобы использовать MySQL, вы обычно делаете Class.forName("com.mysql.jdbc.Driver");
. То есть вы загружаете и инициализируете класс. Я никогда не видел этот код, но, очевидно, статический конструктор этого класса должен зарегистрировать класс (или что-то еще) где-нибудь в JDBC.
Если вы сделали ClassLoader.getSystemClassLoader().loadClass("com.mysql.jdbc.Driver");
, вы не сможете использовать JDBC, так как класс, хотя и загруженный, не был инициализирован (и тогда JDBC не будет знать, какую реализацию использовать, как если бы вы не загружали класс).
Так вот, в чем разница между двумя методами, которые вы просили.