![]() |
Spec-Zone .ru
спецификации, руководства, описания, API
|
Содержание | Предыдущий | Следующий | Индекс | Спецификация языка Java Третий Выпуск |
ГЛАВА 12
Эта глава определяет действия, которые происходят во время выполнения программы. Это организуется вокруг жизненного цикла виртуальной машины Java и классов, интерфейсов, и возражает, что формируют программу.
Виртуальная машина Java запускает, загружая указанный класс и затем вызывая метод main
в этом указанном классе. Разделите §12.1, обрисовывает в общих чертах загрузку, соединение, и шаги инициализации, включенные в выполнение main
, как введение в понятия в этой главе. Дальнейшие разделы определяют детали загрузки (§12.2), соединяясь (§12.3), и инициализация (§12.4).
Глава продолжается со спецификацией процедур для создания новых экземпляров класса (§12.5); и завершение экземпляров класса (§12.6). Это заканчивается, описывая разгрузку классов (§12.7) и процедура, сопровождаемая, когда программа выходит (§12.8).
main
из некоторого указанного класса, передавая это единственный параметр, который является массивом строк. В примерах в этой спецификации обычно вызывают этот первый класс Test
.Точная семантика запуска виртуальной машины дается в главе 5 Спецификации виртуальной машины Java, Втором Выпуске. Здесь мы представляем краткий обзор процесса с точки зрения языка программирования Java.
Способ, которым начальный класс определяется к виртуальной машине Java, выходит за рамки этой спецификации, но это типично в средах узла, которые используют командные строки для полностью определенного имени класса, который будет определен как параметр командной строки и для следующих параметров командной строки, которые будут использоваться в качестве строк, которые будут обеспечены как параметр методу main
. Например, в реализации UNIX, командной строке:
будет обычно запускать виртуальную машину Java, вызывая методjava Test reboot Bob Dot Enzo
main
из класса Test
(класс в неназванном пакете), передавая это массив, содержащий четыре строки "reboot"
, "Bob"
, "Dot"
, и "Enzo"
.Мы теперь обрисовываем в общих чертах шаги, которые виртуальная машина может сделать, чтобы выполниться Test
, как пример загрузки, соединения, и процессов инициализации, которые описываются далее в более поздних разделах.
main
из класса Test
обнаруживает что класс Test
не загружается - то есть, что виртуальная машина в настоящий момент не содержит двоичное представление для этого класса. Виртуальная машина тогда использует загрузчик класса, чтобы попытаться найти такое двоичное представление. Если этот процесс перестал работать, то ошибка бросается. Этот процесс загрузки описывается далее в §12.2.Test
загружается, это должно быть инициализировано прежде main
может быть вызван. И Test
, как все (класс или интерфейс) типы, должен быть соединен прежде, чем это будет инициализировано. Соединение включает проверку, подготовку и (дополнительно) разрешение. Соединение описывается далее в §12.3.Проверка проверяет что загруженное представление Test
правильно построено, с надлежащей таблицей символов. Проверка также проверяет, что код, который реализует Test
повинуется семантическим требованиям языка программирования Java и виртуальной машины Java. Если проблема обнаруживается во время проверки, то ошибка бросается. Проверка описывается далее в §12.3.1.
Подготовка включает выделение статического хранения и любых структур данных, которые используются внутренне виртуальной машиной, такой как таблицы метода. Подготовка описывается далее в §12.3.2.
Разрешение является процессом проверки символьных ссылок отTest
к другим классам и интерфейсам, загружая другие классы и интерфейсы, которые упоминаются и проверяя, что ссылки корректны.
Шаг разрешения является дополнительным во время начального редактирования. Реализация может разрешить символьные ссылки от класса или интерфейса, который соединяется очень рано, даже на грани разрешения всех символьных ссылок от классов и интерфейсов, на которые далее ссылаются, рекурсивно. (Это разрешение может привести к ошибкам от эти дальнейшая загрузка и соединение шагов.) Этот выбор реализации представляет одно экстремальное значение и подобен виду "статического" редактирования, которое было сделано много лет в простых реализациях языка C. (В этих реализациях скомпилированная программа обычно представляется как"a.out
"файл, который содержит полностью соединенную версию программы, включая полностью разрешенные ссылки к библиотечным подпрограммам, используемым программой. Копии этих библиотечных подпрограмм включаются в"a.out
"файл.)
Реализация может вместо этого хотеть разрешать символьную ссылку только, когда она активно используется; непротиворечивое использование этой стратегии всех символьных ссылок представило бы "самую ленивую" форму разрешения.
В этом случае, если Test
имел несколько символьных ссылок на другой класс, тогда ссылки могли бы быть разрешены по одному, поскольку они используются, или возможно нисколько, если эти ссылки никогда не использовались во время выполнения программы.
Test
или любой из далее, рекурсивно ссылаемый, классы и интерфейсы. В системе, которая реализовывала "самое ленивое" разрешение, эти ошибки будут брошены только, когда неправильная символьная ссылка будет активно использоваться.Процесс разрешения описывается далее в §12.3.3.
main
из класса Test
. Это разрешается, только если класс был инициализирован (§12.4.1).Инициализация состоит из выполнения любых инициализаторов переменной класса и статических инициализаторов класса Test
, в текстовом порядке. Но прежде Test
может быть инициализирован, его прямой суперкласс должен быть инициализирован, так же как прямой суперкласс его прямого суперкласса, и так далее, рекурсивно. В самом простом случае, Test
имеет Object
как его неявный прямой суперкласс; если класс Object
еще не был инициализирован, тогда это должно быть инициализировано прежде Test
инициализируется. Класс Object
не имеет никакого суперкласса, таким образом, рекурсия завершается здесь.
Если класс Test
имеет другой класс Super
как его суперкласс, тогда Super
должен быть инициализирован прежде Test
. Это требует загрузки, проверки, и подготовки Super
если это не было уже сделано и, в зависимости от реализации, может также включить разрешение символьных ссылок от Super
и так далее, рекурсивно.
Инициализация может таким образом вызвать загрузку, соединение, и ошибки инициализации, включая такие ошибки, включая другие типы.
Процесс инициализации описывается далее в §12.4.
Test
(во время которого другая последовательная загрузка, соединение, и инициализация, возможно, произошли), метод main
из Test
вызывается.
Метод main
должен быть объявлен public
, static
, и void
. Это должно принять единственный параметр, который является массивом строк. Этот метод может быть объявлен как также
илиpublic static void main(String[] args)
public static void main(String... args)
Class
объект представить класс или интерфейс.Точная семантика загрузки дается в главе 5 Спецификации виртуальной машины Java (всякий раз, когда мы обращаемся к спецификации виртуальной машины Java в этой книге, мы имеем в виду второй выпуск, как исправлено JSR 924). Здесь мы представляем краткий обзор процесса с точки зрения языка программирования Java.
Двоичный формат класса или интерфейса обычно class
формат файла, описанный в Спецификации виртуальной машины Java, процитированной выше, но другие форматы, возможен, если они удовлетворяют требования, определенные в §13.1. Метод defineClass
из класса ClassLoader
может использоваться, чтобы создать Class
объекты от двоичных представлений в class
формат файла.
Загрузчики класса хорошего поведения поддерживают эти свойства:
Для дальнейшего обсуждения этих проблем см. Спецификацию виртуальной машины Java и бумагу Динамический Класс, Загружающийся в виртуальной машине Java, Шенгом Лиэнгом и Джилэдом Брэчей, в Продолжениях OOPSLA '98, опубликованный как ACM SIGPLAN Уведомления, Объем 33, Номер 10, октябрь 1998, страницы 36-44. Основной принцип проекта языка программирования Java - то, что система типов времени выполнения не может ниспровергаться кодом, записанным на языке, не даже реализациями таких иначе чувствительных системных классов как ClassLoader
и SecurityManager
.
ClassLoader
и его подклассы. Различные подклассы ClassLoader
может реализовать различные политики загрузки. В частности загрузчик класса может кэшировать двоичные представления классов и интерфейсов, выбрать их с упреждением основанный на ожидаемом использовании, или загрузить группу связанных классов вместе. Эти действия, возможно, не абсолютно прозрачны к рабочему приложению, если, например, недавно скомпилированная версия класса не находится, потому что более старая версия кэшируется загрузчиком класса. Это - ответственность загрузчика класса, однако, чтобы отразить загружающиеся ошибки только в точках в программе, они, возможно, возникли, не выбирая с упреждением или групповая загрузка.
Если ошибка происходит во время загрузки класса, то экземпляр одного из следующих подклассов класса LinkageError
будет брошен в любую точку в программе, которая (прямо или косвенно) использует тип:
ClassCircularityError
: Класс или интерфейс не могли быть загружены, потому что это будет свой собственный суперкласс или суперинтерфейс (§13.4.4).
ClassFormatError
: Двоичные данные, который подразумевает определять требуемый скомпилированный класс или интерфейс, уродливы.
NoClassDefFoundError
: Никакое определение для требуемого класса или интерфейса не могло быть найдено соответствующим загрузчиком класса. OutOfMemoryError
. Три различных действия включаются в соединение: проверка, подготовка, и разрешение символьных ссылок. Точная семантика соединения дается в главе 5 Спецификации виртуальной машины Java, Втором Выпуске. Здесь мы представляем краткий обзор процесса с точки зрения языка программирования Java.
Эта спецификация позволяет гибкость реализации относительно того, когда соединение действий (и, из-за рекурсии, загрузка) имеют место, при условии, что семантику языка уважают, что класс или интерфейс полностью проверяются и готовятся прежде, чем это будет инициализировано, и что ошибки, обнаруженные во время редактирования, бросаются в точку в программе, где некоторые меры предпринимаются программой, которая могла бы потребовать редактирования к классу или интерфейсу, включенному в ошибку.
Например, реализация может хотеть разрешать каждую символьную ссылку в классе или взаимодействовать через интерфейс индивидуально, только когда это используется (ленивое или последнее разрешение), или разрешить их внезапно, в то время как класс проверяется (статическое разрешение). Это означает, что процесс разрешения может продолжаться в некоторых реализациях, после того, как класс или интерфейс был инициализирован.
Поскольку соединение включает выделение новых структур данных, это может перестать работать с OutOfMemoryError
.
Для спецификации процесса проверки см. отдельный объем этого ряда, Спецификации виртуальной машины Java. и спецификация J2ME Соединенная Ограниченная Конфигурация Устройства, версия 1.1.
Если ошибка происходит во время проверки, то экземпляр следующего подкласса класса LinkageError
будет брошен в точку в программе, которая заставила класс быть проверенным:
VerifyError
: Двоичное определение для класса или интерфейса, отказавшего, чтобы передать ряд необходимых проверок, чтобы проверить, что это повинуется семантике языка виртуальной машины Java и что это не может нарушить целостность виртуальной машины Java. (См. §13.4.2, §13.4.4, §13.4.9, и §13.4.17 для некоторых примеров.) static
поля (переменные класса и константы) для класса или интерфейса и инициализации таких полей к значениям по умолчанию (§4.12.5). Это не требует выполнения любого исходного кода; явные инициализаторы для static
поля выполняются как часть инициализации (§12.4), не подготовка.Во время для подготовки реализации виртуальной машины Java могут предварительно вычислить дополнительные структуры данных, чтобы сделать более поздние операции на классе или интерфейсе более эффективными. Одна особенно полезная структура данных является "таблицей метода" или другой структурой данных, которая позволяет любому методу быть вызванным на экземпляры класса, не требуя поиска суперклассов во время вызова.
Прежде, чем символьная ссылка может использоваться, она должна подвергнуться разрешению, в чем символьная ссылка проверяется, чтобы быть корректной и, обычно, заменяется прямой ссылкой, которая может быть более эффективно обработана, если ссылка неоднократно используется.
Если ошибка произойдет во время разрешения, то ошибка будет брошена. Наиболее обычно это будет экземпляром одного из следующих подклассов класса IncompatibleClassChangeError
, но это может также быть экземпляр некоторого другого подкласса IncompatibleClassChangeError
или даже экземпляр класса IncompatibleClassChangeError
непосредственно. Эта ошибка может быть брошена в любую точку в программе, которая использует символьную ссылку на тип, прямо или косвенно:
IllegalAccessError
: С символьной ссылкой встретились, который определяет использование или присвоение поля, или вызов метода, или создание экземпляра класса, к которому у кода, содержащего ссылку, нет доступа, потому что поле или метод были объявлены private
, protected
, или доступ по умолчанию (нет public
), или потому что класс не был объявлен public
.
Это может произойти, например, если поле, которое первоначально объявляется public
изменяется, чтобы быть private
после того, как другой класс, который обращается к полю, был скомпилирован (§13.4.7).
InstantiationError
: С символьной ссылкой встретились, который используется в выражении создания экземпляра класса, но экземпляр не может быть создан, потому что ссылка, оказывается, обращается к интерфейсу или к abstract
класс.
Это может произойти, например, если класс, который является первоначально нет abstract
изменяется, чтобы быть abstract
после того, как другой класс, который обращается к рассматриваемому классу, был скомпилирован (§13.4.1).
NoSuchFieldError
: С символьной ссылкой встретились, который обращается к определенному полю определенного класса или интерфейса, но класс или интерфейс не содержат поле того имени.
Это может произойти, например, если полевое объявление было удалено из класса после другого класса, который обращается к полю, был скомпилирован (§13.4.8).
NoSuchMethodError
: С символьной ссылкой встретились, который обращается к определенному методу определенного класса или интерфейса, но класс или интерфейс не содержат метод той подписи.
Это может произойти, например, если объявление метода было удалено из класса после другого класса, который обращается к методу, был скомпилирован (§13.4.12).
UnsatisfiedLinkError
(подкласс LinkageError
) может быть брошен, если класс объявляет a native
метод, для которого не может быть найдена никакая реализация. Ошибка произойдет, если метод будет использоваться, или ранее, в зависимости от того, какая стратегия разрешения используется виртуальной машиной (§12.3).static
поля (переменные класса) объявленный в классе. Инициализация интерфейса состоит из выполнения инициализаторов для полей (константы), объявленные там.Прежде, чем класс инициализируется, его суперкласс должен быть инициализирован, но интерфейсы, реализованные классом, не инициализируются. Точно так же суперинтерфейсы интерфейса не инициализируются прежде, чем интерфейс инициализируется.
Прежде, чем класс инициализируется, его прямой суперкласс должен быть инициализирован, но интерфейсы, реализованные классом, не должны быть инициализированы. Точно так же суперинтерфейсы интерфейсной потребности не быть инициализированными перед интерфейсом инициализируются.
Класс или интерфейсный тип T будут сразу инициализированы перед первым возникновением любого из следующего:
assert
оператор (§14.10) лексически вложенный в пределах T выполняется. Class
и в пакете java.lang.reflect
также класс причин или интерфейсная инициализация. Класс или интерфейс не будут инициализированы ни при каком другом обстоятельстве.Намерение здесь состоит в том, что у класса или интерфейсного типа есть ряд инициализаторов, которые помещают это в непротиворечивое состояние, и что это состояние является первым состоянием, которое наблюдается другими классами. Статические инициализаторы и инициализаторы переменной класса выполняются в текстовом порядке, и, возможно, не обращаются к переменным класса, объявленным в классе, объявления которого появляются дословно после использования, даже при том, что эти переменные класса находятся в контексте (§8.3.2.3). Это ограничение разрабатывается, чтобы обнаружить, во время компиляции, наиболее круговой или иначе уродливые инициализации.
Как показано в примере в §8.3.2.3, факт, что код инициализации неограничен, позволяет примерам быть созданными, где значение переменной класса может наблюдаться, когда у этого все еще есть свое начальное значение по умолчанию, прежде, чем ее выражение инициализации будет оценено, но такие примеры редки практически. (Такие примеры могут быть также созданы например переменная инициализация; см. пример в конце §12.5). Полная мощность языка доступна в этих инициализаторах; программисты должны осуществить некоторую заботу. Это питание помещает дополнительное бремя в генераторы кода, но это бремя возникло бы в любом случае, потому что язык параллелен (§12.4.3).
Прежде, чем класс инициализируется, его суперклассы инициализируются, если они не были ранее инициализированы.
Таким образом, тестовая программа:
печатные издания:class Super { static { System.out.print("Super "); } } class One { static { System.out.print("One "); } } class Two extends Super { static { System.out.print("Two "); } } class Test { public static void main(String[] args) { One o = null; Two t = new Two(); System.out.println((Object)o == (Object)t); } }
Super Two false
Класс One
никогда не инициализируется, потому что это не используемый активно и поэтому никогда не соединяется с. Класс Two
инициализируется только после его суперкласса Super
был инициализирован.
Ссылка на поле класса вызывает инициализацию только класса, или взаимодействуйте через интерфейс, это фактически объявляет это, даже при том, что это могло бы быть упомянуто через имя подкласса, подынтерфейса, или класса, который реализует интерфейс.
Тестовая программа:
печатные издания только:class Super { static int taxi = 1729; } class Sub extends Super { static { System.out.print("Sub "); } } class Test { public static void main(String[] args) { System.out.println(Sub.taxi); } }
потому что класс1729
Sub
никогда не инициализируется; ссылка на Sub.taxi
ссылка на поле, фактически объявленное в классе Super
и не инициировал инициализацию класса Sub
.Инициализация интерфейса не делает, себя, вызывает инициализацию любого из ее суперинтерфейсов.
Таким образом, тестовая программа:
производит вывод:interface I { int i = 1, ii = Test.out("ii", 2); } interface J extends I { int j = Test.out("j", 3), jj = Test.out("jj", 4); } interface K extends J { int k = Test.out("k", 5); } class Test { public static void main(String[] args) { System.out.println(J.i); System.out.println(K.j); } static int out(String s, int i) { System.out.println(s + "=" + i); return i; } }
1 j=3 jj=4 3
Ссылка на J.i
к полю, которое является постоянным временем компиляции; поэтому, это не вызывает I
быть инициализированным. Ссылка на K.j
ссылка на поле, фактически объявленное в интерфейсе J
это не постоянное время компиляции; это вызывает инициализацию полей интерфейса J
, но не таковые из его суперинтерфейса I
, ни таковые из интерфейса K
. Несмотря на то, что имя K
используется, чтобы обратиться к полю j
из интерфейса J
, интерфейс K
не инициализируется.
Class
объект был уже проверен и подготовлен, и что Class
объект содержит состояние, которое указывает на одну из четырех ситуаций:
Class
объект проверяется и готовится, но не инициализируется.
Class
объект инициализируется некоторым определенным потоком T.
Class
объект полностью инициализируется и готов к употреблению.
Class
объект находится в ошибочном состоянии, возможно потому что инициализация была предпринята и отказавшей.
Class
объект, который представляет класс или интерфейс, который будет инициализирован. Это включает ожидание, пока текущий поток не может получить блокировку для того объекта (§17.1).
wait
на этом Class
объект (который временно выпускает блокировку). Когда текущий поток просыпается от wait
, повторите этот шаг.
Class
возразите и полный обычно.
Class
возразите и полный обычно.
Class
объект находится в ошибочном состоянии, тогда инициализация не возможна. Выпустите блокировку на Class
объект и бросок a NoClassDefFoundError
.
Class
объект теперь происходит текущим потоком, и выпустите блокировку на Class
объект.
Class
объект представляет класс, а не интерфейс, и суперкласс этого класса еще не был инициализирован, тогда рекурсивно выполните эту всю процедуру для суперкласса. В случае необходимости проверьте и подготовьте суперкласс сначала. Если инициализация суперкласса завершается резко из-за выданного исключения, то заблокируйте это Class
объект, маркируйте это ошибочным, уведомьте все потоки ожидания, выпустите блокировку, и завершитесь резко, выдавая то же самое исключение, которое следовало из инициализации суперкласса.
final
переменные класса и поля интерфейсов, значения которых являются константами времени компиляции, инициализируются сначала (§8.3.2.1, §9.3.1, §13.4.9).
Class
объект, маркируйте, он полностью инициализировал, уведомьте все потоки ожидания, выпустите блокировку, и обычно завершайте эту процедуру.
Error
или один из его подклассов, затем создайте новый экземпляр класса ExceptionInInitializerError
, с E как параметр, и использование этот объект вместо E в следующем шаге. Но если новый экземпляр ExceptionInInitializerError
не может быть создан потому что OutOfMemoryError
происходит, тогда вместо этого используйте OutOfMemoryError
объект вместо E в следующем шаге.
Class
объект, маркируйте это ошибочным, уведомьте все потоки ожидания, выпустите блокировку, и завершите эту процедуру резко с причиной E или ее заменой как определено в предыдущем шаге. (Из-за дефекта в некоторых ранних реализациях, исключение во время инициализации класса было проигнорировано, вместо того, чтобы вызвать ExceptionInInitializerError
как описано здесь.)
Class
объект полностью инициализируется и готов к употреблению, тогда вызов процедуры инициализации больше не необходим, и это может быть устранено из кода например, исправляя это или иначе регенерируя код.Анализ времени компиляции может, в некоторых случаях, быть в состоянии устранить многие из проверок, что тип был инициализирован от сгенерированного кода, если порядок инициализации на группу связанных типов может быть определен. Такой анализ должен, однако, полностью учесть параллелизм и для факта, что код инициализации неограничен.
Новый экземпляр класса может быть неявно создан в следующих ситуациях:
String
литерал (§3.10.5) может создать новое String
объект представить тот литерал. (Это не могло бы произойти если то же самое String
был ранее интернирован (§3.10.5).)
String
объект представить результат. Операторы конкатенации строк могут также создать временные объекты обертки для значения типа примитива.
Всякий раз, когда новый экземпляр класса создается, место в памяти выделяется для него с комнатой для всех переменных экземпляра, объявленных в типе класса и всех переменных экземпляра, объявленных в каждом суперклассе типа класса, включая все переменные экземпляра, которые могут быть скрыты (§8.3). Если нет достаточного пространства, доступного, чтобы выделить память для объекта, то создание экземпляра класса завершается резко с OutOfMemoryError
. Иначе, все переменные экземпляра в новом объекте, включая объявленных в суперклассах, инициализируются к их значениям по умолчанию (§4.12.5).
Непосредственно перед тем, как ссылка на недавно создаваемый объект возвращается как результат, обозначенный конструктор обрабатывается, чтобы инициализировать новый объект, используя следующую процедуру:
this
), затем оцените параметры и обработайте тот вызов конструктора, рекурсивно используя эти те же самые пять шагов. Если тот вызов конструктора завершается резко, то эта процедура завершается резко по той же самой причине; иначе, продолжайте с шагом 5.
this
). Если этот конструктор для класса кроме Object
, тогда этот конструктор начнет с явного или неявного вызова конструктора суперкласса (использование super
). Оцените параметры и обработайте тот вызов конструктора суперкласса, рекурсивно используя эти те же самые пять шагов. Если тот вызов конструктора завершается резко, то эта процедура завершается резко по той же самой причине. Иначе, продолжайте с шагом 4.
В примере:
новый экземплярclass Point { int x, y; Point() { x = 1; y = 1; } } class ColoredPoint extends Point { int color = 0xFF00FF; } class Test { public static void main(String[] args) { ColoredPoint cp = new ColoredPoint(); System.out.println(cp.color); } }
ColoredPoint
создается. Во-первых, место выделяется для нового ColoredPoint
, содержать поля x
, y
, и color
. Все эти поля тогда инициализируются к их значениям по умолчанию (в этом случае, 0
для каждого поля). Затем, ColoredPoint
конструктор без параметров сначала вызывается. С тех пор ColoredPoint
не объявляет конструкторов, конструктора по умолчанию формы:
обеспечивается для этого автоматически компилятором Java.ColoredPoint() { super(); }
Этот конструктор тогда вызывает Point
конструктор без параметров. Point
конструктор не начинает с вызова конструктора, таким образом, компилятор обеспечивает неявный вызов своего конструктора суперкласса никаких параметров, как если бы он был записан:
Поэтому, конструктор дляPoint() { super(); x = 1; y = 1; }
Object
то, который не берет параметров, вызывается.Класс Object
не имеет никакого суперкласса, таким образом, рекурсия завершается здесь. Затем, любые инициализаторы экземпляра, инициализаторы переменной экземпляра Object
вызываются. Затем, тело конструктора Object
это не берет параметров, выполняется. Никакой такой конструктор не объявляется в Object
, таким образом, компилятор предоставляет по умолчанию, который в этом особом случае является:
Этот конструктор выполняется без эффекта и возвратов.Object() { }
Затем, все инициализаторы для переменных экземпляра класса Point
выполняются. Как это происходит, объявления x
и y
не обеспечивайте выражения инициализации, таким образом, никакое действие не требуется для этого шага примера. Затем тело Point
конструктор выполняется, устанавливая x
к 1
и y
к 1
.
Затем, инициализаторы для переменных экземпляра класса ColoredPoint
выполняются. Этот шаг присваивает значение 0xFF00FF
к color
. Наконец, остальная часть тела ColoredPoint
конструктор выполняется (часть после вызова super
); оказывается, нет никаких операторов в остальной части тела, таким образом, никакое дальнейшее действие не требуется, и инициализация полна.
В отличие от C++, язык программирования Java не определяет, что измененные правила для метода диспетчеризируют во время создания нового экземпляра класса. Если методы вызываются, которые переопределяются в подклассах в инициализируемом объекте, то эти методы переопределения используются, даже прежде, чем новый объект полностью инициализируется. Таким образом, компилируя и выполняя пример:
class Super { Super() { printThree(); } void printThree() { System.out.println("three"); } } class Test extends Super { int three = (int)Math.PI; // That is, 3 public static void main(String[] args) { Test t = new Test(); t.printThree(); } void printThree() { System.out.println(three); } }
Это показывает что вызов0
3
printThree
в конструкторе для класса Super
не вызывает определение printThree
в классе Super
, а скорее вызывает определение переопределения printThree
в классе Test
. Этот метод поэтому работает перед полевыми инициализаторами Test
были выполнены, который является, почему первый вывод значения 0
, значение по умолчанию то, к который поле three
из Test
инициализируется. Более поздний вызов printThree
в методе main
вызывает то же самое определение printThree
, но той точкой инициализатор например переменная three
был выполнен, и таким образом, значение 3
печатается.См. §8.8 для большего количества деталей об объявлениях конструктора.
Object
имеет a protected
метод вызывают finalize
; этот метод может быть переопределен другими классами. Определенное определение finalize
это может быть вызвано для объекта, вызывается финализатором того объекта. Прежде, чем хранение для объекта исправляется сборщиком "мусора", виртуальная машина Java вызовет финализатор того объекта.Финализаторы обеспечивают шанс освободить ресурсы, которые не могут быть освобождены автоматически автоматическим менеджером по хранению. В таких ситуациях, просто исправляя память, используемую объектом, не гарантировал бы, что ресурсы, которые она содержала, будут исправлены.
Язык программирования Java не определяет, как скоро финализатор будет вызван, кроме сказать, что это произойдет прежде, чем хранение для объекта снова используется. Кроме того, язык не определяет, какой поток вызовет финализатор для любого данного объекта. Гарантируется, однако, что поток, который вызывает финализатор, не будет содержать видимых пользователем блокировок синхронизации, когда финализатор будет вызван. Если непойманное исключение выдается во время завершения, исключение игнорируется, и завершение того объекта завершается.
Завершение конструктора объекта происходит - прежде (§17.4.5) выполнение finalize
метод (в формальном смысле происходит - прежде).
Обсуждение
Важно отметить, что много потоков финализатора могут быть активными (это иногда необходимо на больших многопроцессорных системах совместно используемой памяти), и что, если большая соединенная структура данных становится мусором, все завершить методы для каждого объекта в той структуре данных могли быть вызваны одновременно, каждый вызов финализатора, работающий в различном потоке.
finalize
метод объявляется в классе Object
не предпринимает мер. Факт тот класс Object
объявляет a finalize
метод означает что finalize
метод для любого класса может всегда вызывать finalize
метод для его суперкласса. Это должно всегда делаться, если это не намерение программиста аннулировать действия финализатора в суперклассе. (В отличие от конструкторов, финализаторы автоматически не вызывают финализатор для суперкласса; такой вызов должен быть кодирован явно.)
Для эффективности реализация может отследить классы, которые не переопределяют finalize
метод класса Object
, или переопределите это тривиальным способом, таким как:
protected void finalize() throws Throwable { super.finalize(); }
Мы поощряем реализации обрабатывать такие объекты как наличие финализатора, который не переопределяется, и завершить их более эффективно, как описано в §12.6.1.
Финализатор может быть вызван явно, точно так же как любой другой метод.Пакет java.lang.ref
описывает слабые ссылки, которые взаимодействуют со сборкой "мусора" и завершением. Как с любым API, у которого есть специальные взаимодействия с языком, конструкторы должны быть осведомлены о любых требованиях, наложенных java.lang.ref
API. Эта спецификация не обсуждает слабые ссылки всегда. Читатели относятся в документацию API для деталей.
Достижимый объект является любым объектом, к которому можно получить доступ в любом потенциальном вычислении продолжения от любого живого потока. Оптимизация преобразований программы может быть разработана, которые сокращают количество объектов, которые достижимы, чтобы быть меньше чем те, которых наивно считали бы достижимыми. Например, компилятор или генератор кода могут хотеть устанавливать переменную или параметр, который больше не привыкнет к null
вызвать хранение для такого объекта быть потенциально исправимым скорее.
Обсуждение
Другой пример этого происходит, если значения в полях объекта сохранены в регистрах. Программа может тогда получить доступ к регистрам вместо объекта, и никогда не получать доступ к объекту снова. Это подразумевало бы, что объект является мусором.
Отметьте, что этот вид оптимизации только позволяется, если ссылки находятся на стеке, не сохраненном в "куче".
Например, рассмотрите образец Опекуна Финализатора:
Силы опекуна финализатораclass Foo { private final Object finalizerGuardian = new Object() { protected void finalize() throws Throwable { /* finalize outer Foo object */ } } }
super.finalize
быть вызванным, если подкласс переопределения завершают и явно не вызывают super.finalize
.
Если эта оптимизация позволяется для ссылок, которые сохранены на "куче", то компилятор может обнаружить что finalizerGuardian
поле никогда не читается, обнулите его, соберите объект сразу, и вызовите финализатор рано. Это работает в противоречии с намерением: программист, вероятно, хотел вызвать Foo
финализатор, когда Foo
экземпляр стал недостижимым. Этот вид преобразования является поэтому не законным: внутренний объект класса должен быть достижимым столько, сколько внешний объект класса достижим.
Преобразования этого вида могут привести к вызовам finalize
метод, происходящий ранее чем, мог бы иначе ожидаться. Чтобы позволить пользователю предотвращать это, мы осуществляем понятие, что синхронизация может поддержать объект. Если финализатор объекта может привести к синхронизации на том объекте, то тот объект должен быть живым и продуманный достижимый всякий раз, когда блокировка сохранена на этом.
Отметьте, что это не предотвращает устранение синхронизации: синхронизация только поддерживает объект, если финализатор мог бы синхронизироваться на ней. Так как финализатор происходит в другом потоке, во многих случаях синхронизация не могла быть удалена так или иначе.
Достижимый финализатором объект может быть достигнут от некоторого объекта finalizable до некоторой цепочки ссылок, но не от любого живого потока. Недостижимый объект не может быть достигнут ни одним средством.
Незавершенному объекту никогда не вызывали его финализатор автоматически; завершенному объекту вызвали его финализатор автоматически. Объекту finalizable никогда не вызывали его финализатор автоматически, но виртуальная машина Java может в конечном счете автоматически вызвать свой финализатор.
Объект o не является finalizable, пока его конструктор не вызвал конструктора для Object
на o и том вызове завершился успешно (то есть, не выдавая исключение). Каждая запись перед завершением к полю объекта должна быть видимой к завершению того объекта. Кроме того ни одно из чтений перед завершением полей того объекта не может видеть записи, которые происходят после того, как завершение того объекта инициируется.
У каждого выполнения есть много моментов принятия решения достижимости, маркировал di. Каждое действие или прибывает - прежде di или прибывает - после di. Кроме как явно упомянуто, прибывать - прежде, чем упорядочить описанный в этом разделе не связано со всеми другими упорядочиваниями в модели памяти.
Если r является чтением, которое видит запись w, и r прибывает - прежде di, то w должен прибыть - прежде di. Если x и y являются действиями синхронизации на той же самой переменной или контролируют так, что так (x, y) (§17.4.4), и y прибывает - прежде di, то x должен прибыть - прежде di.
В каждом моменте принятия решения достижимости отмечается некоторый набор объектов, как недостижимый, и некоторое подмножество тех объектов отмечаются как finalizable. Эти моменты принятия решения достижимости являются также точками, в которых ссылки проверяются, ставятся в очередь и очищаются согласно правилам, обеспеченным в документации API для пакета java.lang.ref
.
Единственные объекты, которые считают определенно достижимыми в точке di, являются теми, которые, как могут показывать, достижимы приложением этих правил:
Действие активного использования X, если и только если по крайней мере одно из следующих условий содержит:
Как пример, если циркулярная соединенная группа незавершенных объектов становится недостижимой (или достижимый финализатором), то все объекты могут стать finalizable вместе. В конечном счете финализаторы для этих объектов могут быть вызваны в любом порядке, или даже одновременно использовании многократных потоков. Если автоматический менеджер по хранению позже находит, что объекты недостижимы, то их хранение может быть исправлено.
Это прямо, чтобы реализовать класс, который заставит ряд подобных финализатору методов быть вызванным в указанном порядке на ряд объектов, когда все объекты станут недостижимыми. Определение такого класса оставляют как осуществление для читателя.
Вот объяснение для правила, данного в предыдущем абзаце:
Класс, разгружающийся, является оптимизацией, которая помогает уменьшить использование памяти. Очевидно, семантика программы не должна зависеть от того, ли и как система хочет реализовывать оптимизацию, такую как разгрузка класса. Сделать иначе поставило бы под угрозу мобильность программ. Следовательно, ли класс или интерфейс были разгружены или не должны быть прозрачными к программе.
Однако, если бы класс или интерфейс C были разгружены, в то время как его загрузчик определения был потенциально достижим, тогда C мог бы быть перезагружен. Никогда нельзя было гарантировать, что это не будет происходить. Даже если бы на класс не сослался никакой другой в настоящий момент загруженный класс, то на него могли бы сослаться некоторый класс или интерфейс, D, который еще не был загружен. Когда D загружается загрузчиком определения К, его выполнение могло бы вызвать перезагрузку C.
Перезагружая, возможно, не прозрачен, если, например, класс имеет:
Собственные методы (который может сохранить статическое состояние).
Кроме того значение хэш-функции Class
объект зависит от своих идентификационных данных. Поэтому, вообще, невозможно перезагрузить класс или интерфейс абсолютно прозрачным способом.
Так как мы никогда не можем гарантировать, что разгрузка класса или взаимодействует через интерфейс, чей загрузчик потенциально достижим, не будет вызывать перезагрузку, и перезагрузка никогда не прозрачна, но разгрузка должна быть прозрачной, из этого следует, что нельзя разгрузить класс или интерфейс, в то время как его загрузчик потенциально достижим. Подобная цепь рассуждений может использоваться, чтобы вывести, что классы и интерфейсы, загруженные программой начальной загрузки, никогда не могут разгружаться.
Нужно также обсудить, почему безопасно разгрузить класс C, если его загрузчик класса определения может быть исправлен. Если загрузчик определения может быть исправлен, то никогда не может быть никаких живых ссылок на него (это включает ссылки, которые не живы, но могли бы быть возрождены финализаторами). Это, поочередно, может только быть истиной, если есть, никогда не могут быть никакие живые ссылки на любой из классов, определенных тем загрузчиком, включая C, или от их экземпляров или от кода.
Класс, разгружающийся, является оптимизацией, которая является только существенной для приложений, которые загружают большие количества классов и той остановки, используя большинство тех классов после некоторого времени. Главным примером такого приложения является веб-браузер, но есть другие. Характеристика таких приложений - то, что они управляют классами посредством явного использования загрузчиков класса. В результате политика, обрисованная в общих чертах выше работ хорошо для них.
Строго говоря не важно, что проблема класса, разгружающегося быть обсужденным этой спецификацией, поскольку класс, разгружающийся, является просто оптимизацией. Однако, проблема является очень тонкой, и таким образом, она упоминается здесь посредством разъяснения.
exit
метод класса Runtime
или класс System
и работа выхода не запрещается менеджером безопасности.
Содержание | Предыдущий | Следующий | Индекс | Спецификация языка Java Третий Выпуск |
Авторское право © 1996-2005 Sun Microsystems, Inc. Все права защищены
Пожалуйста, отправьте любые комментарии или исправления через нашу