Содержание | Предыдущий | Следующий | Индекс

ГЛАВА 12

Выполнение


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

Виртуальная машина Java запускает, загружая указанный класс и затем вызывая метод main в этом указанном классе. Разделите §12.1, обрисовывает в общих чертах загрузку, соединение, и шаги инициализации, включенные в выполнение main, как введение в понятия в этой главе. Дальнейшие разделы определяют детали загрузки (§12.2), соединяясь (§12.3), и инициализация (§12.4).

Глава продолжается со спецификацией процедур для создания новых экземпляров класса (§12.5); завершение экземпляров класса (§12.6); и завершение классов (§12.7). Это заканчивается, описывая разгрузку классов (§12.8) и процедура, сопровождаемая, когда виртуальная машина выходит (§12.9).

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

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

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

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

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

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

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

12.1.2 Ссылка Test: Проверьте, Подготовьтесь, (Дополнительно) Решите

После 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 Инициализировать Test: Выполните Инициализаторы

В нашем примере продолжения виртуальная машина все еще пытается выполнить метод 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. Это должно принять единственный параметр, который является массивом строк.

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

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

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

Система виртуальной машины Java должна поддержать внутреннюю таблицу классов и интерфейсов, которые были загружены ради разрешения символьных ссылок. Каждая запись в таблице должна состоять из полностью определенного имени класса (как строка), загрузчик класса, и a Class объект. Всякий раз, когда символьная ссылка на класс или интерфейс должна быть разрешена, загрузчик класса идентифицируется, который ответственен за загрузку класса или интерфейса в случае необходимости. С таблицей нужно консультироваться сначала, однако; если это уже содержит запись для того имени класса и загрузчика класса, то объект класса в той записи должен использоваться, и никакой метод загрузчика класса не должен быть вызван. Если таблица не содержит такой записи, то метод loadClass (§20.14.2) загрузчика класса должен быть вызван, давая это имя класса или интерфейса. То, если и когда это возвращается, класс возражают, что это возвращается, должно использоваться, чтобы сделать новую запись в таблице для того имени класса и загрузчика класса.

Цель этой внутренней таблицы состоит в том, чтобы позволить процессу проверки (§12.3.1) предполагать в его целях, что два класса или интерфейсы являются тем же самым, если у них есть то же самое имя и тот же самый загрузчик класса. Это свойство позволяет классу быть проверенным, не загружая все классы и интерфейсы, которые оно использует, или активно или пассивно. Загрузчики класса хорошего поведения действительно поддерживают это свойство: учитывая то же самое имя дважды, хороший загрузчик класса должен возвратить тот же самый объект класса каждый раз. Но без внутренней таблицы, злонамеренный загрузчик класса мог нарушить это свойство и подорвать безопасность системы типов Java. Основной принцип проекта языка Java - то, что система типов не может ниспровергаться кодом, записанным в Java, не даже реализациями таких иначе чувствительных системных классов как ClassLoader (§20.14) и SecurityManager (§20.17).

Запись может быть удалена из внутренней таблицы только после разгрузки (§12.8) класс или интерфейс, представленный объектом класса в записи.

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

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

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

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

12.2.2 Загрузка: Импликации для Генерации кода

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

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

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

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

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

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

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

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

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

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

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

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

Реализации Java должны обнаружить следующую ошибку во время подготовки:

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

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

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

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

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

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

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

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

Символьные ссылки в пределах группы типов могут быть разрешены даже прежде, чем группа загружается (§12.2.2) в реализации, которая использует специальный (нестандартный) двоичный формат (§13.1). Это соответствует традиционной практике "редактирования связей." Даже если это не делается, у реализации Java есть большая гибкость. Это может разрешить все символьные ссылки от типа в точке первого действия редактирования на типе, или задержать разрешение каждой символьной ссылки на первое использование той ссылки.

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

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

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

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

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

Класс или интерфейсный тип T будут инициализированы при его первом активном использовании, которое происходит если:

Все другое использование типа является пассивным использованием.

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

