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

12.1 Запуск Виртуальной машины

Виртуальная машина Java запускает выполнение, вызывая метод main из некоторого указанного класса, передавая это единственный параметр, который является массивом строк. В примерах в этой спецификации обычно вызывают этот первый класс Test.

Точная семантика запуска виртуальной машины дается в главе 5 Спецификации виртуальной машины Java, Втором Выпуске. Здесь мы представляем краткий обзор процесса с точки зрения языка программирования Java.

Способ, которым начальный класс определяется к виртуальной машине Java, выходит за рамки этой спецификации, но это типично в средах узла, которые используют командные строки для полностью определенного имени класса, который будет определен как параметр командной строки и для следующих параметров командной строки, которые будут использоваться в качестве строк, которые будут обеспечены как параметр методу main. Например, в реализации UNIX, командной строке:

java Test reboot Bob Dot Enzo
будет обычно запускать виртуальную машину Java, вызывая метод main из класса Test (класс в неназванном пакете), передавая это массив, содержащий четыре строки "reboot", "Bob", "Dot", и "Enzo".

Мы теперь обрисовываем в общих чертах шаги, которые виртуальная машина может сделать, чтобы выполниться Test, как пример загрузки, соединения, и процессов инициализации, которые описываются далее в более поздних разделах.

12.1.1 Загрузите Тест Класса

Начальная попытка выполнить метод main из класса Test обнаруживает что класс Test не загружается - то есть, что виртуальная машина в настоящий момент не содержит двоичное представление для этого класса. Виртуальная машина тогда использует загрузчик класса, чтобы попытаться найти такое двоичное представление. Если этот процесс перестал работать, то ошибка бросается. Этот процесс загрузки описывается далее в §12.2.

12.1.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.

12.1.3 Инициализируйте Тест: Выполните Инициализаторы

В нашем примере продолжения виртуальная машина все еще пытается выполнить метод main из класса Test. Это разрешается, только если класс был инициализирован (§12.4.1).

Инициализация состоит из выполнения любых инициализаторов переменной класса и статических инициализаторов класса Test, в текстовом порядке. Но прежде Test может быть инициализирован, его прямой суперкласс должен быть инициализирован, так же как прямой суперкласс его прямого суперкласса, и так далее, рекурсивно. В самом простом случае, Test имеет Object как его неявный прямой суперкласс; если класс Object еще не был инициализирован, тогда это должно быть инициализировано прежде Test инициализируется. Класс Object не имеет никакого суперкласса, таким образом, рекурсия завершается здесь.

Если класс Test имеет другой класс Super как его суперкласс, тогда Super должен быть инициализирован прежде Test. Это требует загрузки, проверки, и подготовки Super если это не было уже сделано и, в зависимости от реализации, может также включить разрешение символьных ссылок от Super и так далее, рекурсивно.

Инициализация может таким образом вызвать загрузку, соединение, и ошибки инициализации, включая такие ошибки, включая другие типы.

Процесс инициализации описывается далее в §12.4.

12.1.4 Вызовите Test.main

Наконец, после завершения инициализации для класса Test (во время которого другая последовательная загрузка, соединение, и инициализация, возможно, произошли), метод main из Test вызывается.

Метод main должен быть объявлен public, static, и void. Это должно принять единственный параметр, который является массивом строк. Этот метод может быть объявлен как также

public static void main(String[] args)
или

public static void main(String... args)

12.2 Загрузка Классов и Интерфейсов

Загрузка обращается к процессу обнаружения двоичной формы класса или интерфейсного типа с определенным именем, возможно вычисляя это на лету, но более обычно получая двоичное представление, ранее вычисленное из исходного кода компилятором, и построения, из той двоичной формы, a Class объект представить класс или интерфейс.

Точная семантика загрузки дается в главе 5 Спецификации виртуальной машины Java (всякий раз, когда мы обращаемся к спецификации виртуальной машины Java в этой книге, мы имеем в виду второй выпуск, как исправлено JSR 924). Здесь мы представляем краткий обзор процесса с точки зрения языка программирования Java.

Двоичный формат класса или интерфейса обычно class формат файла, описанный в Спецификации виртуальной машины Java, процитированной выше, но другие форматы, возможен, если они удовлетворяют требования, определенные в §13.1. Метод defineClass из класса ClassLoader может использоваться, чтобы создать Class объекты от двоичных представлений в class формат файла.

