0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

public static final Stringとして宣言された文字列定数が埋め込まれる

Last updated at Posted at 2020-04-12

事象

文字列定数(public static final String)ばかりが定義されたクラスConst.javaと、その定数を利用するMain.javaがあるとします。

Const.java
public class Const {
    public static final String VALUE = "BEFORE";
}
Main.java
public class Main {
    public static void main(String[] args) {
        System.out.println(Const.VALUE);
    }
}

このふたつのjavaファイルをコンパイルし、実行すると、文字列定数Const.VALUEの値BEFOREが出力されます。

C:\qiita> javac Const.java Main.java

C:\qiita> java Main

ここで、文字列定数Const.VALUEの値が変更になったとします。

Const.java
public class Const {
    public static final String VALUE = "AFTER";
}

Const.javaを編集後、Const.javaだけを再コンパイルし、ふたたびMainを実行したところ、画面にはConst.VALUEの現在の値であるAFTERではなく、BEFOREが表示されました。

C:\qiita> del Const.class

C:\qiita> javac Const.java

C:\qiita> java Main
BEFORE

調査

javapコマンドでMain.classを逆アセンブルすると、Main.classにはBEFOREという値が埋め込まれていることがわかります。

C:\qiita> javap -c Main.class
Compiled from "Main.java"
public class Main {
  public Main();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #4                  // String BEFORE
       5: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return
}

コンパイラはコンパイル時にMain.java内のConst.VALUEBEFOREという値に置き換えています。Main.classConst.classをすでに参照していないため、Const.VALUEの値が変わっても、その変更がMain.classにまで伝播しませんでした。

ちなみにMain.classConst.classを参照していないため、Const.classを削除してしまっても、Main.classは正常に動作します。

C:\qiita> del Const.class

C:\qiita> java Main
BEFORE

原因

なぜ文字列定数が埋め込まれてしまったのかというと、これはJavaの仕様に起因します。"Java Language Specs - 13.1. The Form of a Binary" には次の通り記述されています。

A reference to a field that is a constant variable (§4.12.4) must be resolved at compile time to the value V denoted by the constant variable's initializer.
If such a field is static, then no reference to the field should be present in the code in a binary file, including the class or interface which declared the field. Such a field must always appear to have been initialized (§12.4.2); the default initial value for the field (if different than V) must never be observed.

"Java Language Specs - 13.1. The Form of a Binary"には以下のとおり記述されており、文字列定数(public static final String)も埋め込みの対象であることがわかります。

A constant variable is a final variable of primitive type or type String that is initialized with a constant expression (§15.28).

対策

文字列定数だけでなく、public static finalとして宣言された定数の値が変更になった場合、関連するソースコードはすべてコンパイルしなおすべきです。

また上記のような問題をはらんでいることからもpublic static final Stringの乱用は危険です。定数を表現したい場合はenumを利用することも検討しましょう。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?