Как показано в примере в §8.5, факт, что код инициализации неограничен, позволяет примерам быть созданными, где значение переменной класса может наблюдаться, когда у этого все еще есть свое начальное значение по умолчанию, прежде, чем ее выражение инициализации будет оценено, но такие примеры редки практически. (Такие примеры могут быть также созданы например переменная инициализация; см. пример в конце §12.5). Java обеспечивает полную мощность языка в этих инициализаторах; программисты должны осуществить некоторую заботу. Это питание помещает дополнительное бремя в генераторы кода, но это бремя возникло бы в любом случае, потому что Java параллелен (§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.17) на Class объект, который представляет класс или интерфейс, который будет инициализирован. Это включает ожидание, пока текущий поток не может получить блокировку для того объекта (§17.13).
  2. Если инициализация происходит для класса или интерфейса некоторым другим потоком, то wait (§20.1.6) на этом Class объект (который временно выпускает блокировку). Когда текущий поток просыпается от wait, повторите этот шаг.
  3. Если инициализация происходит для класса или интерфейса текущим потоком, то это должно быть рекурсивным запросом на инициализацию. Выпустите блокировку на Class возразите и полный обычно.
  4. Если класс или интерфейс были уже инициализированы, то никакое дальнейшее действие не требуется. Выпустите блокировку на Class возразите и полный обычно.
  5. Если Class объект находится в ошибочном состоянии, тогда инициализация не возможна. Выпустите блокировку на Class объект и бросок a NoClassDefFoundError.
  6. Иначе, запишите факт та инициализация Class объект теперь происходит текущим потоком, и выпустите блокировку на Class объект.
  7. Затем, если Class объект представляет класс, а не интерфейс, и суперкласс этого класса еще не был инициализирован, тогда рекурсивно выполните эту всю процедуру для суперкласса. В случае необходимости проверьте и подготовьте суперкласс сначала. Если инициализация суперкласса завершается резко из-за выданного исключения, то заблокируйте это Class объект, маркируйте это ошибочным, уведомьте все потоки ожидания (§20.1.10), выпустите блокировку, и завершитесь резко, выдавая то же самое исключение, которое следовало из инициализации суперкласса.
  8. Затем, выполните или инициализаторы переменной класса и статические инициализаторы класса, или полевые инициализаторы интерфейса, в текстовом порядке, как если бы они были единственным блоком, за исключением того, что final переменные класса и поля интерфейсов, значения которых являются константами времени компиляции, инициализируются сначала (§8.3.2.1, §9.3.1, §13.4.8).
  9. Если выполнение инициализаторов обычно завершается, то заблокируйте это Class объект, маркируйте, он полностью инициализировал, уведомьте все потоки ожидания (§20.1.10), выпустите блокировку, и обычно завершайте эту процедуру.
  10. Иначе, инициализаторы, должно быть, завершились резко, выдавая некоторое исключение E. Если класс E не Error или один из его подклассов, затем создайте новый экземпляр класса ExceptionInInitializerError, с E как параметр, и использование этот объект вместо E в следующем шаге. Но если новый экземпляр ExceptionInInitializerError не может быть создан потому что OutOfMemoryError происходит, тогда вместо этого используйте OutOfMemoryError объект вместо E в следующем шаге.
  11. Заблокируйте Class объект, маркируйте это ошибочным, уведомьте все потоки ожидания (§20.1.10), выпустите блокировку, и завершите эту процедуру резко с причиной E или ее заменой как определено в предыдущем шаге.