Загрузчики класса хорошего поведения поддерживают эти свойства:

Злонамеренный загрузчик класса мог нарушить эти свойства. Однако, это не могло подорвать безопасность системы типов, потому что виртуальная машина Java принимает меры против этого.

Для дальнейшего обсуждения этих проблем см. Спецификацию виртуальной машины Java и бумагу Динамический Класс, Загружающийся в виртуальной машине Java, Шенгом Лиэнгом и Джилэдом Брэчей, в Продолжениях OOPSLA '98, опубликованный как ACM SIGPLAN Уведомления, Объем 33, Номер 10, октябрь 1998, страницы 36-44. Основной принцип проекта языка программирования Java - то, что система типов времени выполнения не может ниспровергаться кодом, записанным на языке, не даже реализациями таких иначе чувствительных системных классов как ClassLoader и SecurityManager.

12.2.1 Процесс загрузки

Процесс загрузки реализуется классом ClassLoader и его подклассы. Различные подклассы ClassLoader может реализовать различные политики загрузки. В частности загрузчик класса может кэшировать двоичные представления классов и интерфейсов, выбрать их с упреждением основанный на ожидаемом использовании, или загрузить группу связанных классов вместе. Эти действия, возможно, не абсолютно прозрачны к рабочему приложению, если, например, недавно скомпилированная версия класса не находится, потому что более старая версия кэшируется загрузчиком класса. Это - ответственность загрузчика класса, однако, чтобы отразить загружающиеся ошибки только в точках в программе, они, возможно, возникли, не выбирая с упреждением или групповая загрузка.

Если ошибка происходит во время загрузки класса, то экземпляр одного из следующих подклассов класса LinkageError будет брошен в любую точку в программе, которая (прямо или косвенно) использует тип:

Поскольку загрузка включает выделение новых структур данных, это может перестать работать с OutOfMemoryError.

12.3 Соединение Классов и Интерфейсов

Соединение является процессом взятия двоичной формы класса или интерфейсного типа и объединения этого в состояние времени выполнения виртуальной машины Java, так, чтобы это могло быть выполнено. Класс или интерфейсный тип всегда загружаются прежде, чем это будет соединено.

Три различных действия включаются в соединение: проверка, подготовка, и разрешение символьных ссылок. Точная семантика соединения дается в главе 5 Спецификации виртуальной машины Java, Втором Выпуске. Здесь мы представляем краткий обзор процесса с точки зрения языка программирования Java.

Эта спецификация позволяет гибкость реализации относительно того, когда соединение действий (и, из-за рекурсии, загрузка) имеют место, при условии, что семантику языка уважают, что класс или интерфейс полностью проверяются и готовятся прежде, чем это будет инициализировано, и что ошибки, обнаруженные во время редактирования, бросаются в точку в программе, где некоторые меры предпринимаются программой, которая могла бы потребовать редактирования к классу или интерфейсу, включенному в ошибку.

Например, реализация может хотеть разрешать каждую символьную ссылку в классе или взаимодействовать через интерфейс индивидуально, только когда это используется (ленивое или последнее разрешение), или разрешить их внезапно, в то время как класс проверяется (статическое разрешение). Это означает, что процесс разрешения может продолжаться в некоторых реализациях, после того, как класс или интерфейс был инициализирован.

Поскольку соединение включает выделение новых структур данных, это может перестать работать с OutOfMemoryError.

12.3.1 Проверка Двоичного Представления

Проверка гарантирует, что двоичное представление класса или интерфейса структурно корректно. Например, это проверяет, что у каждой инструкции есть допустимый код работы; то, что каждая команда перехода переходит к запуску некоторой другой инструкции, а не в середину инструкции; то, что каждому методу предоставляют структурно корректную подпись; и что каждая инструкция повинуется дисциплине типа языка виртуальной машины Java.

Для спецификации процесса проверки см. отдельный объем этого ряда, Спецификации виртуальной машины Java. и спецификация J2ME Соединенная Ограниченная Конфигурация Устройства, версия 1.1.

Если ошибка происходит во время проверки, то экземпляр следующего подкласса класса LinkageError будет брошен в точку в программе, которая заставила класс быть проверенным:

