まんぼう日記

takataka's diary

Mac OS X で異なるバージョンの Java を同時に使う

Java7 の環境で動くクラスファイルを JDK8 で作りたい

Java プログラミングの授業の際に,クラスファイルのサンプルを用意しておいて,それと同じ動作をするプログラムを作ってね,ってことをやったりします.ところが,大学の計算機実習室で学生がサンプルを実行しようとすると,

$ java Hoge
Exception in thread "main" java.lang.UnsupportedClassVersionError: Hoge : 
Unsupported major.minor version 52.0

とか言われて動かせない,ということがありました.たかたかがサンプルを作成した Mac OS X では Java SE 8 (JDK8) を使ってるのに,計算機室(Ubuntu)では 7 なのが原因です.

 

解決のために,サンプルを作るたびに計算機室のマシンにログインするとか,普段使ってる Mac とは別に JDK7 を入れたマシンを用意するとか,そういう面倒くさいことはしたくないので,JDK8 の環境で 7 でも動くクラスファイルを作る方法を調べてみました.ところが,たかたかの環境ではそれがうまくいかなかったので,結局は1台の Mac で JDK8 と JDK7 を共存させて簡単に切り替える方法を採ることにしました.以下,そのメモです.

 

ちなみに,クラスファイルがどのバージョン用になってるかは,UNIX 環境だと file コマンドで調べることができます.

$ file Hoge4Version?.class
Hoge4Version6.class: compiled Java class data, version 50.0 (Java 1.6)
Hoge4Version7.class: compiled Java class data, version 51.0
Hoge4Version8.class: compiled Java class data, version 52.0    

 

 

javac -target 7 でいいんじゃね?

まずは,JDK8 で 7 でも動くクラスファイルを作れるんやないかかと調べてみました.javac のヘルプには,

$ javac -help
   :
-target <release>   特定のVMバージョン用のクラス・ファイルを生成する
   :

とありますので,これでいけそう.と思って試してみると,たかたかの環境(Mac OS X Mavericks / Yosemite + JDK 8u45)では,

$ javac -target 1.7 Hoge.java 
javac: ターゲット・リリース1.7がデフォルトのソース・リリース1.8と競合しています

と言われてうまくいきませんでした.ちぇっ.これなら簡単やと思ったのに.

 

2015-04-30 追記:

-target だけじゃなくて -source オプション

-source <release>          指定されたリリースとソースの互換性を保つ

もつけたら,JDK8 の javac で 7 でも動くクラスファイルを作れるみたいです.

$ javac -source 1.7 -target 1.7 Hoge.java 
警告: [options] ブートストラップ・クラスパスが-source 1.7と一緒に設定されていません
警告1個
$ file Hoge.class 
Hoge.class: compiled Java class data, version 51.0

warningは出てますが.-bootclasspath オプション

-bootclasspath <path>  ブートストラップ・クラス・パスの場所をオーバーライドする

も指定すればよいのかも

 

1台の Mac に JDK8 と JDK7 を共存させる

仕方がないので,1台の Mac に JDK8 と JDK7 の両方を入れて,切り替えて使えるようにすることにしました.こちら( OSXでJavaのバージョンを切り替える - Qiita )を大いに参考にさせてもらいました.

 

JDK8 の入ってる Mac に JDK7 もインストール

まずは,既に JDK8 の入ってる Mac OS X マシンに,JDK 7 もインストールします.普通に Oracle のダウンロードのページ

Java SE - Downloads | Oracle Technology Network | Oracle

から JDK 7u80 を選んでダウンロード&インストールするだけ.JDK 7/8 それぞれの java や javac 等のコマンドは,

/Library/Java/JavaVirtualMachines/jdk1.7.0_80.jdk/Contents/Home/bin/
/Library/Java/JavaVirtualMachines/jdk1.8.0_45.jdk/Contents/Home/bin/

にあります.ただし,/usr/bin/javac や /usr/bin/java は,

/System/Library/Frameworks/JavaVM.framework/Versions/A/Commands/

にある,上記とは別の実体へのシンボリックリンクとなってます.この辺の仕掛けは調査不足のため謎です.

 

これでJDK7/8が両方入ったので,絶対パス使って 

$ /Library/Java/JavaVirtualMachines/jdk1.7.0_80.jdk/Contents/Home/bin/javac Hoge.java 
$ file Hoge.class 
Hoge.class: compiled Java class data, version 51.0
$ /Library/Java/JavaVirtualMachines/jdk1.8.0_45.jdk/Contents/Home/bin/javac Hoge.java 
$ file Hoge.class 
Hoge.class: compiled Java class data, version 52.0

とかやれば,とりあえずどっちのバージョンも使えます.

 

java_home コマンドと JAVA_HOME 環境変数を使う

そういうわけで,.bashrc 辺りでパスやエイリアスを適当に設定すればよいかもしれません.でも,上記のパスには jdk1.8.0_45.jdk みたいに細かいバージョン番号まで入ってるので,そのまま使っちゃうと,JDK をアップデートするたびに設定を修正しないといけなくなっていやんな感じです.

 

この事態に対処するために(?),上述の /System/Library/... の方には,java_home というコマンドがあります.こんなふうに使うようです.

$ /System/Library/Frameworks/JavaVM.framework/Versions/A/Commands/java_home
/Library/Java/JavaVirtualMachines/jdk1.8.0_45.jdk/Contents/Home
$ /System/Library/Frameworks/JavaVM.framework/Versions/A/Commands/java_home -v "1.7"
/Library/Java/JavaVirtualMachines/jdk1.7.0_80.jdk/Contents/Home

それから,JAVA_HOME という環境変数を指定すると,/usr/bin/javac 等を実行した際に実際に呼び出されるコマンドが切り替わります.

$ javac -version
javac 1.8.0_45
$ JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.7.0_80.jdk/Contents/Home javac -version
javac 1.7.0_80

 

というわけで,たかたかは .bashrc に次のような alias 設定を書いとくことにしました.

alias java_home=/System/Library/Frameworks/JavaVM.framework/Versions/A/Commands/java_home
alias java7='JAVA_HOME=`java_home -v "1.7"` java'
alias javac7='JAVA_HOME=`java_home -v "1.7"` javac'

これで,

$ javac -version
javac 1.8.0_45
$ javac7 -version
javac 1.7.0_80

というふうにJDK8/7用のjavacを使い分けることができるようになりました.めでたしめでたし.