Java クラスファイルのアクセスフラグの一つに ACC_SUPER というものがあります。
Class access and property modifiers - Java Virtual Machine Specification
このフラグの意味は、ドキュメントに以下のように書かれています。
ACC_SUPERフラグは、このクラスまたはインタフェースにinvokespecial命令 (§invokespecial) が出現した場合に、2 つの代替セマンティクスのどちらが表現されるかを示します。
Chapter 4. The class File Format
これは何を意味しているのでしょうか。
結論としては、もはやなんの意味もありません。
以前はこのフラグの有無で少し挙動が変わっていたのですが、Java 7 Update 13 から (正式には Java 8 から)クラスファイルにこのフラグがセットされていてもいなくても同じ挙動になったためです。
Java SE 8 以降では、Java 仮想マシンは、クラス ファイル内のフラグの実際の値やクラスファイルのバージョンに関係なく、すべてのクラス ファイルで
ACC_SUPERフラグが設定されているものと見なします。
Chapter 4. The class File Format
そのため、このフラグを気にする必要はありません。
以下、Java 考古学者向けの補足です。
ACC_SUPER 誕生の経緯
この StackOverflow の回答を元に、補足しながら説明します。
元々、ACC_SUPER は invokespecial の挙動を変更するために生まれました。
Java 1.0.2 の invokespecial の仕様
invokespecial はコンストラクタの呼び出しや、オーバーライドした親クラスのメソッド呼び出しなどで使われる命令です。Java 1.0.2 の仕様では、invokespecial 命令は単純に指定されたメソッドを呼び出すとなっていました。
例えば、以下のクラスは Java の最初のバージョン(1.0.2)では C クラスの super.foo() の部分が invokespecial 命令で A.foo() を呼び出すというコンパイル結果になります。実行すると C.foo() → A.foo() の順番で実行されるので "CA" と表示されます。1
class A {
public void foo() {
System.out.print("A");
}
}
class B extends A {
// foo メソッドを A から継承するが、 オーバーライドしない
}
class C extends B {
public void foo() {
System.out.print("C");
super.foo(); // invokespecial A.foo():V
}
}
public class Main {
public static void main(String[] args) {
new C().foo();
}
}
Java 1.1 での invokespecial の仕様変更
ところが、「invokespecial は指定されたメソッドを呼び出す」という仕様だと、オーバーライドが追加された場合に問題が起こることが発覚しました。
例えば、上記の B クラスに以下のように foo() メソッドが追加された場合に問題が起きます。
class B extends A {
public void foo() {
System.out.print("B");
super.foo();
}
}
この場合、C クラスの super.foo() では追加された B.foo() が実行されるべきですが、C クラスをコンパイルし直さない限り invokespecial A.foo():V となっているので 依然として A.foo() が実行されてしまっていました。

そこで、Java 1.1 で invokespecial の仕様に手を加えて、invokevirtual と同様にオーバーライドを考慮して呼び出すメソッドを決めるようになりました。 今回の場合だと、invokespecial A.foo():V という命令で A.foo() ではなく、C クラスのスーパークラスから順に foo() を探すようになり、 B.foo() があるのでそれを呼び出すようになりました。

しかし、単純に仕様を変更すると些細な違いですが Java 1.0.2 と挙動が変わり、互換性がなくなってしまいます。
そのため、以下の仕様となりました。
- クラスファイルにアクセスフラグ
ACC_SUPERを追加- Java 1.1 以降のコンパイラでは、このフラグを必ず設定する
-
ACC_SUPERの有無で、そのクラス内のinvokespecial命令の挙動を変更する-
ACC_SUPERフラグが設定されている(= Java 1.1 以降でコンパイルした)- 指定されたメソッドのオーバーライドを考慮して呼び出す
- 上記の例では、
invokespecial A.foo():VでB.foo()を呼び出し、"CBA" という表示になる
- 上記の例では、
- 指定されたメソッドのオーバーライドを考慮して呼び出す
-
ACC_SUPERフラグが設定されていない(= Java 1.0.2 でコンパイルした)- Java 1.0.2 と同じ挙動にする
- 指定されたメソッドをオーバーライドを考慮せず呼び出す
- 上記の例では、
invokespecial A.foo():VでA.foo()を呼び出し、"CA" という表示になる
- 上記の例では、
-
ACC_SUPER の変更
このような経緯で誕生した ACC_SUPER ですが、Java 7 Update 13 から(正式な仕様としては Java 8 から)クラスファイルの ACC_SUPER が設定されていなくても、ACC_SUPER フラグが設定されているものとして扱うようになりました。
つまり、Java 1.0.2 でコンパイルした(ACC_SUPER フラグが設定されていない)クラスファイルの挙動が変わりました。 具体的には、C クラスの invokespecial A.foo():V で ACC_SUPER フラグが設定されていないので A.foo() を呼び出していたのが、Java 7 Update 13 からは B.foo() を呼び出すように変わりました。
この変更理由は明らかにされていませんが、上記の StackOverflow のコメントではおそらくセキュリティ上の問題があったからではないかと言われています。
具体的な例として、以下のブログでは Java 7 以降では Thread クラスのクローンができないように Thread.clone() で CloneNotSupportedException をスローするように実装されているにもかかわらず、それをスキップして Object.clone() を呼べてしまうという点が指摘されています。
JDK 7 Thread Cloning Vulnerability - IKVM.NET Weblog
まとめ
基本的に Java 仮想マシン仕様では後方互換性を重視しているので、どのバージョンでコンパイルされたクラスファイルであっても、最新の Java 仮想マシンで実行した際には変わらずに動くようになっています。
それにもかかわらず、一度入れた仕様を打ち消す変更をして挙動を変えるというのはとても珍しいです。もしかしたら、唯一かもしれません。
-
ちなみに、検証したところ Java 1.2 からは
invokespecialの実行対象がB:foo()に変わっていました。 ↩
