class ファイルのバージョン
バージョンを調べる
java の .class ファイルには、バージョン情報が埋め込まれている。 古い実行環境では、新しいバージョンの class をロードすることができない。 class ファイルを生成する環境、つまり JDK のバージョンによって 生成できる class のバージョンも決まる。今のところ、新しい javac は 古い class を生成できるようである。
.class ファイルの情報は、javap コマンドで見ることができる。 例えば openjdk7 でコンパイルしたクラスを調べると、以下のような具合である。
% JAVA_HOME=/usr/local/openjdk7 % export JAVA_HOME % java -version openjdk version "1.7.0_21" OpenJDK Runtime Environment (build 1.7.0_21-b11) OpenJDK Server VM (build 23.21-b01, mixed mode) % echo 'public class Empty {}' > Empty.java % javac Empty.java % javap -v Empty | grep version minor version: 0 major version: 51
バージョンには メジャーバージョンとマイナーバージョンがあって、 JDK 7 では メジャーバージョンが 51 だということがわかる。
バージョン番号が知りたいだけであれば、バイナリエディタで見てもよい。 7 バイトめがメジャーバージョン、 8 バイトめがマイナーバージョン である。
(承前)
% od -h Empty.class | head -1
0000000 feca beba 0000 3300 0d00 000a 0003 070a
「33」はもちろん16進なので、脳内で「3 * 16 + 3 = 51」と計算することになる。
バージョン一覧
class ファイルのバージョンは、javac 時に -target で指定することができる。 そこで、それぞれの target 指定時にバージョン番号がどうなるか、 openjdk8 で調べてみた。
(承前) % JAVA_HOME=/usr/local/openjdk8 % export JAVA_HOME % java -version openjdk version "1.8.0" OpenJDK Runtime Environment (build 1.8.0-b132) OpenJDK Server VM (build 25.0-b70, mixed mode) % for v in 1 2 3 4 5 6 7 8; do > echo 1.$v > javac -source 1.2 -target 1.$v -Xlint:-options Empty.java > javap -v Empty | grep version > done 1.1 minor version: 3 major version: 45 1.2 minor version: 0 major version: 46 1.3 minor version: 0 major version: 47 1.4 minor version: 0 major version: 48 1.5 minor version: 0 major version: 49 1.6 minor version: 0 major version: 50 1.7 minor version: 0 major version: 51 1.8 minor version: 0 major version: 52
1.2 以降は、きれいに一つずつ上がってきていることがわかる。
ちなみに 最初 -Xlint:-options を指定しないで試したところ、 警告が出た。
warning: [options] source value 1.2 is obsolete and will be removed in a future release warning: [options] target value 1.4 is obsolete and will be removed in a future release
「将来的には -source 1.2 はサポートしませんよ」 「将来的には -target 1.4 はサポートしませんよ」 ということらしい。 この警告は openjdk8 から出るようになったようだ (openjdk7 では出ない)。
実行時・コンパイル時の挙動
新しいバージョンのクラスを古い実行環境にロードしようとすると、拒否られる。
(承前) % JAVA_HOME=/usr/local/openjdk7 % export JAVA_HOME % java -version openjdk version "1.7.0_21" OpenJDK Runtime Environment (build 1.7.0_21-b11) OpenJDK Server VM (build 23.21-b01, mixed mode) % javap -v Empty | grep version minor version: 0 major version: 52 % java Empty Exception in thread "main" java.lang.UnsupportedClassVersionError: Empty : Unsupported major.minor version 52.0 at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:791)
(UnsupportedClassVersionError の行は便宜上改行しているが、実際は1行)
新旧のバージョンが混ざった状態でも、コンパイル時点では、 コンパイルできてしまうケースがある。
(承前) % echo 'public class EmptyWrapper { Empty e; }' > EmptyWrapper.java % javac EmptyWrapper.java warning: ./Empty.class: major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. 1 warning
(warning は便宜上改行しているが、実際は1行)
関連クラスがロードできない以上、事実上「実行できない」んだから、 コンパイルだけできたって意味ないじゃないか、と思うのだが、さてはて。