12.3.2 Подготовка Класса или Интерфейсного Типа

Подготовка включает создание static поля (переменные класса и константы) для класса или интерфейса и инициализации таких полей к значениям по умолчанию (§4.12.5). Это не требует выполнения любого исходного кода; явные инициализаторы для static поля выполняются как часть инициализации (§12.4), не подготовка.

Во время для подготовки реализации виртуальной машины Java могут предварительно вычислить дополнительные структуры данных, чтобы сделать более поздние операции на классе или интерфейсе более эффективными. Одна особенно полезная структура данных является "таблицей метода" или другой структурой данных, которая позволяет любому методу быть вызванным на экземпляры класса, не требуя поиска суперклассов во время вызова.

12.3.3 Разрешение Символьных Ссылок

Двоичное представление класса или интерфейсных ссылок другие классы и интерфейсы и их поля, методы, и конструкторы символически, используя двоичные имена (§13.1) других классов и интерфейсов (§13.1). Для полей и методов, эти символьные ссылки включают имя класса или интерфейсным типом которого поле или метод являются элементом, так же как именем поля или метода непосредственно, вместе с соответствующей информацией о типе.

Прежде, чем символьная ссылка может использоваться, она должна подвергнуться разрешению, в чем символьная ссылка проверяется, чтобы быть корректной и, обычно, заменяется прямой ссылкой, которая может быть более эффективно обработана, если ссылка неоднократно используется.

Если ошибка произойдет во время разрешения, то ошибка будет брошена. Наиболее обычно это будет экземпляром одного из следующих подклассов класса IncompatibleClassChangeError, но это может также быть экземпляр некоторого другого подкласса IncompatibleClassChangeError или даже экземпляр класса IncompatibleClassChangeError непосредственно. Эта ошибка может быть брошена в любую точку в программе, которая использует символьную ссылку на тип, прямо или косвенно:

Дополнительно, UnsatisfiedLinkError (подкласс LinkageError) может быть брошен, если класс объявляет a native метод, для которого не может быть найдена никакая реализация. Ошибка произойдет, если метод будет использоваться, или ранее, в зависимости от того, какая стратегия разрешения используется виртуальной машиной (§12.3).

12.4 Инициализация Классов и Интерфейсов

Инициализация класса состоит из выполнения его статических инициализаторов и инициализаторов для static поля (переменные класса) объявленный в классе. Инициализация интерфейса состоит из выполнения инициализаторов для полей (константы), объявленные там.

Прежде, чем класс инициализируется, его суперкласс должен быть инициализирован, но интерфейсы, реализованные классом, не инициализируются. Точно так же суперинтерфейсы интерфейса не инициализируются прежде, чем интерфейс инициализируется.

12.4.1 Когда Инициализация Происходит

Инициализация класса состоит из выполнения его статических инициализаторов и инициализаторов для статических полей, объявленных в классе. Инициализация интерфейса состоит из выполнения инициализаторов для полей, объявленных в интерфейсе.

Прежде, чем класс инициализируется, его прямой суперкласс должен быть инициализирован, но интерфейсы, реализованные классом, не должны быть инициализированы. Точно так же суперинтерфейсы интерфейсной потребности не быть инициализированными перед интерфейсом инициализируются.

Класс или интерфейсный тип 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 не инициализируется.

12.4.2 Подробная Процедура Инициализации

Поскольку язык программирования Java многопоточен, инициализация класса или интерфейса требует осторожной синхронизации, так как некоторый другой поток может пытаться инициализировать тот же самый класс или интерфейс одновременно. Есть также возможность, что инициализацию класса или интерфейса можно требовать рекурсивно как часть инициализации того класса или интерфейса; например, переменный инициализатор в классе A мог бы вызвать метод несвязанного класса B, который мог бы поочередно вызвать метод класса A. Реализация виртуальной машины Java ответственна за то, чтобы заботиться о синхронизации и рекурсивной инициализации при использовании следующей процедуры. Это предполагает что Class объект был уже проверен и подготовлен, и что Class объект содержит состояние, которое указывает на одну из четырех ситуаций:

