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

ГЛАВА 13

Совместимость на уровне двоичных кодов


Средства разработки Java должны поддерживать автоматическую перекомпиляцию по мере необходимости всякий раз, когда исходный код доступен. Определенные реализации Java могут также сохранить источник, и двоичный файл вводит базу данных управления версиями и реализацию a ClassLoader (§20.14), который использует механизмы целостности базы данных, чтобы предотвратить ошибки редактирования, обеспечивая совместимые с двоичным файлом версии типов клиентам.

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

Бумага, заключенная в кавычки выше, кажется в Продолжениях OOPSLA '95, опубликованной как ACM SIGPLAN Уведомления, Объем 30, Номер 10, октябрь 1995, страницы 426-438. В пределах платформы той бумаги двоичные файлы Java являются двоичными (от выпуска к выпуску), совместимый при всех соответствующих преобразованиях, которые идентифицируют авторы. Используя их схему, вот список некоторых важных двоичных совместимых изменений, которые поддерживает Java:

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

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

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

13.1 Форма Двоичного файла Java

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

Требования:

Двоичное представление Java для класса или интерфейса должно также содержать все следующее:

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

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

13.2 Что Совместимость на уровне двоичных кодов и Не

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

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

Java разрабатывается, чтобы предотвратить дополнения к контрактам и случайным коллизиям имени от повреждающейся совместимости на уровне двоичных кодов; определенно:

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

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

13.3 Развитие Пакетов

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

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

13.4 Развитие Классов

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

13.4.1 abstract Классы

Если класс, который не был abstract изменяется, чтобы быть объявленным abstract, тогда пред-существующие двоичные файлы, которые пытаются создать новые экземпляры того класса, бросят любого InstantiationError во время ссылки, или InstantiationException во время выполнения (если метод newInstance (§20.3.6) класса Class используется); такое изменение поэтому не рекомендуется для широко распределенных классов.

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

13.4.2 final Классы

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

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

13.4.3 public Классы

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

Если класс, который был объявлен public изменяется, чтобы не быть объявленным public, тогда IllegalAccessError бросается, если существующий ранее двоичный файл соединяется, что у потребностей, но больше есть доступ к типу класса; такое изменение не рекомендуется для широко распределенных классов.

13.4.4 Суперклассы и Суперинтерфейсы

A 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
ee300848
A poke метод, способный к изменению любого расположения в памяти, мог быть придуман подобным образом. Это оставляют как осуществление для читателя.

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

13.4.5 Тело класса и Задействованные Объявления

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

13.4.6 Доступ к Элементам и Конструкторам

Изменение объявленного доступа элемента или конструктора, чтобы разрешить меньше доступа может повредить совместимость с существующими ранее двоичными файлами, заставляя ошибку редактирования быть брошенным, когда эти двоичные файлы разрешаются. Меньше доступа разрешается, если модификатор доступа изменяется от доступа по умолчанию до 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 менее хрупкими. Альтернатива, где такое изменение вызвало бы ошибку редактирования, создаст дополнительные двоичные несовместимости без очевидного преимущества.

13.4.7 Полевые Объявления

Добавление поля к классу не будет повреждать совместимость с любыми существующими ранее двоичными файлами, которые не перекомпилированы, даже в случае, где класс больше не мог быть перекомпилирован потому что доступ к полю, на который ранее ссылаются поле суперкласса с несовместимым типом. Ранее скомпилированный класс с такой ссылкой будет продолжать ссылаться на поле, объявленное в суперклассе. Таким образом компилируя и выполняя код:

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 поля могут быть безопасно удалены из широко распределенного класса.

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

13.4.9 static Поля

Если поле, которое не объявляется private не был объявлен static и изменяется, чтобы быть объявленным static, или наоборот, затем ошибка времени редактирования, определенно IncompatibleClassChangeError, закончится, если поле будет использоваться существующим ранее двоичным файлом, который ожидал поле другого вида. Такие изменения не рекомендуются в коде, который был широко распределен.

13.4.10 transient Поля

Добавление или удаление a transient модификатор поля не повреждает совместимость с существующими ранее двоичными файлами.

13.4.11 volatile Поля

Если поле, которое не объявляется private не был объявлен volatile и изменяется, чтобы быть объявленным volatile, или наоборот, затем ошибка времени редактирования, определенно IncompatibleClassChangeError, может закончиться, если поле используется существующим ранее двоичным файлом, который ожидал поле противоположной энергозависимости. Такие изменения не рекомендуются в коде, который был широко распределен.

