home ホーム search 検索 -  login ログイン  | reload edit datainfo version cmd icon diff delete  | help ヘルプ

技術/Java/static finalを使用する場合の注意点

技術/Java/static finalを使用する場合の注意点

技術 / Java / static finalを使用する場合の注意点
id: 290 所有者: msakamoto-sf    作成日: 2006-11-06 10:07:45
カテゴリ: Java 

public static final を定数として使用していた。
定数の値を変えてpublics static finalを定義しているクラスをコンパイルし、それを参照している、例えばmainクラスなどを起動しても、変更が反映されない。変更前の値を使用してしまう。

という現象に悩まされていました。で、試しに次のような定数クラスを用意し、適当にそれらのフィールドを出力するmainクラスを作成してみる。 ここで重要なのは、定数クラスと mainクラスは別々のJavaソース、クラスファイルになっている点。

public class ConstTest {
public static final String const1 = "const1-1";
public static final int const2 = 123;
public static final String getConst3() { return "const3-1"; }
public final String getConst4() { return "const4-1"; }
public String getConst5() { return "const5-1"; }
public static String const6 = "const6-1";
public final String const7 = "const7-1";
}

やってみたところ、明らかになったのは、finalで宣言されたpublic staticなフィールドについては、どうもインライン展開され、呼び出し側のクラスに直値が書き込まれてしまうらしい、と言うことである。直接そのことが明記されている文章は、JavaHouseに一件見つかった。残りは、finalメソッドについてのインライン化について取り上げられており、finalフィールドについては少々うやむやである。

http://java-house.jp/ml/archive/j-h-b/026215.html
http://www.nextindex.net/java/private.html
http://www.nextindex.net/java/perform/tips.html
http://www-06.ibm.com/jp/developerworks/java/030117/j_j-jtp1029.html
http://www-06.ibm.com/jp/developerworks/java/030627/j_j-jtp04223.html

どちらにせよ、static finalにしてしまうと、参照側のクラスにインライン展開されてしまっている為、元クラスのstatic final値を変更した場合、参照側のクラスも再コンパイルが必要であることには変わりはなかった。

・・・そうか・・・。WebLogicで、プロジェクト共通の定数クラスの値だけ変えても、Servlet側で反映してくれなかったのはこれが原因だったのか・・・。

このインライン化は、finalされたクラスを直接.classで参照していようと、jarファイルの中であろうと関係なく行われるようである。試しに先の実験用のfinalクラスをjar化し、main側を再コンパイルし、

javap -l -s -c -verbose mainクラス

として逆アセンブルした結果を、.class直接参照してコンパイルしたmainクラスの逆アセンブル結果とdiffしてみたところ、相違点は無かった。
つまり、jarファイルの中から参照しているクラスを探し、適切にfinalフィールドをインライン展開できていた。

public static finalの組み合わせ自体は昔から使われてきた手法である。にも関わらず、今までこの問題が表面化してこなかったのは何故か、非常に疑問に思う。特に、先に示したJavaHouseの記事については1999年のものである。・・・何でだろう。もしや、javacがある程度参照の連携を自動認識し、参照・被参照クラスをコンパイルしてくれるのではあるまいか?・・・そういわれれば、今回この現象が発覚したのは、被参照クラスと参照クラスを、別ディレクトリで管理していたからかも知れない。被参照クラスはAppサーバ側の設定で直接classpathを切ってしまっており、参照クラスはServletである。当然、コンパイル時のディレクトリ構造も別になっており、ServletクラスはCLASSPATHで被参照クラスを探索するようになっていた。

まあ、逆にfinalがインライン化されて取り込まれてしまう、という事が、徒に公開クラスの定数をpublic static finalにしてしまうとその後値を迂闊に変更できなくなる、という事にも結びつく。
Log4jのLogManager#DEFAULT_CONFIGURATION_KEYなどがdeprecatedになっていてもなお、消えることが無いのも、finalであるが故に、利用アプリのクラスでインライン展開されてしまっている可能性があるからであろう。log4jのjarを入れ替えればそれで済む話ではなくなってしまっているのだ。

検証用に用いたクラス・ソースツリー一式 : java_static_final_test.zip
hoge.bakみたいなディレクトリは、元はhogeディレクトリ。hoge.jarはhogeディレクトリをjarでまとめたもの。念のためhogeディレクトリはhoge.bakにリネームしただけ。



プレーンテキスト形式でダウンロード
現在のバージョン : 1
更新者: msakamoto-sf
更新日: 2009-04-05 10:21:24
md5:76603be6ef9569ca2b014ad97f08b227
sha1:5014a191876e356d89efa22d0a7161890a0809c2
コメント
コメントを投稿するにはログインして下さい。