Процедура для того, чтобы инициализировать класс или интерфейс тогда следующие:

  1. Синхронизируйтесь (§14.19) на Class объект, который представляет класс или интерфейс, который будет инициализирован. Это включает ожидание, пока текущий поток не может получить блокировку для того объекта (§17.1).
  2. Если инициализация происходит для класса или интерфейса некоторым другим потоком, то wait на этом Class объект (который временно выпускает блокировку). Когда текущий поток просыпается от wait, повторите этот шаг.
  3. Если инициализация происходит для класса или интерфейса текущим потоком, то это должно быть рекурсивным запросом на инициализацию. Выпустите блокировку на Class возразите и полный обычно.
  4. Если класс или интерфейс были уже инициализированы, то никакое дальнейшее действие не требуется. Выпустите блокировку на Class возразите и полный обычно.
  5. Если Class объект находится в ошибочном состоянии, тогда инициализация не возможна. Выпустите блокировку на Class объект и бросок a NoClassDefFoundError.
  6. Иначе, запишите факт та инициализация Class объект теперь происходит текущим потоком, и выпустите блокировку на Class объект.
  7. Затем, если Class объект представляет класс, а не интерфейс, и суперкласс этого класса еще не был инициализирован, тогда рекурсивно выполните эту всю процедуру для суперкласса. В случае необходимости проверьте и подготовьте суперкласс сначала. Если инициализация суперкласса завершается резко из-за выданного исключения, то заблокируйте это Class объект, маркируйте это ошибочным, уведомьте все потоки ожидания, выпустите блокировку, и завершитесь резко, выдавая то же самое исключение, которое следовало из инициализации суперкласса.
  8. Затем, определите, включают ли утверждениям (§14.10) для этого класса, запрашивая его загрузчик класса определения.
  9. Затем, выполните или инициализаторы переменной класса и статические инициализаторы класса, или полевые инициализаторы интерфейса, в текстовом порядке, как если бы они были единственным блоком, за исключением того, что final переменные класса и поля интерфейсов, значения которых являются константами времени компиляции, инициализируются сначала (§8.3.2.1, §9.3.1, §13.4.9).
  10. Если выполнение инициализаторов обычно завершается, то заблокируйте это Class объект, маркируйте, он полностью инициализировал, уведомьте все потоки ожидания, выпустите блокировку, и обычно завершайте эту процедуру.
  11. Иначе, инициализаторы, должно быть, завершились резко, выдавая некоторое исключение E. Если класс E не Error или один из его подклассов, затем создайте новый экземпляр класса ExceptionInInitializerError, с E как параметр, и использование этот объект вместо E в следующем шаге. Но если новый экземпляр ExceptionInInitializerError не может быть создан потому что OutOfMemoryError происходит, тогда вместо этого используйте OutOfMemoryError объект вместо E в следующем шаге.
  12. Заблокируйте Class объект, маркируйте это ошибочным, уведомьте все потоки ожидания, выпустите блокировку, и завершите эту процедуру резко с причиной E или ее заменой как определено в предыдущем шаге.

(Из-за дефекта в некоторых ранних реализациях, исключение во время инициализации класса было проигнорировано, вместо того, чтобы вызвать ExceptionInInitializerError как описано здесь.)

12.4.3 Инициализация: Импликации для Генерации кода

Генераторы кода должны сохранить точки возможной инициализации класса или интерфейса, вставляя вызов процедуры инициализации, только описанной. Если эта процедура инициализации обычно завершается и Class объект полностью инициализируется и готов к употреблению, тогда вызов процедуры инициализации больше не необходим, и это может быть устранено из кода например, исправляя это или иначе регенерируя код.

Анализ времени компиляции может, в некоторых случаях, быть в состоянии устранить многие из проверок, что тип был инициализирован от сгенерированного кода, если порядок инициализации на группу связанных типов может быть определен. Такой анализ должен, однако, полностью учесть параллелизм и для факта, что код инициализации неограничен.

12.5 Создание Новых Экземпляров Класса

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

Новый экземпляр класса может быть неявно создан в следующих ситуациях:

Каждая из этих ситуаций идентифицирует определенного конструктора, чтобы быть вызванной с указанными параметрами (возможно ни один) как часть процесса создания экземпляра класса.

