Подтвердить что ты не робот

Что делает "новое" в Java w.r.t. класс загрузчика?

Я не могу легко найти его в JLS/JVMSpec или в SO. Я уверен, что его, должно быть, спросили...

Итак, что делает "новое" на самом деле? Предположим, что мы создаем класс B в A:

class A {
    // ...
    new B();
    // ...
}

Это эквивалентно

class A {
    // ...
    A.class.getClassLoader().loadClass("B canonical name").newInstance();
    // ...
}

?

Это или не работает так, как в любой среде?

Буду признателен, если вы можете указать мне соответствующую главу в JLS/JVMSpec. Спасибо!

Изменить: мы не можем вызвать вызов B.class.getCanonicalName() в loadClass(), так как B еще не загружен. JVM должен разрешить имя на основе оператора импорта.

4b9b3361

Ответ 1

Это эквивалентно

class A {
    // ...
    A.class.getClassLoader().loadClass("B canonical name").newInstance();
    // ...
}

?

Нет, не всегда.

Загрузка класса выполняется только один раз для заданного пространства имен, если только вопрос Class не был ранее выгружен. Поэтому эквивалентное выражение A.class.getClassLoader().loadClass("B canonical name") будет выполняться только один раз в большинстве сценариев. Другими словами, если у вас есть два выражения - new A(), loadClass будет выполняться только один раз.

Вызов конструктора рассматривается как вызов метода JVM, но для этого требуется сотрудничество Java-компилятора. JVM и компилятор должны придерживаться раздела 3.9 спецификации виртуальной машины Java, в котором говорится:

3.9 Специально названные методы инициализации

На уровне виртуальной машины Java каждый конструктор (§2.12) появляется как метод инициализации экземпляра, который имеет специальное имя <init>. Это имя предоставляется компилятором. Поскольку имя <init>не является допустимым идентификатором, его нельзя использовать непосредственно в программе написанных на языке программирования Java. Инициализация экземпляра методы могут быть вызваны только в виртуальной машине Java посредством вызывает специальную инструкцию, и они могут быть вызваны только на неинициализированные экземпляры классов. Метод инициализации экземпляра принимает на правах доступа (§ 2.7.4) конструктора, из которого он.

Класс или интерфейс имеет не более одного класса или инициализации интерфейса метод и инициализируется (§ 2.17.4), вызывая этот метод. метод инициализации класса или интерфейса статичен и не принимает аргументы. Он имеет специальное имя <clinit>. Это имя предоставляется компилятор. Поскольку имя <clinit> не является допустимым идентификатором, оно не может использоваться непосредственно в программе, написанной на Java-программировании язык. Вызываются методы инициализации класса и интерфейса неявно с помощью виртуальной машины Java; они никогда не вызываются непосредственно из любой инструкции виртуальной машины Java, но вызывается только косвенно, как часть процесса инициализации класса.

В этом разделе предполагается, что объект Class, относящийся к рассматриваемому классу, доступен для текущего потока. Как только объект Class доступен, будет вызван метод <init>, соответствующий конструктору с правильным набором аргументов.

Вопрос о том, какой classloader будет использоваться для загрузки класса, если он еще не загружен, немного отличается и не имеет никакого отношения к новому ключевому слову. Это зависит от того, как один класс ссылается на другой, т.е. Нужно ли разрешать символическую ссылку в пуле постоянной среды выполнения? Поведение в этом контексте определено в разделе 5.3 Спецификации виртуальной машины Java:

5.3 Создание и загрузка

Создание класса или интерфейса C, обозначаемого именем N, состоит из построение в области метода виртуальной машины Java (П. 3.5.4) внутреннего представления C. Создание класса или интерфейса запускается другим классом или интерфейсом D, который ссылается на C через свой постоянный пул времени выполнения.

...

Виртуальная машина Java использует одну из трех процедур для создания класса или интерфейс C, обозначаемый N:

  • Если N обозначает класс nonarray или интерфейс, один из двух следующих методов используется для загрузки и тем самым создает C:

    • Если D был определен загрузчиком класса bootstrap, тогда загрузочный блок class loader инициирует загрузку C (§5.3.1).

    • Если D был определен пользовательским загрузчиком классов, то тот же самый пользовательский загрузчик классов инициирует загрузку C (§5.3.2).

  • В противном случае N обозначает класс массива. Класс массива создается непосредственно виртуальной машиной Java (§5.3.3), а не загрузчиком классов. Однако определяющий загрузчик классов 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 Spec; это, конечно, предполагается, что класс-оболочка не загружается загрузчиком классов bootstrap.

Ответ 2

Чтобы следить за моим комментарием, строка типа

new A()

переводится на

0:  new #2; //class A
3:  dup
4:  invokespecial   #3; //Method A."<init>":()V
7:  pop

И stacktrace:

  [1] java.net.URLClassLoader$1.run (URLClassLoader.java:202)
  [2] java.security.AccessController.doPrivileged (native method)
  [3] java.net.URLClassLoader.findClass (URLClassLoader.java:190)
  [4] sun.misc.Launcher$ExtClassLoader.findClass (Launcher.java:229)
  [5] java.lang.ClassLoader.loadClass (ClassLoader.java:307)
  [6] java.lang.ClassLoader.loadClass (ClassLoader.java:296)
  [7] sun.misc.Launcher$AppClassLoader.loadClass (Launcher.java:301)
  [8] java.lang.ClassLoader.loadClass (ClassLoader.java:248)
  [9] Loader.main (Loader.java:11)

Итак, я думаю, вы были довольно близки в своей догадке.

Ответ 3

Я нашел эту ссылку, которая в основном объясняет концепцию "нового" оператора в Java. Основная идея для меня - это фраза:

"(..) Новый оператор создает экземпляр класса, выделяя память для нового объекта и возвращая ссылку на эту память. Новый оператор также вызывает конструктор объекта (..)"

Я думаю, что нужно учитывать три вещи:

  • "Новый" оператор распространен в каждой среде, но из-за необходимости разработчиков. В случае Java "новый" оператор также выделяет пространство памяти для объекта.
  • Иногда (в старых компиляторах) он недоступен, и все объявления должны были быть сделаны "длинным путем". Таким образом, для ретро-совместимости оба утверждения эквивалентны.
  • Всегда бывает, что вам может потребоваться переопределить "loadClass" или "getClassLoader()"

"(..) Несмотря на то, что ключевое слово Java новое является ключевым для языка, могут быть лучшие способы выполнения задания. (..)"

Надеюсь, что это поможет;)