![]() |
Spec-Zone .ru
спецификации, руководства, описания, API
|
Содержание | Предыдущий | Следующий | Индекс | Спецификация языка Java Третий Выпуск |
ГЛАВА 13
Средства разработки для языка программирования Java должны поддерживать автоматическую перекомпиляцию по мере необходимости всякий раз, когда исходный код доступен. Определенные реализации могут также сохранить источник, и двоичный файл вводит базу данных управления версиями и реализацию a ClassLoader
это использует механизмы целостности базы данных, чтобы предотвратить ошибки редактирования, обеспечивая совместимые с двоичным файлом версии типов клиентам.
Разработчики пакетов и классов, которые должны быть широко распределены, обращенным к различному набору проблем. В Интернете, который является нашим любимым примером широко распределенной системы, это часто непрактично или невозможно автоматически перекомпилировать существующие ранее двоичные файлы, которые прямо или косвенно зависят от типа, который должен быть изменен. Вместо этого эта спецификация определяет ряд изменений, которые разработчикам разрешают произвести в пакете или в классе или интерфейсном типе, сохраняя (не повреждающийся) совместимость с существующими двоичными файлами.
Бумага, заключенная в кавычки выше, кажется в Продолжениях OOPSLA '95, опубликованной как ACM SIGPLAN Уведомления, Объем 30, Номер 10, октябрь 1995, страницы 426-438. В пределах платформы той бумаги двоичные файлы языка программирования Java двоичные совместимый при всех соответствующих преобразованиях, которые авторы идентифицируют (с некоторыми протестами относительно добавления переменных экземпляра). Используя их схему, вот список некоторых важных двоичных совместимых изменений, которые поддерживает язык программирования Java:
private
поля, методы, или конструкторы класса.
Мы поощряем системы разработки предоставлять услуги, которые предупреждают разработчиков к воздействию изменений на существующих ранее двоичных файлах, которые не могут быть перекомпилированы.
Эта глава сначала определяет некоторые свойства, которые любой двоичный формат для языка программирования Java должен иметь (§13.1). Это затем определяет совместимость на уровне двоичных кодов, объясняя, что это и что это не (§13.2). Это наконец перечисляет большой набор возможных изменений к пакетам (§13.3), классы (§13.4) и интерфейсы (§13.5), определяя, какие из этих изменений, как гарантируют, сохранят совместимость на уровне двоичных кодов и которые не являются.
class
формат файла, определенный Спецификация виртуальной машины Java, или в представление, которое может быть отображено в тот формат загрузчиком класса, записанным в языке программирования Java. Кроме того, получающееся class
у файла должны быть определенные свойства. Много этих свойств определенно выбираются, чтобы поддерживать преобразования исходного кода, которые сохраняют совместимость на уровне двоичных кодов.
super
.f тогда суперкласс C является типом квалификации ссылки.
super
.f тогда суперкласс X является типом квалификации ссылки.
Ссылка на f должна быть скомпилирована в символьную ссылку на стирание (§4.6) типа квалификации ссылки, плюс простое имя поля, f. Ссылка должна также включать символьную ссылку на стирание объявленного типа поля так, чтобы верификатор мог проверить, что тип как ожидалось.
Если D Object
тогда тип квалификации выражения Object
. Иначе:
super
.m тогда суперкласс C является типом квалификации вызова метода.
super
.m тогда суперкласс X является типом квалификации вызова метода.
Ссылка на метод должна быть разрешена во время компиляции к символьной ссылке на стирание (§4.6) типа квалификации вызова плюс стирание подписи метода (§8.4.2). Ссылка на метод должна также включать или символьную ссылку на стирание типа возврата обозначенного метода или индикацию, которой объявляется обозначенный метод void
и не возвращает значение. Подпись метода должна включать все следующее:
new
D (...) или X.new
D (...), тогда тип квалификации вызова является D.
new
D (..) {...} или X.new
D (...) {...}, тогда тип квалификации выражения является типом времени компиляции выражения.
super
(...) или Основной.super
(...) тогда тип квалификации выражения является прямым суперклассом C.
this
(...), тогда тип квалификации выражения является C.
Ссылка на конструктора должна быть разрешена во время компиляции к символьной ссылке на стирание (§4.6) типа квалификации вызова плюс подпись конструктора (§8.8.2). Подпись конструктора должна включать обоих:
Кроме того, конструктор нечастного внутреннего задействованного класса должен быть скомпилирован так, что, он имеет как его первый параметр, дополнительный неявный параметр, представляющий сразу экземпляр включения (§8.1.3).
Object
, тогда символьная ссылка на стирание прямого суперкласса этого класса
class
поддержка формата файла эти изменения. Любой другой допустимый двоичный формат, такой как сжатое или зашифрованное представление, которое отображается назад в файлы класса загрузчиком класса под вышеупомянутыми требованиями, будет обязательно поддерживать эти изменения также. Двоичные файлы компилируются, чтобы положиться на доступные элементы и конструкторов других классов и интерфейсов. Чтобы сохранить совместимость на уровне двоичных кодов, класс или интерфейс должны обработать свои доступные элементы и конструкторов, их существование и поведение, как контракт с его пользователями.
Язык программирования Java разрабатывается, чтобы предотвратить дополнения к контрактам и случайным коллизиям имени от повреждающейся совместимости на уровне двоичных кодов; определенно:
Изменения в высокоуровневом классе и интерфейсных типах, которые не являются public
и это не суперкласс или суперинтерфейс, соответственно, a public
введите, влияйте только на типы в пределах пакета, в котором они объявляются. Такие типы могут быть удалены или иначе изменены, даже если несовместимости иначе описываются здесь, при условии, что двоичные файлы, на которые влияют, того пакета обновляются вместе.
abstract
изменяется, чтобы быть объявленным abstract
, тогда существующие ранее двоичные файлы, которые пытаются создать новые экземпляры того класса, бросят любого InstantiationError
во время ссылки, или (если отражающий метод используется), InstantiationException
во время выполнения; такое изменение поэтому не рекомендуется для широко распределенных классов.
Изменение класса, который был объявлен abstract
больше не быть объявленным abstract
не повреждает совместимость с существующими ранее двоичными файлами.
final
изменяется, чтобы быть объявленным final
, тогда a VerifyError
бросается, если двоичный файл существующего ранее подкласса этого класса загружается, потому что final
у классов не может быть никаких подклассов; такое изменение не рекомендуется для широко распределенных классов.
Изменение класса, который был объявлен final
больше не быть объявленным final
не повреждает совместимость с существующими ранее двоичными файлами.
public
быть объявленным public
не повреждает совместимость с существующими ранее двоичными файлами.
Если класс, который был объявлен public
изменяется, чтобы не быть объявленным public
, тогда IllegalAccessError
бросается, если существующий ранее двоичный файл соединяется, что у потребностей, но больше есть доступ к типу класса; такое изменение не рекомендуется для широко распределенных классов.
ClassCircularityError
бросается во время загрузки, если класс был бы суперклассом себя. Изменения к иерархии классов, которая могла привести к такому зацикливанию, когда недавно скомпилированные двоичные файлы загружаются существующими ранее двоичными файлами, не рекомендуются для широко распределенных классов.Изменение прямого суперкласса или набора прямых суперинтерфейсов типа класса не будет повреждать совместимость с существующими ранее двоичными файлами, при условии, что полный набор суперклассов или суперинтерфейсов, соответственно, типа класса не теряет элементов.
Если изменение к прямому суперклассу или набору прямых результатов суперинтерфейсов в любом классе или интерфейсе, больше являющемся суперклассом или суперинтерфейсом, соответственно, то разовые ссылкой ошибки могут закончиться, существуя ранее двоичные файлы, загружается двоичным файлом измененного класса. Такие изменения не рекомендуются для широко распределенных классов.
Например, предположите что следующая тестовая программа:
компилируется и выполняется, производя вывод:class Hyper { char h = 'h'; } class Super extends Hyper { char s = 's'; } class Test extends Super { public static void printH(Hyper h) { System.out.println(h.h); } public static void main(String[] args) { printH(new Super()); } }
Предположите что новая версия классаh
Super
тогда компилируется:
Эта версия классаclass Super { char s = 's'; }
Super
не подкласс Hyper
. Если мы тогда выполняем существующие двоичные файлы Hyper
и Test
с новой версией Super
, тогда a VerifyError
бросается во время ссылки. Верификатор возражает потому что результат new
Super()
не может быть передан как параметр вместо формального параметра типа Hyper
, потому что Super
не подкласс Hyper
.Это поучительно, чтобы рассмотреть то, что могло бы произойти без шага проверки: программа могла бы работать и печать:
Это демонстрирует, что без верификатора система типов могла быть побеждена, соединяя непоследовательные двоичные файлы, даже при том, что каждый был произведен корректным компилятором Java.s
Урок - то, что реализация, которая испытывает недостаток в верификаторе или сбоях, чтобы использовать это, не будет поддерживать безопасность типов и, поэтому, не допустимая реализация.
Обсуждение
Отметьте что, если такие переменные типа используются в типе поля или метода, у которого могут быть нормальные импликации изменения вышеупомянутого типа.
Изменение первого, связанного параметра типа, изменит стирание (§4.6) любого элемента, который использует ту переменную типа в ее собственном типе, и это может произвести совместимость на уровне двоичных кодов. Изменение любого другого связанного не имеет никакого эффекта на совместимость на уровне двоичных кодов.
static
) элемент, у которого есть то же самое имя, доступность, (для полей) или то же самое имя, доступность, подпись, и тип возврата (для методов) как экземпляр (соответственно static
) элемент суперкласса или подкласса. Никакая ошибка не происходит, даже если бы набор соединяемых классов встретился бы с ошибкой времени компиляции.
Удаление элемента класса или конструктора, который не объявляется private
может вызвать ошибку редактирования, если элемент или конструктор используются существующим ранее двоичным файлом.
Если программа:
компилируется и выполняется, это производит вывод:class Hyper { void hello() { System.out.println("hello from Hyper"); } } class Super extends Hyper { void hello() { System.out.println("hello from Super"); } } class Test { public static void main(String[] args) { new Super().hello(); } }
Предположите что новая версия классаhello from Super
Super
производится:
тогда перекомпиляцияclass Super extends Hyper { }
Super
и выполнение этого нового двоичного файла с исходными двоичными файлами для Test
и Hyper
производит вывод:
как ожидалось.hello from Hyper
super
ключевое слово может использоваться, чтобы получить доступ к методу, объявленному в суперклассе, обходя любые методы, объявленные в текущем классе. Выражение:
разрешается, во время компиляции, к методу M в суперклассе S. Если метод M является методом экземпляра, то MR метода, вызванный во время выполнения, является методом с той же самой подписью как М., который является элементом прямого суперкласса класса, содержащего включение выраженияsuper.Identifier
super
. Таким образом, если программа:
компилируется и выполняется, это производит вывод:class Hyper { void hello() { System.out.println("hello from Hyper"); } } class Super extends Hyper { } class Test extends Super { public static void main(String[] args) { new Test().hello(); } void hello() { super.hello(); } }
Предположите что новая версия классаhello from Hyper
Super
производится:
Еслиclass Super extends Hyper { void hello() { System.out.println("hello from Super"); } }
Super
и Hyper
перекомпилированы, но нет Test
, тогда выполняя новые двоичные файлы с существующим двоичным файлом Test
производит вывод:
поскольку Вы могли бы ожидать. (Дефект в некоторых ранних реализациях, вызванных их, чтобы напечатать:hello from Super
неправильно.)hello from Hyper
private
доступ; от protected
доступ, чтобы принять значение по умолчанию или private
доступ; или от public
доступ к protected
, значение по умолчанию, или private
доступ. Изменение элемента или конструктора, чтобы разрешить меньше доступа поэтому не рекомендуется для широко распределенных классов.Возможно, удивительно двоичный формат определяется так, чтобы изменение элемента или конструктора, чтобы быть более доступным не вызвало ошибку редактирования, когда подкласс (уже) определяет метод, чтобы иметь меньше доступа.
Так, например, если пакет points
определяет класс Point
:
package points; public class Point { public int x, y; protected void print() { System.out.println("(" + x + "," + y + ")"); } }
тогда эти классы компиляция иclass Test extends points.Point { protected void print() { System.out.println("Test"); } public static void main(String[] args) { Test t = new Test(); t.print(); } }
Test
выполняется, чтобы произвести вывод:
Если методTest
print
в классе Point
изменяется, чтобы быть public
, и затем только Point
класс перекомпилирован, и затем выполнен с ранее существующим двоичным файлом для Test
тогда никакая ошибка редактирования не происходит, даже при том, что это является неподходящим, во время компиляции, для a public
метод, который будет переопределен a protected
метод (как показано фактом, что класс Test
не мог быть перекомпилирован, используя это новое Point
класс, если печать не были изменены, чтобы быть public
.)Разрешение суперклассов измениться protected
методы, чтобы быть public
не повреждая двоичные файлы существующих ранее подклассов помогает сделать двоичные файлы менее хрупкими. Альтернатива, где такое изменение вызвало бы ошибку редактирования, создаст дополнительные двоичные несовместимости.
Примите ссылку на поле f с квалификацией типа T. Предположите далее, что f является фактически экземпляром (соответственно static
) поле, объявленное в суперклассе T, S, и что тип f X. Если новое поле типа X с тем же самым именем как f добавляется к подклассу S, который является суперклассом T или T непосредственно, то ошибка редактирования может произойти. Такая ошибка редактирования произойдет, только если, в дополнение к вышеупомянутому, любому из следующих условий содержите:
В частности никакая ошибка редактирования не произойдет в случае, где класс больше не мог быть перекомпилирован потому что доступ к полю, на который ранее ссылаются поле суперкласса с несовместимым типом. Ранее скомпилированный класс с такой ссылкой будет продолжать ссылаться на поле, объявленное в суперклассе.
Таким образом компилируя и выполняя код:
производит вывод:class Hyper { String h = "hyper"; } class Super extends Hyper { String s = "super"; } class Test { public static void main(String[] args) { System.out.println(new Super().h); } }
Изменениеhyper
Super
быть определенным как:
перекомпиляцияclass Super extends Hyper { String s = "super"; int h = 0; }
Hyper
и Super
, и выполнение получающихся новых двоичных файлов со старым двоичным файлом Test
производит вывод:
Полеhyper
h
из Hyper
выводится исходным двоичным файлом main
. В то время как это может казаться удивительным сначала, это служит, чтобы сократить количество несовместимостей, которые происходят во время выполнения. (В идеальном мире будут перекомпилированы все исходные файлы, которые нуждались в перекомпиляции всякий раз, когда любой из них изменился, устраняя такие неожиданности. Но такая массовая перекомпиляция часто непрактична или невозможна, особенно в Интернете. И, как был ранее отмечен, такая перекомпиляция будет иногда требовать дальнейших изменений к исходному коду.)Как пример, если программа:
компилируется и выполняется, это производит вывод:class Hyper { String h = "Hyper"; } class Super extends Hyper { } class Test extends Super { public static void main(String[] args) { String s = new Test().h; System.out.println(s); } }
Предположите что новая версия классаHyper
Super
тогда компилируется:
Если получающийся двоичный файл используется с существующими двоичными файлами дляclass Super extends Hyper { char h = 'h'; }
Hyper
и Test
, тогда вывод все еще:
даже при том, что компиляция источника для этих двоичных файлов:Hyper
привел бы к ошибке времени компиляции, потому чтоclass Hyper { String h = "Hyper"; } class Super extends Hyper { char h = 'h'; } class Test extends Super { public static void main(String[] args) { String s = new Test().h; System.out.println(s); } }
h
в исходном коде для main
был бы теперь рассмотрен как обращающийся к char
поле, объявленное в Super
, и a char
значение не может быть присвоено a String
.
Удаление поля от класса повредит совместимость с любыми существующими ранее двоичными файлами, которые ссылаются на это поле, и a NoSuchFieldError
будет брошен, когда такая ссылка от существующего ранее двоичного файла соединяется. Только private
поля могут быть безопасно удалены из широко распределенного класса.
В целях совместимости на уровне двоичных кодов добавляя или удаляя поле f, тип которого включает переменные типа (§4.4) или параметризованные типы (§4.5) эквивалентен дополнению (соответственно, удаление) поля того же самого имени, тип которого является стиранием (§4.6) типа f.
final
изменяется, чтобы быть final
, тогда это может повредить совместимость с существующими ранее двоичными файлами, которые пытаются присвоить новые значения полю. Например, если программа:
компилируется и выполняется, это производит вывод:class Super { static char s; } class Test extends Super { public static void main(String[] args) { s = 'a'; System.out.println(s); } }
Предположите что новая версия классаa
Super
производится:
Еслиclass Super { final static char s = 'b'; }
Super
перекомпилирован, но нет Test
, тогда выполняя новый двоичный файл с существующим двоичным файлом Test
результаты в a IllegalAccessError
.
Удаление ключевого слова final
или изменение значения, к которому инициализируется поле, не повреждает совместимость с существующими двоичными файлами.
Если поле является постоянной переменной (§4.12.4), то удаление ключевого слова final
или изменение его значения не будет повреждать совместимость с существующими ранее двоичными файлами, заставляя их не работать, но они не будут видеть нового значения для использования поля, если они не будут перекомпилированы. Это - истина, даже если само использование не является константным выражением времени компиляции (§15.28)
Если пример:
компилируется и выполняется, это производит вывод:class Flags { final static boolean debug = true; } class Test { public static void main(String[] args) { if (Flags.debug) System.out.println("debug is true"); } }
Предположите что новая версия классаdebug is true
Flags
производится:
Еслиclass Flags { final static boolean debug = false; }
Flags
перекомпилирован, но нет Test
, тогда выполняя новый двоичный файл с существующим двоичным файлом Test
производит вывод:
потому что значениеdebug is true
debug
был постоянное время компиляции, и, возможно, использовалось в компиляции Test
не делая ссылку на класс Flags
.Этим результатом является побочный эффект решения поддерживать условную компиляцию, как обсуждено в конце §14.21.
Это поведение не изменилось бы если Flags
были изменены, чтобы быть интерфейсом, как в измененном примере:
(Одна причина требования встраивания констант является этимinterface Flags { boolean debug = true; } class Test { public static void main(String[] args) { if (Flags.debug) System.out.println("debug is true"); } }
switch
операторы требуют констант на каждом case
, и никакие две таких постоянных величины не могут быть тем же самым. Компилятор проверяет на двойные постоянные величины в a switch
оператор во время компиляции; class
формат файла не делает символьного редактирования case
значения.)Лучший способ избежать проблем с "неустойчивыми константами" в широко распределенном коде состоит в том, чтобы объявить, поскольку константы времени компиляции только оценивают, которые действительно маловероятны когда-либо измениться. Кроме для истинных математических констант, мы рекомендуем, чтобы исходный код сделал очень экономящее использование переменных класса, которые объявляются static
и final
. Если природа только для чтения final
требуется, лучший выбор состоит в том, чтобы объявить a private
static
переменная и подходящий метод средства доступа, чтобы получить его значение. Таким образом мы рекомендуем:
вместо:private static int N; public static int getN() { return N; }
Нет никакой проблемы с:public static final int N = ...;
еслиpublic static int N = ...;
N
не должно быть только для чтения. Мы также рекомендуем, как правило, чтобы только действительно постоянные величины были объявлены в интерфейсах. Мы отмечаем, но не рекомендуем, что, если поле типа примитива интерфейса может измениться, его значение может быть выражено идиоматично как в:
обеспечение, чтобы это значение не было константой. Подобные идиомы существуют для других типов примитивов.interface Flags { boolean debug = new Boolean(true).booleanValue(); }
Одна другая вещь отметить является этим static
final
поля, у которых есть постоянные величины (ли из примитивных или String
введите), никогда должно казаться, не имеет начальное значение по умолчанию для их типа (§4.12.5). Это означает, что все такие поля, кажется, инициализируются сначала во время инициализации класса (§8.3.2.1, §9.3.1, §12.4.2).
private
не был объявлен static
и изменяется, чтобы быть объявленным static
, или наоборот, затем ошибка времени редактирования, определенно IncompatibleClassChangeError
, закончится, если поле будет использоваться существующим ранее двоичным файлом, который ожидал поле другого вида. Такие изменения не рекомендуются в коде, который был широко распределен.transient
модификатор поля не повреждает совместимость с существующими ранее двоичными файлами.
Примите ссылку на метод м. с квалификацией типа T. Предположите далее, что м. является фактически экземпляром (соответственно static
) метод, объявленный в суперклассе T, S. Если новый метод типа X с той же самой подписью и типа возврата как м. добавляется к подклассу S, который является суперклассом T или T непосредственно, то ошибка редактирования может произойти. Такая ошибка редактирования произойдет, только если, в дополнение к вышеупомянутому, любому из следующих условий содержите:
static
(соответственно экземпляр) метод. NoSuchMethodError
может быть брошен, когда такая ссылка от существующего ранее двоичного файла соединяется. Такая ошибка произойдет, только если никакой метод с соответствующей подписью и не возвращается, тип объявляется в суперклассе. Если исходный код для класса не содержит объявленных конструкторов, компилятор Java автоматически предоставляет конструктора без параметров. Добавление одного или более объявлений конструктора к исходному коду такого класса будет препятствовать тому, чтобы этот конструктор по умолчанию был предоставлен автоматически, эффективно удаляя конструктора, если у одного из новых конструкторов также не будет никаких параметров, таким образом заменяя конструктора по умолчанию. Автоматически предоставленному конструктору без параметров дают тот же самый модификатор доступа как класс его объявления, таким образом, у любой замены должны быть так много или больше доступа, если совместимость с существующими ранее двоичными файлами должна быть сохранена.
Обсуждение
Отметьте что, если такие переменные типа используются в типе метода или конструктора, у которого могут быть нормальные импликации изменения вышеупомянутого типа.
Изменение первого, связанного параметра типа, может изменить стирание (§4.6) любого элемента, который использует ту переменную типа в ее собственном типе, и это может произвести совместимость на уровне двоичных кодов. Определенно:
В целях совместимости на уровне двоичных кодов добавляя или удаляя метод или конструктора м., подпись которого включает переменные типа (§4.4) или параметризованные типы (§4.5), эквивалентен дополнению (соответственно, удаление) иначе эквивалентного метода, подпись которого является стиранием (§4.6) подписи м.
void
, или замена void
с результатом у типа есть совместное воздействие удаления старого метода и добавления нового метода с новым типом результата или недавно void
результат (см. §13.4.12).В целях совместимости на уровне двоичных кодов добавляя или удаляя метод или конструктора м., тип возврата которого включает переменные типа (§4.4) или параметризованные типы (§4.5), эквивалентен дополнению (соответственно, удаление) иначе эквивалентный метод, тип возврата которого является стиранием (§4.6) типа возврата м.
abstract
больше не быть объявленным abstract
не повреждает совместимость с существующими ранее двоичными файлами.
Изменение метода, который не объявляется abstract
быть объявленным abstract
повредит совместимость с существующими ранее двоичными файлами, которые ранее вызвали метод, вызывая AbstractMethodError
.
Если пример программы:
компилируется и выполняется, это производит вывод:class Super { void out() { System.out.println("Out"); } } class Test extends Super { public static void main(String[] args) { Test t = new Test(); System.out.println("Way "); t.out(); } }
Предположите что новая версия классаWay Out
Super
производится:
Еслиabstract class Super { abstract void out(); }
Super
перекомпилирован, но нет Test
, тогда выполняя новый двоичный файл с существующим двоичным файлом Test
результаты в a AbstractMethodError
, потому что класс Test
не имеет никакой реализации метода out
, и, поэтому (или должен быть), краткий обзор. final
быть final
может повредить совместимость с существующими двоичными файлами, которые зависят от возможности переопределить метод. Если тестовая программа:
компилируется и выполняется, это производит вывод:class Super { void out() { System.out.println("out"); } } class Test extends Super { public static void main(String[] args) { Test t = new Test(); t.out(); } void out() { super.out(); } }
Предположите что новая версия классаout
Super
производится:
Еслиclass Super { final void out() { System.out.println("!"); } }
Super
перекомпилирован, но нет Test
, тогда выполняя новый двоичный файл с существующим двоичным файлом Test
результаты в a VerifyError
потому что класс Test
ненадлежащим образом попытки переопределить метод экземпляра out
.
Изменение класса (static
) метод, который не является final
быть final
не повреждает совместимость с существующими двоичными файлами, потому что метод, возможно, не был переопределен.
Удаление final
модификатор от метода не повреждает совместимость с существующими ранее двоичными файлами.
native
модификатор метода не повреждает совместимость с существующими ранее двоичными файлами.
Воздействие изменений к типам на существовании ранее native
методы, которые не перекомпилированы, выходят за рамки этой спецификации и должны быть предоставлены описание реализации. Реализации поощряются, но не требуются, чтобы реализовать native
методы в пути, который ограничивает такое воздействие.
private
был объявлен static
(то есть, метод класса), и изменяется, чтобы не быть объявленным static
(то есть, к методу экземпляра), или наоборот, тогда совместимость с существующими ранее двоичными файлами может быть повреждена, приводя к ошибке времени редактирования, а именно, IncompatibleClassChangeError
, если эти методы используются существующими ранее двоичными файлами. Такие изменения не рекомендуются в коде, который был широко распределен.synchronized
модификатор метода не повреждает совместимость с существующими двоичными файлами.throws
пункт методов или конструкторов не повреждает совместимость с существующими двоичными файлами; эти пункты проверяются только во время компиляции.Мы отмечаем, что компилятор не может развернуть метод, встроенный во время компиляции.
Ключевое слово final
на методе не означает, что метод может быть безопасно встроен; это означает только, что метод не может быть переопределен. Все еще возможно, что новая версия того метода будет обеспечена во время ссылки. Кроме того структура исходной программы должна быть сохранена в целях отражения.
Вообще мы предлагаем, чтобы реализации использовали последний ограниченный (время выполнения) генерация кода и оптимизация.
В то время как добавление нового перегруженного метода или конструктора может вызвать ошибку времени компиляции в следующий раз, когда класс или интерфейс компилируются, потому что нет никакого метода или конструктора, который является самым определенным (§15.12.2.5), никакая такая ошибка не происходит, когда программа выполняется, потому что никакое разрешение перегрузки не делается во время выполнения.
Если пример программы:
компилируется и выполняется, это производит вывод:class Super { static void out(float f) { System.out.println("float"); } } class Test { public static void main(String[] args) { Super.out(2); } }
Предположите что новая версия классаfloat
Super
производится:
Еслиclass Super { static void out(float f) { System.out.println("float"); } static void out(int i) { System.out.println("int"); } }
Super
перекомпилирован, но нет Test
, тогда выполняя новый двоичный файл с существующим двоичным файлом Test
все еще производит вывод:
Однако, еслиfloat
Test
тогда перекомпилирован, используя это новое Super
, вывод тогда:
как, возможно, наивно ожидался в предыдущем случае.int
Если предварительно скомпилированный двоичный файл попытается получить доступ к перечислимой константе, которая больше не существует, то клиент перестанет работать во время выполнения с a NoSuchFieldError
. Поэтому такое изменение не рекомендуется для широко распределенных перечислений.
Во всех других отношениях правила совместимости на уровне двоичных кодов для перечислений идентичны тем для классов.
public
быть объявленным public
не повреждает совместимость с существующими ранее двоичными файлами.
Если интерфейс, который объявляется public
изменяется, чтобы не быть объявленным public
, тогда IllegalAccessError
бросается, если существующий ранее двоичный файл соединяется, что у потребностей, но больше есть доступ к интерфейсному типу, таким образом, такое изменение не рекомендуется для широко распределенных интерфейсов.
VerifyError
.IncompatibleClassChangeError
закончится. Если исходная ссылка была присвоением, IllegalAccessError
закончится.Удаление элемента от интерфейса может вызвать ошибки редактирования в существующих ранее двоичных файлах.
Если пример программы:
компилируется и выполняется, это производит вывод:interface I { void hello(); } class Test implements I { public static void main(String[] args) { I anI = new Test(); anI.hello(); } public void hello() { System.out.println("hello"); } }
Предположите что новая версия интерфейсаhello
I
компилируется:
Еслиinterface I { }
I
перекомпилирован, но нет Test
, тогда выполняя новый двоичный файл с существующим двоичным файлом для Test
приведет к a NoSuchMethodError
. (В некоторых ранних реализациях все еще выполнилась эта программа; факт, что метод hello
больше не существует в интерфейсе I
не был правильно обнаружен.)static
final
поля в классах, как описано в §13.4.8 и §13.4.9.abstract
методы в классах, как описано в §13.4.14, §13.4.15, §13.4.21, и §13.4.23.Добавление или удаление аннотаций не имеют никакого эффекта на корректное редактирование двоичных представлений программ в языке программирования Java.
Содержание | Предыдущий | Следующий | Индекс | Спецификация языка Java Третий Выпуск |
Авторское право © 1996-2005 Sun Microsystems, Inc. Все права защищены
Пожалуйста, отправьте любые комментарии или исправления через нашу