Недоумение ClassLoader

Я видел несколько мест, которые "Class.getClassLoader() возвращает ClassLoader, используемый для загрузки этого конкретного класса", и поэтому я озадачен результатами следующего примера:


package test;

import java.lang.*;

public class ClassLoaders { 
    public static void main(String[] args) throws java.lang.ClassNotFoundException{
      MyClassLoader mcl = new MyClassLoader();
      Class clazz = mcl.loadClass("test.FooBar");
      System.out.println(clazz.getClassLoader() == mcl); // prints false
      System.out.println(clazz.getClassLoader()); // prints e.g. [email protected]
    }
}

class FooBar { }

class MyClassLoader extends ClassLoader { }

Не должно ли выражение clazz.getClassLoader() == mcl возвращать true? Может кто-нибудь объяснить, чего я здесь не хватает?

Спасибо.

Ответ 1

Всякий раз, когда вы создаете свой собственный загрузчик классов, он будет привязан в древовидной иерархии загрузчиков классов. Для загрузки класса загрузчик классов сначала делегирует загрузку родительскому объекту. Только после того, как все родители не найдут класс, загрузчик, которого сначала попросили загрузить класс, попытается загрузить его.

В вашем конкретном случае загрузка делегируется родительскому загрузчику классов. Хотя вы запрашиваете MyClassLoader для его загрузки, именно он выполняет загрузку. В этом случае это AppClassLoader.

Ответ 2

Ссылаясь на API-документ ClassLoader:

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

Ответ 3

Если самоопределенный загрузчик классов делегирует вызов загрузчику классов VM, который загружает класс. clazz.getClassLoader() вернет этот загрузчик классов.

Чтобы узнать подробности: Javadoc класса ClassLoader содержит следующее объяснение порядка выполненных шагов:

Загружает класс с указанным двоичное имя. По умолчанию реализация этого метода для классов в следующем порядке:

  • Вызвать findLoadedClass (String), чтобы проверить, был ли класс уже загружен.
  • Вызвать метод loadClass для загрузчика родительского класса. Если родительский null загрузчик классов, встроенный в вместо этого используется виртуальная машина.
  • Вызовите метод findClass (String), чтобы найти класс.

Как вы унаследовали без изменения методы, это поведение не изменится. Шаг 2 будет тем, где будет загружен класс. Когда вы вызываете конструктор без параметров в ClassLoader (автоматически, поскольку вы не определяли конструктор в MyClassLoader), вы автоматически используете встроенный ClassLoader.