Ответ
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) { /* ... */ }
} Ответ 18+ 🔞
О, слушай, смотри, сейчас я тебе про ClassLoader так расскажу, что ты офигеешь, блядь. Это ж не просто какая-то хуйня, это основа основ, на которой всё держится, как тот самый Герасим на своей подводной лодке, только он Муму топил, а этот классы грузит.
Представь себе, что JVM — это такой большой, важный дом, а ClassLoader — это, блядь, швейцар, привратник, охранник и курьер в одном лице. Его задача — не пустить всякую левую шнягу, но при этом вовремя доставить нужных гостей, то есть классы.
1. Иерархия, или «Папа, решай вопрос!» Тут всё по понятиям, чёткая вертикаль, блядь. Каждый загрузчик знает своего папочку.
- Bootstrap ClassLoader (Наш пахан, босс): Этот, сука, самый главный. Он на нативном коде написан, его даже на Java не увидишь. Он грузит самые важные классы, типа
java.lang.*, изrt.jar. Он как тот немой Герасим — сил дохуя, но говорит только «Муму», то есть работает молча и без вопросов. - Platform (Extension) ClassLoader (Старший сынок): Его папа — Bootstrap. Он отвечает за стандартные расширения, всякие библиотеки из
jre/lib/ext. Уже поближе к нам, но всё ещё не наш. - System (Application) ClassLoader (Наш парень с района): Вот он-то и грузит твой код из classpath, который ты в
-cpуказал. Его папа — Platform ClassLoader. Это тот, с кем ты чаще всего имеешь дело, пока не начнёшь выёбываться. - Пользовательские (кастомные) ClassLoader'ы (Мелкие распиздяи): Это мы с тобой можем накодить. Обычно их родителем делают System ClassLoader, но они могут и своё мутить.
2. Механизм делегирования, или «Спроси у отца!» Вот тут вся соль, блядь! Алгоритм простой, как три копейки:
- Приходит запрос на класс. Загрузчик, вместо того чтобы сразу бежать искать, такой: «А не, погоди, я спрошу у папы».
- Он тыкает своего родителя: «Пап, загрузи класс
com.example.MyAwesomeShit». - Родитель делает ровно то же самое — тыкает своего родителя. И так по цепочке вверх, до самого Bootstrap.
- Если наверху сказали «Да, ёпта, знаем такого, держи» — всё, класс загружен.
- Если все папы разводят руками и говорят «Не, сынок, это не наши люди», только тогда текущий загрузчик начинает искать сам, через свой метод
findClass().
Это чтобы, блядь, какой-нибудь ушлый кастомный загрузчик не подсунул свою левую версию класса java.lang.String и не устроил пиздец. Безопасность, ёпта!
3. Ключевые методы, или Инструменты в руках у швейцара
loadClass(String name)— это публичное лицо. Ты ему имя класса — он тебе Class-объект. Внутри он и реализует всю эту хуйню с делегированием родителям.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) {
// Если не нашёл — извини, дружок, ClassNotFoundException тебе в сраку.
throw new ClassNotFoundException(name);
}
// 2. Нашёл? А теперь волшебный пендель!
// defineClass сделает из этой кучи байтов законный класс.
return defineClass(name, classBytes, 0, classBytes.length);
}
private byte[] loadClassBytesFromCustomSource(String name) {
// Тут твоя фантазия, блядь. Можешь даже с Луны скачивать.
// Главное — верни массив байтов, а не хуйню какую-то.
}
}
Вот и вся философия. Сначала спроси у старших, не нарушай иерархию, а если уж очень надо — тогда делай по-своему, но будь готов к последствиям. Прямо как в жизни, ёпта.