Что «новое» делает в Java w.r.t. класс загрузчик? - PullRequest
19 голосов
/ 10 октября 2011

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

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

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

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

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

?

Работает ли это или не работает в каждомenvironment?

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

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

Ответы [ 3 ]

15 голосов
/ 10 октября 2011

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

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; это, конечно, при условии, что включающий класс не загружается загрузчиком классов начальной загрузки.

3 голосов
/ 10 октября 2011

Чтобы прокомментировать мой комментарий, строка типа

new A()

преобразуется в

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

И трассировка стека:

  [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)

Так что я думаюВы были довольно близки в своем предположении.

0 голосов
/ 10 октября 2011

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

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

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

  1. Оператор" new "распространен в любой среде, но из-за необходимости разработчиковдля этого.В случае Java оператор «new» также выделяет пространство памяти для объекта.
  2. Иногда (в старых компиляторах) он был недоступен, и все объявления приходилось делать «длинным путем».Таким образом, для обеспечения ретро-совместимости оба утверждения эквивалентны.
  3. Всегда есть случай, когда вам может понадобиться переопределить «loadClass» или «getClassLoader ()»

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...