13.4.12 Метод и Объявления Конструктора

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

Удаление метода или конструктора от класса повредит совместимость с любым существующим ранее двоичным файлом, который ссылался на этот метод или конструктора; a NoSuchMethodError будет брошен, когда такая ссылка от существующего ранее двоичного файла соединяется. Только private методы или конструкторы могут быть безопасно удалены из широко распределенного класса.

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

13.4.13 Метод и Параметры Конструктора

Изменять название формального параметра метода или конструктора не воздействует на существующие ранее двоичные файлы. Изменяя название метода, тип формального параметра к методу или конструктору, или добавление параметра к или удаление параметра от метода или объявления конструктора создают метод или конструктора с новой подписью, и имеют совместное воздействие удаления метода или конструктора со старой подписью и добавлением метода или конструктора с новой подписью (см. §13.4.12).

13.4.14 Тип Результата метода

Изменение типа результата метода, замена типа результата с void, или замена void с результатом у типа есть совместное воздействие удаления старого метода или конструктора и добавления нового метода или конструктора с новым типом результата или недавно void результат (см. §13.4.12).

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

13.4.16 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 модификатор от метода не повреждает совместимость с существующими ранее двоичными файлами.

13.4.17 native Методы

Добавление или удаление a native модификатор метода не повреждает совместимость с существующими ранее двоичными файлами.

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

13.4.18 static Методы

Если метод, который не объявляется private был объявлен static (то есть, метод класса), и изменяется, чтобы не быть объявленным static (то есть, к методу экземпляра), или наоборот, тогда совместимость с существующими ранее двоичными файлами может быть повреждена, приводя к ошибке времени редактирования, а именно, IncompatibleClassChangeError, если эти методы используются существующими ранее двоичными файлами. Такие изменения не рекомендуются в коде, который был широко распределен.

13.4.19 synchronized Методы

Добавление или удаление a synchronized модификатор метода не повреждает совместимость с существующими двоичными файлами.

13.4.20 Метод и Броски Конструктора

Изменения к throws пункт методов или конструкторов не повреждает совместимость с существующими двоичными файлами; эти пункты проверяются только во время компиляции.

Мы рассматриваем, должна ли будущая версия языка Java потребовать более строгой проверки throws пункты, когда классы проверяются.

13.4.21 Метод и Тело Конструктора

Изменения к телу метода или конструктора не повреждают совместимость с существующими ранее двоичными файлами.

Мы отмечаем, что компилятор не может встроить, не разворачивают метод во время компиляции если, например, также:

Ключевое слово final на методе не означает, что метод может быть безопасно встроен; это только означает, что метод не может быть переопределен. Если у компилятора нет экстраординарного знания, все еще возможно, что новая версия того метода будет обеспечена во время ссылки.

Вообще мы предлагаем, чтобы реализации Java использовали последний ограниченный (время выполнения) генерация кода и оптимизация.

13.4.22 Метод и Конструктор, Перегружающийся

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

В то время как добавление нового перегруженного метода или конструктора может вызвать ошибку времени компиляции в следующий раз, когда класс или интерфейс компилируются, потому что нет никакого метода или конструктора, который является самым определенным (§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
как, возможно, наивно ожидался в предыдущем случае.

13.4.23 Переопределение метода

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


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!
быть произведенным.

13.4.24 Статические Инициализаторы

Добавление, удаляя, или изменяя статический инициализатор (§8.5) класса не воздействует на существующие ранее двоичные файлы.

13.5 Развитие Интерфейсов

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

13.5.1 public Интерфейсы

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

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

13.5.2 Суперинтерфейсы

Изменения к интерфейсной иерархии вызывают ошибки таким же образом, который изменяется на иерархию классов, делают, как описано в §13.4.4. В частности изменения, которые приводят к любому предыдущему суперинтерфейсу класса, больше являющегося суперинтерфейсом, могут повредить совместимость с существующими ранее двоичными файлами, приводящими к a VerifyError.

13.5.3 Интерфейсные Элементы

Добавление элемента к интерфейсу не повреждает совместимость с существующими ранее двоичными файлами.

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

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 не был правильно обнаружен.)

13.5.4 Полевые Объявления

Соображения для того, чтобы изменить полевые объявления в интерфейсах являются тем же самым как теми для static final поля в классах, как описано в §13.4.7 и §13.4.8.

13.5.5 Абстрактные Объявления метода

Соображения для того, чтобы изменить абстрактные объявления метода в интерфейсах являются тем же самым как теми для 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



Spec-Zone.ru - all specs in one place



free hit counter