Загрузчики классов (ClassLoader)
ClassLoader загружает .class-файлы в JVM в момент первого использования класса — лениво, не все сразу при старте. Загрузчиков несколько, они выстроены в иерархию:
Принцип родительского делегирования (Parent Delegation): запрос на загрузку класса сначала идёт вверх к Bootstrap, и только если тот не нашёл — обрабатывает дочерний. Это защищает системные классы: никто не может подменить java.lang.String своим классом из classpath.
// Посмотреть загрузчик класса
System.out.println(String.class.getClassLoader()); // null — Bootstrap (native)
System.out.println(MyClass.class.getClassLoader()); // AppClassLoader
Копнуть глубже
Когда класс загружается. Загрузка происходит при первом из событий:
- создание экземпляра (
new MyClass()) - обращение к статическому полю или методу
- явная загрузка:
Class.forName("com.app.MyClass")
После загрузки идут линковка (верификация байт-кода + разрешение ссылок) и инициализация (выполнение static {}). Статический блок выполняется ровно один раз при первой загрузке.
Custom ClassLoader — для особых случаев: загрузка из сети/БД, hot-reload (Spring DevTools, JRebel), изоляция зависимостей. Tomcat использует свой ClassLoader чтобы изолировать разные веб-приложения друг от друга:
public class MyLoader extends ClassLoader {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] bytes = loadBytesFromNetwork(name);
return defineClass(name, bytes, 0, bytes.length);
}
}
Проблема «разных ClassLoader’ов». Класс com.App загруженный ClassLoader’ом A и тот же класс загруженный ClassLoader’ом B — разные типы для JVM. Причина загадочных ClassCastException в OSGi, Tomcat, и при сериализации между classloader’ами.
• когда именно загружается класс и что выполняет static {} блок (если дошёл до 2-го слоя).