Spec-Zone .ru
спецификации, руководства, описания, API
|
ГЛАВА 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).
main
из некоторого указанного класса, передавая это единственный параметр, который является массивом строк. В примерах в этой спецификации обычно вызывают этот первый класс Test
. Способ, которым начальный класс определяется к виртуальной машине Java, выходит за рамки этой спецификации, но это типично в средах узла, которые используют командные строки для полностью определенного имени класса, который будет определен как параметр командной строки и для следующих параметров командной строки, которые будут использоваться в качестве строк, которые будут обеспечены как параметр методу main
. Например, в реализации UNIX, командной строке:
java Test reboot Bob Dot Enzoбудет обычно запускать виртуальную машину Java, вызывая метод
main
из класса Test
(класс в неназванном пакете), передавая это массив, содержащий четыре строки "reboot"
, "Bob"
, "Dot"
, и "Enzo"
. Мы теперь обрисовываем в общих чертах шаги, которые виртуальная машина может сделать, чтобы выполниться Test
, как пример загрузки, соединения, и процессов инициализации, которые описываются далее в более поздних разделах.
Test
main
из класса Test
обнаруживает что класс Test
не загружается - то есть, что виртуальная машина в настоящий момент не содержит двоичное представление для этого класса. Виртуальная машина тогда использует загрузчик класса (§20.14), чтобы попытаться найти такое двоичное представление. Если этот процесс перестал работать, то ошибка бросается. Этот процесс загрузки описывается далее в §12.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.
Test
: Выполните Инициализаторыmain
из класса Test
. Это - предпринятое активное использование (§12.4.1) класса, который разрешается, только если класс был инициализирован. Инициализация состоит из выполнения любых инициализаторов переменной класса и статических инициализаторов класса Test
, в текстовом порядке. Но прежде Test
может быть инициализирован, его прямой суперкласс должен быть инициализирован, так же как прямой суперкласс его прямого суперкласса, и так далее, рекурсивно. В самом простом случае, Test
имеет Object
как его неявный прямой суперкласс; если класс Object
еще не был инициализирован, тогда это должно быть инициализировано прежде Test
инициализируется. Класс Object
не имеет никакого суперкласса, таким образом, рекурсия завершается здесь.
Если класс Test
имеет другой класс Super
как его суперкласс, тогда Super
должен быть инициализирован прежде Test
. Это требует загрузки, проверки, и подготовки Super
если это не было уже сделано и, в зависимости от реализации, может также включить разрешение символьных ссылок от Super
и так далее, рекурсивно.
Инициализация может таким образом вызвать загрузку, соединение, и ошибки инициализации, включая такие ошибки, включая другие типы.
Процесс инициализации описывается далее в §12.4.
Test.main
Test
(во время которого другая последовательная загрузка, соединение, и инициализация, возможно, произошли), метод main
из Test
вызывается. Метод main
должен быть объявлен public
, static
, и void
. Это должно принять единственный параметр, который является массивом строк.
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) класс или интерфейс, представленный объектом класса в записи.
ClassLoader
(§20.14) и его подклассы. Различные подклассы ClassLoader
может реализовать различные политики загрузки. В частности загрузчик класса может кэшировать двоичные представления классов и интерфейсов, выбрать их с упреждением основанный на ожидаемом использовании, или загрузить группу связанных классов вместе. Эти действия, возможно, не абсолютно прозрачны к рабочему приложению Java, если, например, недавно скомпилированная версия класса не находится, потому что более старая версия кэшируется загрузчиком класса. Это - ответственность загрузчика класса, однако, чтобы отразить загружающиеся ошибки только в точках в программе, они, возможно, возникли, не выбирая с упреждением или групповая загрузка. Если ошибка происходит во время загрузки класса, то экземпляр одного из следующих подклассов класса LinkageError
будет брошен в любую точку в программе Java, которая (прямо или косвенно) использует тип:
ClassCircularityError
: Класс или интерфейс не могли быть загружены, потому что это будет свой собственный суперкласс или суперинтерфейс (§13.4.4).
ClassFormatError
: Двоичные данные, который подразумевает определять требуемый скомпилированный класс или интерфейс, уродливы.
NoClassDefFoundError
: Никакое определение для требуемого класса или интерфейса не могло быть найдено соответствующим загрузчиком класса. OutOfMemoryError
.Java позволяет гибкость реализации относительно того, когда соединение действий (и, из-за рекурсии, загрузка) имеют место, при условии, что семантику языка уважают, что класс или интерфейс полностью проверяются и готовятся прежде, чем это будет инициализировано, и что ошибки, обнаруженные во время редактирования, бросаются в точку в программе, где некоторые меры предпринимаются программой, которая могла бы потребовать редактирования к классу или интерфейсу, включенному в ошибку.
Например, реализация может хотеть разрешать каждую символьную ссылку в классе или взаимодействовать через интерфейс индивидуально, только когда это используется (ленивое или последнее разрешение), или разрешить их внезапно, в то время как класс проверяется (статическое разрешение). Это означает, что процесс разрешения может продолжаться в некоторых реализациях, после того, как класс или интерфейс был инициализирован.
Поскольку соединение включает выделение новых структур данных, это может перестать работать с OutOfMemoryError
.
Для более подробного описания процесса проверки см. отдельный объем этого ряда, Спецификации виртуальной машины Java.
Если ошибка происходит во время проверки, то экземпляр следующего подкласса класса LinkageError
будет брошен в точку в программе Java, которая заставила класс быть проверенным:
VerifyError
: Двоичное определение для класса или интерфейса, отказавшего, чтобы передать ряд необходимых проверок, чтобы проверить, что это повинуется семантике языка Java и что это не может нарушить целостность виртуальной машины Java. (См. §13.4.2, §13.4.4, §13.4.8, и §13.4.16 для некоторых примеров.) static
поля (переменные класса и константы) для класса или интерфейса и инициализации таких полей к стандартным значениям по умолчанию (§4.5.4). Это не требует выполнения любого кода Java; явные инициализаторы для static
поля выполняются как часть инициализации (§12.4), не подготовка. Реализации Java должны обнаружить следующую ошибку во время подготовки:
AbstractMethodError
: Класс, который, как объявляют, не является abstract
имеет abstract
метод. Это может произойти, например, если метод, который является первоначально нет abstract
изменяется, чтобы быть abstract
после другого класса, который наследовался теперь abstract
объявление метода было скомпилировано (§13.4.15). AbstractMethodError
должен быть брошен в точку в программе Java, которая заставила класс быть подготовленным. Во время для подготовки реализации виртуальной машины Java могут предварительно вычислить дополнительные структуры данных, чтобы сделать более поздние операции на классе или интерфейсе более эффективными. Одна особенно полезная структура данных является "таблицей метода" или другой структурой данных, которая позволяет любому методу быть вызванным на экземпляры класса, не требуя поиска суперклассов во время вызова.
Прежде, чем символьная ссылка может использоваться, это должно быть, подвергаются разрешению, в чем символьная ссылка проверяется, чтобы быть корректной и, обычно, заменяется прямой ссылкой, которая может быть более эффективно обработана, если ссылка неоднократно используется.
Если ошибка произойдет во время разрешения, то ошибка будет брошена. Наиболее обычно это будет экземпляром одного из следующих подклассов класса IncompatibleClassChangeError
, но это может также быть экземпляр некоторого другого подкласса IncompatibleClassChangeError
или даже экземпляр класса IncompatibleClassChangeError
непосредственно. Эта ошибка может быть брошена в любую точку в программе, которая использует символьную ссылку на тип, прямо или косвенно:
IllegalAccessError
: С символьной ссылкой встретились, который определяет использование или присвоение поля, или вызов метода, или создание экземпляра класса, к которому у кода, содержащего ссылку, нет доступа, потому что поле или метод были объявлены private
, protected
, или доступ по умолчанию (нет public
), или потому что класс не был объявлен public
. Это может произойти, например, если поле, которое первоначально объявляется public
изменяется, чтобы быть private
после того, как другой класс, который обращается к полю, был скомпилирован (§13.4.6).
InstantiationError
: С символьной ссылкой встретились, который используется в выражении создания экземпляра класса, но экземпляр не может быть создан, потому что ссылка, оказывается, обращается к интерфейсу или к abstract
класс. Это может произойти, например, если класс, который является первоначально нет abstract
изменяется, чтобы быть abstract
после того, как другой класс, который обращается к рассматриваемому классу, был скомпилирован (§13.4.1).
NoSuchFieldError
: С символьной ссылкой встретились, который обращается к определенному полю определенного класса или интерфейса, но класс или интерфейс не объявляют поле того имени (определенно не достаточно для этого просто быть наследованным полем того класса или интерфейса). Это может произойти, например, если полевое объявление было удалено из класса после другого класса, который обращается к полю, был скомпилирован (§13.4.7).
NoSuchMethodError
: С символьной ссылкой встретились, который обращается к определенному методу определенного класса или интерфейса, но класс или интерфейс не объявляют метод той подписи (определенно не достаточно для этого просто быть наследованным методом того класса или интерфейса). Это может произойти, например, если объявление метода было удалено из класса после другого класса, который обращается к методу, был скомпилирован (§13.4.12). UnsatisfiedLinkError
(подкласс LinkageError
) может быть брошен, если класс объявляет a native
метод, для которого не может быть найдена никакая реализация. Ошибка произойдет, если метод будет использоваться, или ранее в зависимости от того, какая стратегия разрешения используется виртуальной машиной (§12.3).Мы отмечаем, что гибкость, предоставленная, реализация Java в процессе редактирования не влияет на правильно сформированные программы Java, которые никогда не должны встречаться с ошибками редактирования.
static
поля (переменные класса) объявленный в классе. Инициализация интерфейса состоит из выполнения инициализаторов для полей (константы), объявленные там. Прежде, чем класс инициализируется, его суперкласс должен быть инициализирован, но интерфейсы, реализованные классом, не должны быть инициализированы. Точно так же суперинтерфейсы интерфейсной потребности не быть инициализированными перед интерфейсом инициализируются.
final
и static
, и это инициализируется со значением константного выражения времени компиляции (§15.27). Java определяет, что ссылка на постоянное поле должна быть разрешена во время компиляции к копии постоянной величины времени компиляции, таким образом, использование такого поля никогда не является активным использованием. См. §13.4.8 для дальнейшего обсуждения. Намерение здесь состоит в том, что у класса или интерфейсного типа есть ряд инициализаторов, которые помещают это в непротиворечивое состояние, и что это состояние является первым состоянием, которое наблюдается другими классами. Статические инициализаторы и инициализаторы переменной класса выполняются в текстовом порядке, и, возможно, не обращаются к переменным класса, объявленным в классе, объявления которого появляются дословно после использования, даже при том, что эти переменные класса находятся в контексте (§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
активно не используется. Class
объект был уже проверен и подготовлен, и что Class
объект содержит состояние, которое указывает на одну из четырех ситуаций: Class
объект проверяется и готовится, но не инициализируется.
Class
объект инициализируется некоторым определенным потоком T.
Class
объект полностью инициализируется и готов к употреблению.
Class
объект находится в ошибочном состоянии, возможно потому что шаг проверки или подготовки, отказавший, или потому что инициализация была предпринята и отказавшей. Class
объект, который представляет класс или интерфейс, который будет инициализирован. Это включает ожидание, пока текущий поток не может получить блокировку для того объекта (§17.13).
wait
(§20.1.6) на этом Class
объект (который временно выпускает блокировку). Когда текущий поток просыпается от wait
, повторите этот шаг.
Class
возразите и полный обычно.
Class
возразите и полный обычно.
Class
объект находится в ошибочном состоянии, тогда инициализация не возможна. Выпустите блокировку на Class
объект и бросок a NoClassDefFoundError
.
Class
объект теперь происходит текущим потоком, и выпустите блокировку на Class
объект.
Class
объект представляет класс, а не интерфейс, и суперкласс этого класса еще не был инициализирован, тогда рекурсивно выполните эту всю процедуру для суперкласса. В случае необходимости проверьте и подготовьте суперкласс сначала. Если инициализация суперкласса завершается резко из-за выданного исключения, то заблокируйте это Class
объект, маркируйте это ошибочным, уведомьте все потоки ожидания (§20.1.10), выпустите блокировку, и завершитесь резко, выдавая то же самое исключение, которое следовало из инициализации суперкласса.
final
переменные класса и поля интерфейсов, значения которых являются константами времени компиляции, инициализируются сначала (§8.3.2.1, §9.3.1, §13.4.8).
Class
объект, маркируйте, он полностью инициализировал, уведомьте все потоки ожидания (§20.1.10), выпустите блокировку, и обычно завершайте эту процедуру.
Error
или один из его подклассов, затем создайте новый экземпляр класса ExceptionInInitializerError
, с E как параметр, и использование этот объект вместо E в следующем шаге. Но если новый экземпляр ExceptionInInitializerError
не может быть создан потому что OutOfMemoryError
происходит, тогда вместо этого используйте OutOfMemoryError
объект вместо E в следующем шаге.
Class
объект, маркируйте это ошибочным, уведомьте все потоки ожидания (§20.1.10), выпустите блокировку, и завершите эту процедуру резко с причиной E или ее заменой как определено в предыдущем шаге. ExceptionInInitializerError
как описано здесь.) Class
объект полностью инициализируется и готов к употреблению, тогда вызов процедуры инициализации больше не необходим, и это может быть устранено из кода например, исправляя это или иначе регенерируя код. Анализ времени компиляции может, в некоторых случаях, быть в состоянии устранить многие из проверок, что тип был инициализирован от сгенерированного кода, если порядок инициализации на группу связанных типов может быть определен. Такой анализ должен, однако, полностью учесть факт, что Java параллелен и что код инициализации неограничен.
newInstance
метод (§20.3.6) класса Class
создает новый экземпляр класса, представленного Class
объект, для которого был вызван метод. String
литерал (§3.10.5) может создать новое String
объект (§20.12), чтобы представить тот литерал. (Это не могло бы произойти если то же самое String
был ранее интернирован (§3.10.5).)
String
объект представить результат. Операторы конкатенации строк могут также создать временные объекты обертки для значения типа примитива. Всякий раз, когда новый экземпляр класса создается, место в памяти выделяется для него с комнатой для всех переменных экземпляра, объявленных в типе класса и всех переменных экземпляра, объявленных в каждом суперклассе типа класса, включая все переменные экземпляра, которые могут быть скрыты. Если нет достаточного пространства, доступного, чтобы выделить память для объекта, то создание экземпляра класса завершается резко с OutOfMemoryError
. Иначе, все переменные экземпляра в новом объекте, включая объявленных в суперклассах, инициализируются к их значениям по умолчанию (§4.5.4). Непосредственно перед тем, как ссылка на недавно создаваемый объект возвращается как результат, обозначенный конструктор обрабатывается, чтобы инициализировать новый объект, используя следующую процедуру:
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
не объявляет конструкторов, конструктора по умолчанию формы: 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 для большего количества деталей об объявлениях конструктора.
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 {Мы поощряем реализации обрабатывать такие объекты как наличие финализатора, который не переопределяется, и завершить их более эффективно, как описано в §12.6.1.
super.finalize();
}
Финализатор может быть вызван явно, точно так же как любой другой метод.
Достижимый объект является любым объектом, к которому можно получить доступ в любом потенциальном вычислении продолжения от любого живого потока. Оптимизация преобразований программы может быть разработана, которые сокращают количество объектов, которые достижимы, чтобы быть меньше чем те, которых наивно считали бы достижимыми. Например, компилятор или генератор кода могут выбрать, явно или неявно, чтобы установить переменную или параметр, который больше не привыкнет к 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
методы быть сохраненным простой и что они быть запрограммированным защитно, так, чтобы они работали во всех случаях.
Как пример, если циркулярная соединенная группа незавершенных объектов становится недостижимой (или достижимый финализатором), то все объекты могут стать finalizable вместе. В конечном счете финализаторы для этих объектов могут быть вызваны в любом порядке, или даже одновременно использовании многократных потоков. Если автоматический менеджер по хранению позже находит, что объекты недостижимы, то их хранение может быть исправлено.
Это прямо, чтобы реализовать класс Java, который заставит ряд подобных финализатору методов быть вызванным в указанном порядке на ряд объектов, когда все объекты станут недостижимыми. Определение такого класса оставляют как осуществление для читателя.
classFinalize
это не берет параметров и не возвращает результата: static void classFinalize() throws Throwable { . . . }тогда этот метод будет вызван прежде, чем класс разгружается (§12.8). Как
finalize
метод для объектов, этот метод будет автоматически вызван только однажды. Этот метод может дополнительно быть объявлен private
, protected
, или public
. Класс не может быть разгружен, в то время как любой экземпляр его все еще достижим (§12.6). Класс или интерфейс не могут быть разгружены в то время как Class
объект, который представляет это, все еще достижим.
Классам, которые объявляют финализаторы класса (§12.7), выполнят эти финализаторы прежде, чем они будут разгружены.
exit
метод (§20.16.2) класса Runtime
или класс System
и работа выхода не запрещается менеджером безопасности (§20.17.13). runFinalizersOnExit
из класса System
с параметром true
. Значение по умолчанию не должно выполнить финализаторы на выходе, и это поведение может быть восстановлено, вызывая runFinalizersOnExit
с параметром false
. Вызов runFinalizersOnExit
метод разрешается, только если вызывающей стороне позволяют exit
, и иначе отклоняется SecurityManager
(§20.17).
Содержание | Предыдущий | Следующий | Индекс
Спецификация языка Java (HTML, сгенерированный Блинчиком "сюзет" Pelouch 24 февраля 1998)
Авторское право © Sun Microsystems, Inc 1996 года. Все права защищены
Пожалуйста, отправьте любые комментарии или исправления к doug.kramer@sun.com