Из каких основных компонентов состоит ClassLoader в Java?

«Из каких основных компонентов состоит ClassLoader в Java?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

ClassLoader — это компонент JRE, отвечающий за динамическую загрузку классов. Его архитектура состоит из следующих ключевых частей:

1. Иерархия загрузчиков (родительское делегирование)

  • Bootstrap ClassLoader (Нативный): Загружает основные классы Java (java.lang.*, java.util.* и др.) из rt.jar или модулей. Реализован на нативном коде.
  • Platform (Extension) ClassLoader: Загружает классы из стандартных расширений (ранее jre/lib/ext) или модулей платформы. Родитель — Bootstrap.
  • System (Application) ClassLoader: Загружает классы из classpath приложения (-cp, -classpath). Родитель — Platform ClassLoader.
  • Пользовательские (кастомные) ClassLoader'ы: Создаются разработчиками, обычно родителем указывается System ClassLoader.

2. Механизм делегирования При запросе на загрузку класса загрузчик сначала делегирует запрос своему родителю. Только если родительская цепочка не смогла загрузить класс, текущий загрузчик пытается сделать это сам через метод findClass().

3. Ключевые методы класса java.lang.ClassLoader

  • loadClass(String name) — основной публичный метод, реализующий логику делегирования и вызова findClass.
  • findClass(String name) — метод, который должен быть переопределен в кастомном загрузчике для поиска и определения байт-кода класса.
  • defineClass(String name, byte[] b, int off, int len) — финальный метод, преобразующий массив байтов в объект Class<?> внутри JVM.
  • getParent() — возвращает родительский загрузчик.

Пример кастомного ClassLoader:

public class MyClassLoader extends ClassLoader {
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        // 1. Загрузить байт-код класса из нестандартного места (файл, сеть)
        byte[] classBytes = loadClassBytesFromCustomSource(name);
        if (classBytes == null) {
            throw new ClassNotFoundException(name);
        }
        // 2. Преобразовать байты в Class-объект
        return defineClass(name, classBytes, 0, classBytes.length);
    }
    private byte[] loadClassBytesFromCustomSource(String name) { /* ... */ }
}