(Из-за дефекта в некоторых ранних реализациях Java, исключение во время инициализации класса было проигнорировано, вместо того, чтобы вызвать ExceptionInInitializerError как описано здесь.)

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

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

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

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

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

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

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

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

  1. Присвойте параметры за конструктора к недавно создаваемым переменным параметра для этого вызова конструктора.
  2. Если этот конструктор начинает с явного вызова конструктора другого конструктора в том же самом классе (использование this), затем оцените параметры и обработайте тот вызов конструктора, рекурсивно используя эти те же самые пять шагов. Если тот вызов конструктора завершается резко, то эта процедура завершается резко по той же самой причине; иначе, продолжайте с шагом 5.
  3. Этот конструктор не начинает с явного вызова конструктора другого конструктора в том же самом классе (использование this). Если этот конструктор для класса кроме Object, тогда этот конструктор начнет с явного или неявного вызова конструктора суперкласса (использование super). Оцените параметры и обработайте тот вызов конструктора суперкласса, рекурсивно используя эти те же самые пять шагов. Если тот вызов конструктора завершается резко, то эта процедура завершается резко по той же самой причине. Иначе, продолжайте с шагом 4.
  4. Выполните инициализаторы переменной экземпляра для этого класса, присваивая их значения соответствующим переменным экземпляра, в слева направо порядок, в котором они появляются дословно в исходном коде для класса. Если выполнение любого из этих результатов инициализаторов в исключении, то никакие дальнейшие инициализаторы не обрабатываются и эта процедура, завершается резко с тем же самым исключением. Иначе, продолжайте с шагом 5. (В некоторых ранних реализациях Java, компилятор, неправильно опущенный код, чтобы инициализировать поле, если полевое выражение инициализатора было константным выражением, значение которого было равно значению инициализации по умолчанию для его типа.)
  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 indiana = (int)Math.PI;													// That is, 3

	public static void main(String[] args) {
		Test t = new Test();
		t.printThree();
	}
	void printThree() { System.out.println(indiana); }
}
производит вывод:


0
3
Это показывает что вызов printThree в конструкторе для класса Super не вызывает определение printThree в классе Super, а скорее вызывает определение переопределения printThree в классе Test. Этот метод поэтому работает перед полевыми инициализаторами Test были выполнены, который является, почему первый вывод значения 0, значение по умолчанию то, к который поле three из Test инициализируется. Более поздний вызов printThree в методе main вызывает то же самое определение printThree, но той точкой инициализатор например переменная three был выполнен, и таким образом, значение 3 печатается.

См. §8.6 для большего количества деталей об объявлениях конструктора.

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

Класс Object имеет a protected метод вызывают finalize (§20.1.11); этот метод может быть переопределен другими классами. Определенное определение finalize это может быть вызвано для объекта, вызывается финализатором того объекта. Прежде, чем хранение для объекта исправляется сборщиком "мусора", виртуальная машина Java вызовет финализатор того объекта.

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

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

finalize метод объявляется в классе Object не предпринимает мер. Однако, факт тот класс Object объявляет a finalize метод означает что finalize метод для любого класса может всегда вызывать finalize метод для его суперкласса, который является обычно хорошей практикой. (В отличие от конструкторов, финализаторы автоматически не вызывают финализатор для суперкласса; такой вызов должен быть кодирован явно.)

Для эффективности реализация может отследить классы, которые не переопределяют finalize метод класса Object, или переопределите это тривиальным способом, таким как:

protected void finalize() throws Throwable {
super.finalize();
}
Мы поощряем реализации обрабатывать такие объекты как наличие финализатора, который не переопределяется, и завершить их более эффективно, как описано в §12.6.1.

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

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

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

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

Незавершенному объекту никогда не вызывали его финализатор автоматически; завершенному объекту вызвали его финализатор автоматически. Объекту finalizable никогда не вызывали его финализатор автоматически, но виртуальная машина Java может в конечном счете автоматически вызвать свой финализатор.

Жизненный цикл объекта повинуется следующей схеме перехода, где мы сокращаем "достижимый финализатором" как "f-reachable":

Когда объект сначала создается (A), это достижимо и незавершается.

Поскольку ссылки на объект отбрасываются во время выполнения программы, объект, который был достижим, может стать достижимым финализатором (B, C, D) или недостижимый (E, F). (Отметьте, что достижимый финализатором объект никогда не становится недостижимым непосредственно; это становится достижимым, когда финализатор, от которого это может быть достигнуто, вызывается, как объяснено ниже.)

