Какие типы загрузчиков классов (ClassLoader) существуют в Java?

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

Ответ

Загрузчики классов в Java образуют иерархию с чётким порядком делегирования.

Встроенные загрузчики (Иерархия)

  1. Bootstrap ClassLoader (Primordial):

    • Назначение: Загружает основные классы Java Core (java.lang.*, java.util.* и т.д.) из rt.jar и других ключевых библиотек.
    • Особенность: Реализован на нативном коде (часть JVM), не является Java-объектом. В коде отображается как null.
  2. Platform (Extension) ClassLoader:

    • Назначение: Загружает классы из директории расширений JRE (например, jre/lib/ext) или пути, указанного в java.ext.dirs.
    • Родитель: Bootstrap ClassLoader.
    • Реализация: Java-класс, наследник java.net.URLClassLoader.
  3. System (Application) ClassLoader:

    • Назначение: Загружает классы приложения из classpath (параметр -cp или -classpath).
    • Родитель: Platform ClassLoader.
    • Реализация: Также наследник URLClassLoader. Доступен через ClassLoader.getSystemClassLoader().

Пользовательские загрузчики (Custom ClassLoaders)

Разработчик может создавать собственные загрузчики, наследуясь от ClassLoader или URLClassLoader. Это нужно для:

  • Динамической загрузки классов (например, из сети или БД).
  • Реализации изоляции модулей (как в OSGi или сервлет-контейнерах).
  • Hot-swapping кода в runtime.

Пример пользовательского загрузчика:

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

Принцип делегирования (Parent-Delegation Model)

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