Всякий раз, когда новый экземпляр класса создается, место в памяти выделяется для него с комнатой для всех переменных экземпляра, объявленных в типе класса и всех переменных экземпляра, объявленных в каждом суперклассе типа класса, включая все переменные экземпляра, которые могут быть скрыты (§8.3). Если нет достаточного пространства, доступного, чтобы выделить память для объекта, то создание экземпляра класса завершается резко с OutOfMemoryError. Иначе, все переменные экземпляра в новом объекте, включая объявленных в суперклассах, инициализируются к их значениям по умолчанию (§4.12.5).

Непосредственно перед тем, как ссылка на недавно создаваемый объект возвращается как результат, обозначенный конструктор обрабатывается, чтобы инициализировать новый объект, используя следующую процедуру:

  1. Присвойте параметры за конструктора к недавно создаваемым переменным параметра для этого вызова конструктора.
  2. Если этот конструктор начинает с явного вызова конструктора другого конструктора в том же самом классе (использование this), затем оцените параметры и обработайте тот вызов конструктора, рекурсивно используя эти те же самые пять шагов. Если тот вызов конструктора завершается резко, то эта процедура завершается резко по той же самой причине; иначе, продолжайте с шагом 5.
  3. Этот конструктор не начинает с явного вызова конструктора другого конструктора в том же самом классе (использование this). Если этот конструктор для класса кроме Object, тогда этот конструктор начнет с явного или неявного вызова конструктора суперкласса (использование super). Оцените параметры и обработайте тот вызов конструктора суперкласса, рекурсивно используя эти те же самые пять шагов. Если тот вызов конструктора завершается резко, то эта процедура завершается резко по той же самой причине. Иначе, продолжайте с шагом 4.
  4. Выполните инициализаторы экземпляра и инициализаторы переменной экземпляра для этого класса, присваивая значения инициализаторов переменной экземпляра к соответствующим переменным экземпляра, в слева направо порядок, в котором они появляются дословно в исходном коде для класса. Если выполнение любого из этих результатов инициализаторов в исключении, то никакие дальнейшие инициализаторы не обрабатываются и эта процедура, завершается резко с тем же самым исключением. Иначе, продолжайте с шагом 5. (В некоторых ранних реализациях, компилятор, неправильно опущенный код, чтобы инициализировать поле, если полевое выражение инициализатора было константным выражением, значение которого было равно значению инициализации по умолчанию для его типа.)
  5. Выполните остальную часть тела этого конструктора. Если то выполнение завершается резко, то эта процедура завершается резко по той же самой причине. Иначе, эта процедура обычно завершается.

В примере:

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 не объявляет конструкторов, конструктора по умолчанию формы:

ColoredPoint() { super(); }
обеспечивается для этого автоматически компилятором Java.

Этот конструктор тогда вызывает 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 для большего количества деталей об объявлениях конструктора.

12.6 Завершение Экземпляров Класса

Класс 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 для деталей.

12.6.1 Реализация Завершения

Каждый объект может быть характеризован двумя атрибутами: это может быть достижимо, достижимо финализатором, или недостижимо, и это может также быть незавершено, finalizable, или завершено.

Достижимый объект является любым объектом, к которому можно получить доступ в любом потенциальном вычислении продолжения от любого живого потока. Оптимизация преобразований программы может быть разработана, которые сокращают количество объектов, которые достижимы, чтобы быть меньше чем те, которых наивно считали бы достижимыми. Например, компилятор или генератор кода могут хотеть устанавливать переменную или параметр, который больше не привыкнет к 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 и том вызове завершился успешно (то есть, не выдавая исключение). Каждая запись перед завершением к полю объекта должна быть видимой к завершению того объекта. Кроме того ни одно из чтений перед завершением полей того объекта не может видеть записи, которые происходят после того, как завершение того объекта инициируется.

12.6.1.1 Взаимодействие с Моделью Памяти

Для модели памяти (§17) должно быть возможно решить, когда это может фиксировать действия, которые имеют место в финализаторе. Этот раздел описывает взаимодействие завершения с моделью памяти.

У каждого выполнения есть много моментов принятия решения достижимости, маркировал 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, если и только если по крайней мере одно из следующих условий содержит:

Если объект X отмечается как недостижимый в di,

Если объект X отмечается как finalizable в di, то

12.6.2 Вызовы финализатора Не Упорядочиваются