Если виртуальная машина Java обнаруживает, что незавершенный объект стал достижимым финализатором или недостижимым, это может маркировать объект finalizable (Г, H); кроме того, если объект был недостижим, это становится достижимым финализатором (H).

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

В любое время виртуальная машина Java может взять любой объект finalizable, маркировать завершенным, и затем вызвать finalize метод в некотором потоке. Это вызывает объект стать завершенным и достижимым (J, K), и он также может вызвать другие объекты, которые были достижимы финализатором, чтобы стать достижимыми снова (L, М., N).

Объект finalizable не может также быть недостижимым; это может быть достигнуто, потому что его финализатор может в конечном счете быть вызван, после чего у потока, выполняющего финализатор, будет доступ к объекту, как this (§15.7.2). Таким образом есть фактически только восемь возможных состояний для объекта.

После того, как объект был завершен, никакие дальнейшие меры не предпринимаются, пока автоматическое управление хранением не решает, что это недостижимо. Из-за способа, которым объект прогрессирует от незавершенного состояния до finalizable, утверждают к завершенному состоянию, finalize метод автоматически никогда не вызывается не раз виртуальной машиной Java для каждого объекта, даже если объект снова делается достижимым после того, как это было завершено.

Явный вызов финализатора игнорирует текущее состояние объекта и не изменяет состояние объекта от незавершенного или finalizable к завершенному.

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

Программисты Java должны также знать, что финализатор может быть автоматически вызван, даже при том, что это достижимо, во время завершения на выходе (§12.9); кроме того финализатор может также быть вызван явно как обычный метод. Поэтому, мы рекомендуем что проект finalize методы быть сохраненным простой и что они быть запрограммированным защитно, так, чтобы они работали во всех случаях.

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

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

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

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

12.7 Завершение Классов

Если класс объявляет метод класса classFinalize это не берет параметров и не возвращает результата:

static void classFinalize() throws Throwable { . . . }
тогда этот метод будет вызван прежде, чем класс разгружается (§12.8). Как finalize метод для объектов, этот метод будет автоматически вызван только однажды. Этот метод может дополнительно быть объявлен private, protected, или public.

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

Виртуальная машина Java может обеспечить механизмы, посредством чего классы разгружаются. Детали таких механизмов не определяются в этой версии Спецификации языка Java. Вообще, группы связанного класса и интерфейсных типов будут разгружены вместе. Это может использоваться, например, чтобы разгрузить группу связанных типов, которые были загружены, используя определенный загрузчик класса. Такая группа могла бы состоять из всех классов, реализовывая единственный апплет в основанном на Java браузере, таких как HotJava, например.

Класс не может быть разгружен, в то время как любой экземпляр его все еще достижим (§12.6). Класс или интерфейс не могут быть разгружены в то время как Class объект, который представляет это, все еще достижим.

Классам, которые объявляют финализаторы класса (§12.7), выполнят эти финализаторы прежде, чем они будут разгружены.

12.9 Выход Виртуальной машины

Виртуальная машина Java завершает все свое действие и выходит, когда одна из двух вещей происходит:

Программа Java может определить, что финализаторы всех объектов, у которых есть финализаторы, и все классы, у которых есть финализаторы класса, которые еще не были автоматически вызваны, должны быть выполнены перед выходами виртуальной машины. Это делается, вызывая метод runFinalizersOnExit из класса System с параметром true. Значение по умолчанию не должно выполнить финализаторы на выходе, и это поведение может быть восстановлено, вызывая runFinalizersOnExit с параметром false. Вызов runFinalizersOnExit метод разрешается, только если вызывающей стороне позволяют exit, и иначе отклоняется SecurityManager (§20.17).


Содержание | Предыдущий | Следующий | Индекс

Спецификация языка Java (HTML, сгенерированный Блинчиком "сюзет" Pelouch 24 февраля 1998)
Авторское право © Sun Microsystems, Inc 1996 года. Все права защищены
Пожалуйста, отправьте любые комментарии или исправления к doug.kramer@sun.com



Spec-Zone.ru - all specs in one place



free hit counter