Spec-Zone .ru
спецификации, руководства, описания, API
|
ГЛАВА 13
Средства разработки Java должны поддерживать автоматическую перекомпиляцию по мере необходимости всякий раз, когда исходный код доступен. Определенные реализации Java могут также сохранить источник, и двоичный файл вводит базу данных управления версиями и реализацию a ClassLoader
(§20.14), который использует механизмы целостности базы данных, чтобы предотвратить ошибки редактирования, обеспечивая совместимые с двоичным файлом версии типов клиентам.
Разработчики пакетов и классов, которые должны быть широко распределены, обращенным к различному набору проблем. В Интернете, который является нашим любимым примером широко распределенной системы, это часто непрактично или невозможно автоматически перекомпилировать существующие ранее двоичные файлы, которые прямо или косвенно зависят от типа, который должен быть изменен. Вместо этого Java определяет ряд изменений, которые разработчикам разрешают произвести в пакете или в классе или интерфейсном типе, сохраняя (не повреждающийся) совместимость с существующими двоичными файлами.
Бумага, заключенная в кавычки выше, кажется в Продолжениях OOPSLA '95, опубликованной как ACM SIGPLAN Уведомления, Объем 30, Номер 10, октябрь 1995, страницы 426-438. В пределах платформы той бумаги двоичные файлы Java являются двоичными (от выпуска к выпуску), совместимый при всех соответствующих преобразованиях, которые идентифицируют авторы. Используя их схему, вот список некоторых важных двоичных совместимых изменений, которые поддерживает Java:
private
поля, методы, или конструкторы класса или интерфейса.
Мы поощряем системы разработки Java предоставлять услуги, которые предупреждают разработчиков к воздействию изменений на существующих ранее двоичных файлах, которые не могут быть перекомпилированы.
Эта глава сначала определяет некоторые свойства, которые любой двоичный формат Java должен иметь (§13.1). Это затем определяет совместимость на уровне двоичных кодов, объясняя, что это и что это не (§13.2). Это наконец перечисляет большой набор возможных изменений к пакетам (§13.3), классы (§13.4) и интерфейсы (§13.5), определяя, какие изменения, как гарантируют, сохранят совместимость на уровне двоичных кодов и которые не являются.
class
формат файла, определенный Спецификация виртуальной машины Java, эта спецификация не передает под мандат использование любого определенного формата двоичного файла. Скорее это определяет свойства, которым должен повиноваться любой двоичный формат для скомпилированных типов. Много этих свойств определенно выбираются, чтобы поддерживать преобразования исходного кода, которые сохраняют совместимость на уровне двоичных кодов.
static
, final
, и инициализированный с константными выражениями времени компиляции разрешаются во время компиляции к постоянной величине, которая обозначается. Никакая ссылка на такое постоянное поле не должна присутствовать в коде в двоичном файле (кроме в классе или интерфейсе, содержащем постоянное поле, у которого будет код, чтобы инициализировать это), и такие постоянные поля, должно всегда казаться, были инициализированы; начальное значение по умолчанию для типа такого поля никогда не должно наблюдаться. См. §13.4.8 для обсуждения.
void
и не возвращает значение. Подпись метода должна включать все следующее:
java.lang.Object
, тогда символьная ссылка на прямой суперкласс этого класса
private
объявленный в классе или интерфейсе, данном как простое имя поля и символьной ссылки на тип поля
private
объявленный в классе или интерфейсе, его подписи и типе возврата, как описано выше
Следующие разделы определяют изменения, которые могут быть произведены, чтобы классифицировать и соединить интерфейсом с описаниями типа, не повреждая совместимость с существующими ранее двоичными файлами. Виртуальная машина Java и ее стандарт class
поддержка формата файла эти изменения; другие двоичные форматы Java обязаны поддерживать эти изменения также.
Как описано в §13.1, символьные ссылки на методы и поля называют точный класс или интерфейс, в котором объявляются метод или поле. Это означает, что двоичные файлы компилируются, чтобы положиться на доступные элементы и конструкторов других классов и интерфейсов. Чтобы сохранить совместимость на уровне двоичных кодов, класс или интерфейс должны обработать эти доступные элементы и конструкторов, их существование и поведение, как контракт с пользователями класса или интерфейса.
Java разрабатывается, чтобы предотвратить дополнения к контрактам и случайным коллизиям имени от повреждающейся совместимости на уровне двоичных кодов; определенно:
Мы надеемся сделать некоторые улучшения будущих версий Java, чтобы лучше поддерживать и источник и двоичное совместимое развитие типов. В частности мы полагаем, что механизм позволяет классу реализовывать два интерфейса, которые имеют методы с той же самой подписью, но должны считаться отличающимися или иметь различные типы возврата. Мы приветствуем предложения и предложения, которые помогли бы нам сделать дополнительные улучшения, или на управляющее имя и конфликты подписи или на другие источники несовместимости.
Изменения в классе и интерфейсных типах, которые не являются public
и это не суперкласс или суперинтерфейс, соответственно, a public
введите, влияйте только на типы в пределах пакета, в котором они объявляются. Такие типы могут быть удалены или иначе изменены, даже если несовместимости иначе описываются здесь, при условии, что двоичные файлы, на которые влияют, того пакета обновляются вместе.
abstract
Классыabstract
изменяется, чтобы быть объявленным abstract
, тогда пред-существующие двоичные файлы, которые пытаются создать новые экземпляры того класса, бросят любого InstantiationError
во время ссылки, или InstantiationException
во время выполнения (если метод newInstance
(§20.3.6) класса Class
используется); такое изменение поэтому не рекомендуется для широко распределенных классов. Изменение класса, который был объявлен abstract
больше не быть объявленным abstract
не повреждает совместимость с существующими ранее двоичными файлами.
final
Классыfinal
изменяется, чтобы быть объявленным final
, тогда a VerifyError
бросается, если двоичный файл существующего ранее подкласса этого класса загружается, потому что final
у классов не может быть никаких подклассов; такое изменение не рекомендуется для широко распределенных классов. Изменение класса, который был объявлен final
больше не быть объявленным final
не повреждает совместимость с существующими ранее двоичными файлами.
public
Классыpublic
быть объявленным public
не повреждает совместимость с существующими ранее двоичными файлами. Если класс, который был объявлен public
изменяется, чтобы не быть объявленным public
, тогда IllegalAccessError
бросается, если существующий ранее двоичный файл соединяется, что у потребностей, но больше есть доступ к типу класса; такое изменение не рекомендуется для широко распределенных классов.
ClassCircularityError
бросается во время загрузки, если класс был бы суперклассом себя. Изменения к иерархии классов, которая могла привести к такому зацикливанию, когда недавно скомпилированные двоичные файлы загружаются существующими ранее двоичными файлами, не рекомендуются для широко распределенных классов. Изменение прямого суперкласса или набора прямых суперинтерфейсов типа класса не будет повреждать совместимость с существующими ранее двоичными файлами, при условии, что полный набор суперклассов или суперинтерфейсов, соответственно, типа класса не теряет элементов.
Изменения к набору суперклассов класса не будут повреждать совместимость с существующими ранее двоичными файлами просто из-за использования переменных класса и методов класса. Это - то, потому что использование переменных класса и методов класса разрешается во время компиляции к символьным ссылкам на имя класса, который объявляет их. Такое использование поэтому зависит только от продолжающегося существования класса, объявляя переменную или метод, не на форме иерархии классов.
Если изменение к прямому суперклассу или набору прямых результатов суперинтерфейсов в любом классе или интерфейсе, больше являющемся суперклассом или суперинтерфейсом, соответственно, то разовые ссылкой ошибки могут закончиться, существуя ранее двоичные файлы, загружается двоичным файлом измененного класса. Такие изменения не рекомендуются для широко распределенных классов. Получающиеся ошибки обнаруживаются верификатором виртуальной машины Java, когда работа, которая ранее скомпилировала, нарушит систему типов. Например, предположите что следующая тестовая программа:
class Hyper { char h = 'h'; } class Super extends Hyper { char s = 's'; } class Test extends Super { public static void main(String[] args) { Hyper h = new Super(); System.out.println(h.h); } }компилируется и выполняется, производя вывод:
hПредположите что новая версия класса
Super
тогда компилируется: class Super { char s = 's'; }Эта версия класса
Super
не подкласс Hyper
. Если мы тогда выполняем существующие двоичные файлы Hyper
и Test
с новой версией Super
, тогда a VerifyError
бросается во время ссылки. Верификатор возражает потому что результат new
Super()
не может быть присвоен переменной типа Hyper
, потому что Super
не подкласс Hyper
. Это поучительно, чтобы рассмотреть то, что могло бы произойти без шага проверки: программа могла бы работать и печать:
sЭто демонстрирует, что без верификатора система типов могла быть побеждена, соединяя непоследовательные двоичные файлы, даже при том, что каждый был произведен корректным компилятором Java.
Как дальнейший пример, вот реализация броска от ссылочного типа до int
, который мог быть сделан работать в определенных реализациях Java, если бы они были не в состоянии выполнить процесс проверки. Примите реализацию, что метод использования диспетчеризирует таблицы и чей компоновщик присваивает смещения в те таблицы последовательным и прямым способом. Затем предположите, что следующий код Java компилируется:
class Hyper { int zero(Object o) { return 0; } } class Super extends Hyper { int peek(int i) { return i; } }
class Test extends Super { public static void main(String[] args) throws Throwable { Super as = new Super(); System.out.println(as); System.out.println(Integer.toHexString(as.zero(as))); } }Принятая реализация решает что класс
Super
имеет два метода: первым является метод zero
унаследованный от класса Hyper
, и вторым является метод peek
. Любой подкласс Super
также имел бы эти те же самые два метода в первых двух записях его таблицы метода. (Фактически, всем этим методам предшествовали бы в таблицах метода все методы, унаследованные от класса Object
но, чтобы упростить обсуждение, мы игнорируем это здесь.) Для вызова метода as.zero(as)
, компилятор определяет, что первый метод таблицы метода должен быть вызван; это всегда корректно, если безопасность типов сохраняется. Если скомпилированный код тогда выполняется, он печатает что-то как:
Super@ee300858 0который является корректным выводом. Но если новая версия
Super
компилируется, который является тем же самым за исключением extends
пункт: class Super { int peek(int i) { return i; } }тогда первый метод в таблице метода для
Super
теперь будет peek
, нет zero
. Используя новый двоичный код для Super
со старым двоичным кодом для Hyper
и Test
вызовет вызов метода as.zero(as)
диспетчеризировать методу peek
в Super
, а не метод zero
в Hyper
. Это - нарушение типа, конечно; параметр имеет тип Super
но параметр имеет тип int
. С несколькими вероятными предположениями о внутренних представлениях данных и последствиях нарушения типа, выполнение этой неправильной программы могло бы произвести вывод: Super@ee300848 ee300848A
poke
метод, способный к изменению любого расположения в памяти, мог быть придуман подобным образом. Это оставляют как осуществление для читателя. Урок - то, что реализация Java, который испытывает недостаток в верификаторе или сбоях, чтобы использовать ее, не будет поддерживать безопасность типов и, поэтому, не допустимая реализация Java.
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
. Удаление элемента класса или конструктора, который не объявляется 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
и Hyper
перекомпилированы, но нет Test
, тогда a NoSuchMethodError
закончится во время ссылки, потому что метод hello
больше не объявляется в классе Super
. Чтобы сохранить совместимость на уровне двоичных кодов, методы не должны быть удалены; вместо этого, "передача методов" должна использоваться. В нашем примере, заменяя объявление Super
с:
class Super extends Hyper { void hello() { super.hello(); } }тогда перекомпиляция
Super
и Hyper
и выполнение этих новых двоичных файлов с исходным двоичным файлом для Test
, производит вывод: hello from Hyperкак, возможно, наивно ожидался от предыдущего примера.
super
ключевое слово может использоваться, чтобы получить доступ к методу, объявленному в суперклассе, обходя любые методы, объявленные в текущем классе. Выражение:
super.
Identifier
разрешается, во время компиляции, к методу M, объявленному в определенном суперклассе S. Метод M должен все еще быть объявлен в том классе во время выполнения, или ошибка редактирования закончится. Если метод M является методом экземпляра, то MR метода, вызванный во время выполнения, является методом с той же самой подписью как М., который является элементом прямого суперкласса класса, содержащего включение выражения 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поскольку Вы могли бы ожидать. (Дефект в некоторых ранних версиях Java, вызванного их, чтобы напечатать:
hello from Hyperнеправильно.)
private
доступ; от protected
доступ, чтобы принять значение по умолчанию или private
доступ; или от public
доступ к protected
, значение по умолчанию, или private
доступ. Изменение элемента или конструктора, чтобы разрешить меньше доступа поэтому не рекомендуется для широко распределенных классов. Возможно, удивительно Java определяется так, чтобы изменение элемента или конструктора, чтобы быть более доступным не вызвало ошибку редактирования, когда подкласс (уже) определяет метод, чтобы иметь меньше доступа. Так, например, если пакет points
определяет класс Point
:
package points;
public class Point {
public int x, y;
protected void print() {
System.out.println("(" + x + "," + y + ")");
}
}
используемый Test
программа: 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
не повреждая двоичные файлы существующих ранее подклассов помогает сделать двоичные файлы Java менее хрупкими. Альтернатива, где такое изменение вызвало бы ошибку редактирования, создаст дополнительные двоичные несовместимости без очевидного преимущества.
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
независимо от того, что поле типа h
объявляется в Super
. В то время как это может казаться удивительным сначала, это служит, чтобы сократить количество несовместимостей, которые происходят во время выполнения. (В идеальном мире будут перекомпилированы все исходные файлы, которые нуждались в перекомпиляции всякий раз, когда любой из них изменился, устраняя такие неожиданности. Но такая массовая перекомпиляция часто непрактична или невозможна, особенно в Интернете. И, как был ранее отмечен, такая перекомпиляция будет иногда требовать дальнейших изменений к исходному коду.) Удаление поля от класса повредит совместимость с любыми существующими ранее двоичными файлами, которые ссылаются на это поле, и a NoSuchFieldError
будет брошен, когда такая ссылка от существующего ранее двоичного файла соединяется. Только private
поля могут быть безопасно удалены из широко распределенного класса.
final
Поля и Константы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 { static char s; }Если
Super
перекомпилирован, но нет Test
, тогда выполняя новый двоичный файл с существующим двоичным файлом Test
результаты в a IncompatibleClassChangeError
. (В определенных ранних реализациях Java этот пример работал бы без ошибки из-за дефекта в реализации.) Мы вызываем поле, которое является static
, final
, и инициализированный с константным выражением времени компиляции примитивная константа. Отметьте, что все поля в интерфейсах неявно static
и final
, и они часто, но не всегда, константы.
Если поле не является примитивной константой, то удаление ключевого слова final
или изменение значения, к которому инициализируется поле, не повреждает совместимость с существующими двоичными файлами.
Если поле является примитивной константой, то удаление ключевого слова final
или изменение его значения не будет повреждать совместимость с существующими ранее двоичными файлами, заставляя их не работать, но они не будут видеть нового значения для константы, если они не будут перекомпилированы. Если пример:
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.19.
Это поведение не изменилось бы если Flags
были изменены, чтобы быть интерфейсом, как в измененном примере:
interface Flags { boolean debug = true; } class Test { public static void main(String[] args) { if (Flags.debug) System.out.println("debug is true"); } }(Одной причиной требования встраивания примитивных констант является тот Java
switch
операторы требуют констант на каждом case
, и никакие две таких постоянных величины не могут быть тем же самым. Java проверяет на двойные постоянные величины в a switch
оператор во время компиляции; class
формат файла не делает символьного редактирования case
значения.) Лучший способ избежать проблем с "неустойчивыми константами" в широко распределенном коде состоит в том, чтобы объявить, поскольку примитивные константы только оценивают, которые действительно маловероятны когда-либо измениться. Много примитивных констант в интерфейсах являются маленькими целочисленными значениями, заменяющими перечислимые типы, которые не поддерживает Java; эти маленькие значения могут быть выбраны произвольно, и не должны должны быть быть изменены. Кроме для истинных математических констант, мы рекомендуем, чтобы код Java сделал очень экономящее использование переменных класса, которые объявляются 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.5.4). Это означает, что все такие поля, кажется, инициализируются сначала во время инициализации класса (§8.3.2.1, §9.3.1, §12.4.2).
static
Поляprivate
не был объявлен static
и изменяется, чтобы быть объявленным static
, или наоборот, затем ошибка времени редактирования, определенно IncompatibleClassChangeError
, закончится, если поле будет использоваться существующим ранее двоичным файлом, который ожидал поле другого вида. Такие изменения не рекомендуются в коде, который был широко распределен. transient
Поляtransient
модификатор поля не повреждает совместимость с существующими ранее двоичными файлами. volatile
Поляprivate
не был объявлен volatile
и изменяется, чтобы быть объявленным volatile
, или наоборот, затем ошибка времени редактирования, определенно IncompatibleClassChangeError
, может закончиться, если поле используется существующим ранее двоичным файлом, который ожидал поле противоположной энергозависимости. Такие изменения не рекомендуются в коде, который был широко распределен. Удаление метода или конструктора от класса повредит совместимость с любым существующим ранее двоичным файлом, который ссылался на этот метод или конструктора; a NoSuchMethodError
будет брошен, когда такая ссылка от существующего ранее двоичного файла соединяется. Только private
методы или конструкторы могут быть безопасно удалены из широко распределенного класса.
Если исходный код для класса не содержит объявленных конструкторов, компилятор Java автоматически предоставляет конструктора без параметров. Добавление одного или более объявлений конструктора к исходному коду такого класса будет препятствовать тому, чтобы этот конструктор по умолчанию был предоставлен автоматически, эффективно удаляя конструктора, если у одного из новых конструкторов также не будет никаких параметров, таким образом заменяя конструктора по умолчанию. Автоматически предоставленному конструктору без параметров дают тот же самый модификатор доступа как класс его объявления, таким образом, у любой замены должны быть так много или больше доступа, если совместимость с существующими ранее двоичными файлами должна быть сохранена.
void
, или замена void
с результатом у типа есть совместное воздействие удаления старого метода или конструктора и добавления нового метода или конструктора с новым типом результата или недавно void
результат (см. §13.4.12). abstract
Методы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
, и, поэтому (или должен быть), краткий обзор. (Ранняя версия Java, неправильно произведенного вывод: Wayпрежде, чем встретиться
AbstractMethodError
вызывая метод out
, неправильно разрешение класса Test
быть подготовленным даже при том, что это имеет abstract
метод и не объявляется abstract
.) final
Методы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
модификатор метода не повреждает совместимость с существующими ранее двоичными файлами. Воздействие изменений к Java вводит на существовании ранее native
методы, которые не перекомпилированы, выходят за рамки этой спецификации и должны быть предоставлены описание реализации Java. Реализации поощряются, но не требуются, чтобы реализовать native
методы в пути, который ограничивает такое воздействие.
static
Методыprivate
был объявлен static
(то есть, метод класса), и изменяется, чтобы не быть объявленным static
(то есть, к методу экземпляра), или наоборот, тогда совместимость с существующими ранее двоичными файлами может быть повреждена, приводя к ошибке времени редактирования, а именно, IncompatibleClassChangeError
, если эти методы используются существующими ранее двоичными файлами. Такие изменения не рекомендуются в коде, который был широко распределен. synchronized
Методыsynchronized
модификатор метода не повреждает совместимость с существующими двоичными файлами. throws
пункт методов или конструкторов не повреждает совместимость с существующими двоичными файлами; эти пункты проверяются только во время компиляции. Мы рассматриваем, должна ли будущая версия языка Java потребовать более строгой проверки throws
пункты, когда классы проверяются.
Мы отмечаем, что компилятор не может встроить, не разворачивают метод во время компиляции если, например, также:
private
к его классу
final
на методе не означает, что метод может быть безопасно встроен; это только означает, что метод не может быть переопределен. Если у компилятора нет экстраординарного знания, все еще возможно, что новая версия того метода будет обеспечена во время ссылки. Вообще мы предлагаем, чтобы реализации Java использовали последний ограниченный (время выполнения) генерация кода и оптимизация.
В то время как добавление нового перегруженного метода или конструктора может вызвать ошибку времени компиляции в следующий раз, когда класс или интерфейс компилируются, потому что нет никакого метода или конструктора, который является самым определенным (§15.11.2.2), никакая такая ошибка не происходит, когда программа Java выполняется, потому что никакое разрешение перегрузки не делается во время выполнения.
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как, возможно, наивно ожидался в предыдущем случае.
class Hyper { void hello() { System.out.print("Hello, "); } static void world() { System.out.println("world!"); } } class Super extends Hyper { } class Test { public static void main(String[] args) { Super s = new Super(); s.hello(); s.world(); } }компилируется и выполняется, это производит вывод:
Hello, world!Предположите что новая версия класса
Super
производится: class Super extends Hyper { void hello() { System.out.print("Goodbye, cruel "); } static void world() { System.out.println("earth!"); } }Если
Super
перекомпилирован, но нет Hyper
или Test
, тогда выполняя новый двоичный файл с существующими двоичными файлами для Hyper
и Test
произведет вывод: Goodbye, cruel world!Этот пример демонстрирует что вызов в:
s.world();в методе
main
разрешается, во время компиляции, к символьной ссылке на класс, содержащий метод класса world
, как если бы это было записано: Hyper.world();Это то, почему
world
метод Hyper
вместо Super
вызывается в этом примере. Конечно, перекомпиляция всех классов, чтобы произвести новые двоичные файлы позволит вывод: Goodbye, cruel earth!быть произведенным.
public
Интерфейсыpublic
быть объявленным public
не повреждает совместимость с существующими ранее двоичными файлами. Если интерфейс, который объявляется public
изменяется, чтобы не быть объявленным public
, тогда IllegalAccessError
бросается, если существующий ранее двоичный файл соединяется, что у потребностей, но больше есть доступ к интерфейсному типу, таким образом, такое изменение не рекомендуется для широко распределенных интерфейсов.
VerifyError
. Удаление элемента от интерфейса может вызвать ошибки редактирования в существующих ранее двоичных файлах. Если пример программы:
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
. (В некоторых ранних реализациях Java все еще выполнилась эта программа; факт, что метод hello
больше не существует в интерфейсе I
не был правильно обнаружен.) static
final
поля в классах, как описано в §13.4.7 и §13.4.8. abstract
методы в классах, как описано в §13.4.13, §13.4.14, §13.4.20, и §13.4.22.
Содержание | Предыдущий | Следующий | Индекс
Спецификация языка Java (HTML, сгенерированный Блинчиком "сюзет" Pelouch 24 февраля 1998)
Авторское право © Sun Microsystems, Inc 1996 года. Все права защищены
Пожалуйста, отправьте любые комментарии или исправления к doug.kramer@sun.com