ACC_SUPER был введен для исправления проблемы с вызовом супер методов.Флаг ACC_SUPER помечает класс как скомпилированный для измененной семантики инструкции кода операции 183.Его назначение аналогично назначению номера версии файла класса, поскольку позволяет JVM определять, был ли класс скомпилирован для более старой или более новой семантики этой инструкции.Java 1.0.2 не установила и проигнорировала ACC_SUPER, в то время как Java 1.1 и более поздние всегда устанавливают ACC_SUPER.
До Java 1.1 инструкция байтового кода с кодом операции 183, который теперь называется invokespecial
, называлась invokenonvirtual
и имелачастично другая спецификация.Это использовалось всякий раз, когда методы экземпляра должны были быть вызваны без поиска виртуального метода.Это имело место для частных методов, инициализаторов экземпляров (конструкторов) и для реализации вызовов методов на super
.Но в последнем случае возникли проблемы с развивающимися библиотеками классов.
Ссылка на метод в байтовом коде (CONSTANT_Methodref_info
) определяет не только имя и аргумент и возвращаемые типы метода, но также и класс, к которому он принадлежит,Код операции 183 получает такой ссылочный параметр метода и предназначен для непосредственного вызова ссылочного метода из указанного класса без дальнейших поисков.В случае вызовов на super
ответственность за разрешение ближайшего суперкласса, реализующего этот метод, и генерацию ссылки на него в байт-коде лежит на компиляторах.
Начиная с Java 1.1 он был изменен, чтобы по существу игнорироватькласс, указанный в CONSTANT_Methodref_info
, и вместо этого выполнить поиск ближайшего супер-метода с заданным именем метода и сигнатурой в JVM.Обычно это делается сейчас, когда класс загружается или непосредственно перед выполнением инструкции или когда JIT компилируется в первый раз.
Вот пример, почему это изменение было необходимым.В Java 1.0.2 классы AWT Container и Component были определены следующим образом:
class Component
{
public void paint( Graphics g ) {}
}
class Container extends Component
{
// inherits paint from Component but doesn't override it
}
В Java 1.1 класс Conatiner был изменен, чтобы иметь собственную реализацию paint
:
class Container extends Component
{
public void paint( Graphics g ) {/*...*/}
}
Теперь, когда у вас был прямой или косвенный подкласс Container, который сделал вызов на super.paint(g)
и скомпилировал его для 1.0.2, он сгенерировал инструкцию invokenonvirtual
для Component.paint
, так как это был первый родительский объект, у которого был этот метод.Но если бы вы использовали этот скомпилированный класс на JVM, которая также имела Container.paint
, он все равно вызвал бы Component.paint
, что не соответствует ожиданиям.
С другой стороны, когда вы скомпилировали класс для 1.1и выполнил его на JVM 1.0.2, он выкинул бы AbstractMethodError или, что более вероятно, для виртуальных машин той эпохи просто рухнулЧтобы избежать сбоя, вы должны были написать ((Component)super).paint(g)
и скомпилировать его с помощью компилятора 1.1, чтобы получить желаемое поведение в любой виртуальной машине.Это установит ACC_SUPER, но все равно сгенерирует инструкцию для вызова Component.paint
.Виртуальная машина 1.0.2 игнорировала бы ACC_SUPER и пошла бы прямо, чтобы вызвать Component.paint
, что хорошо, в то время как виртуальная машина 1.1 нашла бы установленный ACC_SUPER и, таким образом, сама выполнила поиск, который заставил бы ее вызвать Container.paint
, даже если ссылка на метод байтового кода была Component.paint
.
Подробнее об этом можно прочитать в этом старом посте в блоге ikvm.net .