Это эквивалентно
class A {
// ...
A.class.getClassLoader().loadClass("B's canonical name").newInstance();
// ...
}
Нет, не всегда.
Загрузка класса выполняется только один раз для данного пространства имен, если только рассматриваемый Class
не был ранее выгружен. Следовательно, эквивалентное выражение A.class.getClassLoader().loadClass("B's canonical name")
будет выполняться только один раз в большинстве сценариев. Другими словами, если у вас есть два выражения - new A()
, loadClass
будет выполняться только один раз.
Вызов конструктора обрабатывается JVM как вызов метода, но для этого требуется сотрудничество компилятора Java. JVM и компилятор должны придерживаться раздела 3.9 Спецификации виртуальной машины Java, в котором говорится:
3.9 Специально названные методы инициализации
На уровне виртуальной машины Java каждый конструктор (§2.12)
появляется как метод инициализации экземпляра со специальным именем
<init>
. Это имя предоставлено компилятором. Потому что имя <init>
не является допустимым идентификатором, его нельзя использовать непосредственно в программе
написано на языке программирования Java. Инициализация экземпляра
методы могут быть вызваны только в виртуальной машине Java
invokespecial инструкция, и они могут быть вызваны только на
неинициализированные экземпляры классов. Метод инициализации экземпляра занимает
на разрешениях доступа (§2.7.4) конструктора, из которого он
был получен.
Класс или интерфейс имеют не более одного класса или интерфейса инициализации
метод и инициализируется (§2.17.4), вызывая этот метод.
Метод инициализации класса или интерфейса является статическим и не принимает
аргументы. У него есть специальное имя <clinit>
. Это имя предоставлено
компилятор. Поскольку имя <clinit>
не является допустимым идентификатором, оно
нельзя использовать напрямую в программе, написанной на Java
язык. Методы инициализации класса и интерфейса вызываются
неявно виртуальной машиной Java; они никогда не используются
непосредственно из любой инструкции виртуальной машины Java, но вызываются
только косвенно как часть процесса инициализации класса.
В этом разделе предполагается, что объект Class
, относящийся к рассматриваемому классу, доступен для текущего потока. Как только объект Class
станет доступен, будет вызван метод <init>
, соответствующий конструктору с правильным набором аргументов.
Вопрос о том, какой загрузчик классов будет использоваться для загрузки класса, если он еще не загружен, немного отличается и не имеет ничего общего с новым ключевым словом. Это зависит от того, как один класс ссылается на другой, т. Е. Нужно ли разрешать символьную ссылку в пуле констант времени выполнения? Поведение в этом контексте определено в Разделе 5.3 Спецификации Виртуальной машины Java:
5.3 Создание и загрузка
Создание класса или интерфейса C, обозначенного именем N, состоит из
конструкция в области методов виртуальной машины Java
(§3.5.4) специфического для реализации внутреннего представления C.
Создание класса или интерфейса запускается другим классом или интерфейсом
D, который ссылается на C через свой пул констант времени выполнения.
...
Виртуальная машина Java использует одну из трех процедур для создания класса
или интерфейс C обозначен N:
Если N обозначает не массив или интерфейс, один из двух следующих методов используется для загрузки и, тем самым, создания C:
Если D был определен загрузчиком класса начальной загрузки, тогда начальная загрузка
Загрузчик классов инициирует загрузку C (§5.3.1).
Если D был определен пользовательским загрузчиком классов, то это то же самое
пользовательский загрузчик классов инициирует загрузку C (§5.3.2).
В противном случае N обозначает класс массива. Класс массива создается непосредственно виртуальной машиной Java (§5.3.3), а не загрузчиком классов.
Howevто есть загрузчик определяющего класса D используется в процессе
создание массива класса C.
Обратите внимание на предложение - If D was defined by a user-defined class loader, then that same user-defined class loader initiates loading of C
в приведенной выше цитате. В контексте выражения new A()
загрузчик классов, который загрузил включающий класс, будет отвечать за загрузку A
в соответствии со спецификацией VM; это, конечно, при условии, что включающий класс не загружается загрузчиком классов начальной загрузки.