Язык программирования Java не налагает упорядочивания на, завершают вызовы метода. Финализаторы можно вызвать в любом порядке, или даже одновременно.

Как пример, если циркулярная соединенная группа незавершенных объектов становится недостижимой (или достижимый финализатором), то все объекты могут стать finalizable вместе. В конечном счете финализаторы для этих объектов могут быть вызваны в любом порядке, или даже одновременно использовании многократных потоков. Если автоматический менеджер по хранению позже находит, что объекты недостижимы, то их хранение может быть исправлено.

Это прямо, чтобы реализовать класс, который заставит ряд подобных финализатору методов быть вызванным в указанном порядке на ряд объектов, когда все объекты станут недостижимыми. Определение такого класса оставляют как осуществление для читателя.

12.7 Разгрузка Классов и Интерфейсов

Реализация языка программирования Java может разгрузить классы. Класс или интерфейс могут быть разгружены, если и только если его загрузчик класса определения может быть исправлен сборщиком "мусора" как обсуждено в §12.6. Классы и интерфейсы, загруженные программой начальной загрузки, не могут быть разгружены.

Вот объяснение для правила, данного в предыдущем абзаце:

Класс, разгружающийся, является оптимизацией, которая помогает уменьшить использование памяти. Очевидно, семантика программы не должна зависеть от того, ли и как система хочет реализовывать оптимизацию, такую как разгрузка класса. Сделать иначе поставило бы под угрозу мобильность программ. Следовательно, ли класс или интерфейс были разгружены или не должны быть прозрачными к программе.

Однако, если бы класс или интерфейс C были разгружены, в то время как его загрузчик определения был потенциально достижим, тогда C мог бы быть перезагружен. Никогда нельзя было гарантировать, что это не будет происходить. Даже если бы на класс не сослался никакой другой в настоящий момент загруженный класс, то на него могли бы сослаться некоторый класс или интерфейс, D, который еще не был загружен. Когда D загружается загрузчиком определения К, его выполнение могло бы вызвать перезагрузку C.

Перезагружая, возможно, не прозрачен, если, например, класс имеет:

Кроме того значение хэш-функции Class объект зависит от своих идентификационных данных. Поэтому, вообще, невозможно перезагрузить класс или интерфейс абсолютно прозрачным способом.

Так как мы никогда не можем гарантировать, что разгрузка класса или взаимодействует через интерфейс, чей загрузчик потенциально достижим, не будет вызывать перезагрузку, и перезагрузка никогда не прозрачна, но разгрузка должна быть прозрачной, из этого следует, что нельзя разгрузить класс или интерфейс, в то время как его загрузчик потенциально достижим. Подобная цепь рассуждений может использоваться, чтобы вывести, что классы и интерфейсы, загруженные программой начальной загрузки, никогда не могут разгружаться.

Нужно также обсудить, почему безопасно разгрузить класс C, если его загрузчик класса определения может быть исправлен. Если загрузчик определения может быть исправлен, то никогда не может быть никаких живых ссылок на него (это включает ссылки, которые не живы, но могли бы быть возрождены финализаторами). Это, поочередно, может только быть истиной, если есть, никогда не могут быть никакие живые ссылки на любой из классов, определенных тем загрузчиком, включая C, или от их экземпляров или от кода.

Класс, разгружающийся, является оптимизацией, которая является только существенной для приложений, которые загружают большие количества классов и той остановки, используя большинство тех классов после некоторого времени. Главным примером такого приложения является веб-браузер, но есть другие. Характеристика таких приложений - то, что они управляют классами посредством явного использования загрузчиков класса. В результате политика, обрисованная в общих чертах выше работ хорошо для них.

Строго говоря не важно, что проблема класса, разгружающегося быть обсужденным этой спецификацией, поскольку класс, разгружающийся, является просто оптимизацией. Однако, проблема является очень тонкой, и таким образом, она упоминается здесь посредством разъяснения.

12.8 Выход программы

Программа завершает все свое действие и выходит, когда одна из двух вещей происходит:


Содержание | Предыдущий | Следующий | Индекс Спецификация языка Java
Третий Выпуск

Авторское право © 1996-2005 Sun Microsystems, Inc. Все права защищены
Пожалуйста, отправьте любые комментарии или исправления через нашу форму обратной связи

free hit counter