Можно ли загрузить в JVM два класса с одинаковым именем
Что проверяют
Знаете ли вы, что класс в JVM определяется не только именем, но и загрузчиком. Ответ «да, если в разных пакетах» не засчитывают: у таких классов разные полные имена. Следом спрашивают, где это встречается и какую ошибку вы увидите при приведении типов.
Как отвечать
Да, можно. Идентичность класса в рантайме это пара: полное имя и загрузчик, который его определил. Два com.example.Foo, загруженные разными ClassLoader, для JVM разные типы.
- где встречается: Tomcat даёт каждому веб-приложению свой загрузчик, поэтому приложения держат разные версии одной библиотеки. Так же устроены OSGi, плагинные системы,
ModuleLayerи перезапуск в Spring Boot DevTools; - делегирование: стандартный загрузчик сначала спрашивает родителя, и класс из общего родителя загрузится один раз. Для своей копии дочерний загрузчик должен искать у себя первым, как загрузчик веб-приложения в Tomcat;
- ограничения: один загрузчик не определит имя дважды, будет
LinkageErrorо дублирующем определении. Классы изjava.*пользовательский загрузчик определить не даст, получитеSecurityException.
Последствия:
ClassCastException: Foo cannot be cast to Foo: объект из одного загрузчика не приводится к одноимённому типу из другого;- две копии статики: у каждой версии свои статические поля, синглтон перестаёт быть единственным;
- утечки Metaspace: при передеплое старый загрузчик не соберётся, пока на его класс есть ссылка, например из
ThreadLocal.
Обратная сторона: если на одном classpath две версии jar, загрузится класс, найденный первым, а вызов метода из другой версии упадёт с NoSuchMethodError. Лечат выравниванием версий или релокацией пакетов через shade-плагин.
Где спотыкаются
Отвечают про разные пакеты. Это ответ на другой вопрос.
Путают два загрузчика с двумя jar на одном classpath: во втором случае вторая копия просто не будет видна.
Не могут объяснить ClassCastException с одинаковыми именами в сообщении, хотя это самый частый реальный симптом.