お世話になってます。
定数クラスはstaticイニシャライザで初期化すべきととある書籍で読みました。
理由はクラスファイルのサイズが大きくなるかららしいのですが、それなら定数クラスに限らず全てのクラスの全ての定数をstaticイニシャライザで初期化した方が良いのではないかと思うのですがどうでしょうか?
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答3件
0
###質問と議論
議論がはっきりしないのですが、次のように整理します。
A. 定数クラスは以下の2つの初期化方法がある。
a) 定数宣言と初期化を同時に行う。
b) 定数宣言と初期化は別に(初期化はスタティックイニシャライザで)行う。
B. 定数クラスを利用するクラスは、
a) を利用するクラスは定数をインライン展開するので、クラスのサイズが大きくなる。定数のサイズが大きいほど、参照するクラスの数が多いほどメモリを消費する。
b) を利用するクラスは、定数をインライン展開しないが、定数への参照をリゾルブするので、実行時にオーバーヘッドがあるだろう。a) と b) がトレードオフの関係にあるかもしれない。
質問は「定数クラスに限らず全てのクラスの全ての定数をstaticイニシャライザで初期化した方が良いのではないか」。 Aのこととして考えます。
ソースコードを示します。
Java
1package constants; 2public class ConstantsA { 3 4 public static final String CONSTANT_NAME = "some constant"; 5 public static final long SYS_TIME = System.currentTimeMillis(); 6 7}
Java
1package constants; 2public class ConstantsB { 3 4 public static final String CONSTANT_NAME; 5 public static final long SYS_TIME; 6 7 static { 8 CONSTANT_NAME = "some constant"; 9 SYS_TIME = System.currentTimeMillis(); 10 } 11}
クラスファイルのサイズはほとんど変わりません。
fedora
1$ ls -l Constants?.class 2-rw-rw-r--. 1 vornan19 vornan19 510 10月 22 20:29 ConstantsA.class 3-rw-rw-r--. 1 vornan19 vornan19 509 10月 22 20:28 ConstantsB.class
###定数クラスを利用するクラス
では、定数クラスの定数を利用するクラスはどうか?ソースコードを示します。
クラスAの定数を利用するクラス。これはCONSTANT_NAMEがインライン展開されます。
Java
1package constants; 2public class ConsumerA { 3 public String getConst() { 4 return ConstantsA.CONSTANT_NAME + ConstantsA.SYS_TIME; 5 } 6}
クラスBの定数を利用するクラス。これはCONSTANT_NAMEを参照します。
Java
1 2package constants; 3public class ConsumerB { 4 public String getConst() { 5 return ConstantsB.CONSTANT_NAME + ConstantsB.SYS_TIME; 6 } 7}
サイズを確認します。ConsumerB.classのほうが大きいのですが、定数が大きければConsumerA.classのほうが大きくなります。たとえばCONSTANT_NAMEが1000バイトなら、ConsumerA.classはそれだけ大きくなりますが、ConsumerB.classのサイズは変わりません。
fedora
1$ ls -l Consumer?.class 2-rw-rw-r--. 1 vornan19 vornan19 617 10月 22 20:37 ConsumerA.class 3-rw-rw-r--. 1 vornan19 vornan19 646 10月 22 20:38 ConsumerB.class
###クラスファイルのダンプ
最後にクラスファイルの中身を確認しておきます。クラスの中身を見ることで、理解が深まります。ConsumerA.classにはインライン展開された文字列(コンスタントプール#26)があります。
Java
1$ javap -v -p -c ConsumerA.class 2Classfile build/classes/constants/ConsumerA.class 3 Last modified 2016/10/22; size 617 bytes 4 MD5 checksum 8dfb512a2c4f05331ac2d39e26d0df46 5 Compiled from "ConsumerA.java" 6public class constants.ConsumerA 7 minor version: 0 8 major version: 52 9 flags: ACC_PUBLIC, ACC_SUPER 10Constant pool: 11 #1 = Methodref #11.#23 // java/lang/Object."<init>":()V 12 #2 = Class #24 // java/lang/StringBuilder 13 #3 = Methodref #2.#23 // java/lang/StringBuilder."<init>":()V 14 #4 = Class #25 // constants/ConstantsA 15 #5 = String #26 // some constant 16 #6 = Methodref #2.#27 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 17 #7 = Fieldref #4.#28 // constants/ConstantsA.SYS_TIME:J 18 #8 = Methodref #2.#29 // java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder; 19 #9 = Methodref #2.#30 // java/lang/StringBuilder.toString:()Ljava/lang/String; 20 #10 = Class #31 // constants/ConsumerA 21 #11 = Class #32 // java/lang/Object 22 #12 = Utf8 <init> 23 #13 = Utf8 ()V 24 #14 = Utf8 Code 25 #15 = Utf8 LineNumberTable 26 #16 = Utf8 LocalVariableTable 27 #17 = Utf8 this 28 #18 = Utf8 Lconstants/ConsumerA; 29 #19 = Utf8 getConst 30 #20 = Utf8 ()Ljava/lang/String; 31 #21 = Utf8 SourceFile 32 #22 = Utf8 ConsumerA.java 33 #23 = NameAndType #12:#13 // "<init>":()V 34 #24 = Utf8 java/lang/StringBuilder 35 #25 = Utf8 constants/ConstantsA 36 #26 = Utf8 some constant 37 #27 = NameAndType #33:#34 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 38 #28 = NameAndType #35:#36 // SYS_TIME:J 39 #29 = NameAndType #33:#37 // append:(J)Ljava/lang/StringBuilder; 40 #30 = NameAndType #38:#20 // toString:()Ljava/lang/String; 41 #31 = Utf8 constants/ConsumerA 42 #32 = Utf8 java/lang/Object 43 #33 = Utf8 append 44 #34 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder; 45 #35 = Utf8 SYS_TIME 46 #36 = Utf8 J 47 #37 = Utf8 (J)Ljava/lang/StringBuilder; 48 #38 = Utf8 toString 49{ 50... 省略 51} 52SourceFile: "ConsumerA.java"
ConsumerB.classにはインライン展開された文字列はなくクラス定数への参照(#32,#33)があります。
Java
1$ javap -v -p -c ConsumerB.class 2Classfile build/classes/constants/ConsumerB.class 3 Last modified 2016/10/22; size 646 bytes 4 MD5 checksum 58a646314ecd5af5dd6ef54e447b6f9a 5 Compiled from "ConsumerB.java" 6public class constants.ConsumerB 7 minor version: 0 8 major version: 52 9 flags: ACC_PUBLIC, ACC_SUPER 10Constant pool: 11 #1 = Methodref #10.#22 // java/lang/Object."<init>":()V 12 #2 = Class #23 // java/lang/StringBuilder 13 #3 = Methodref #2.#22 // java/lang/StringBuilder."<init>":()V 14 #4 = Fieldref #24.#25 // constants/ConstantsB.CONSTANT_NAME:Ljava/lang/String; 15 #5 = Methodref #2.#26 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 16 #6 = Fieldref #24.#27 // constants/ConstantsB.SYS_TIME:J 17 #7 = Methodref #2.#28 // java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder; 18 #8 = Methodref #2.#29 // java/lang/StringBuilder.toString:()Ljava/lang/String; 19 #9 = Class #30 // constants/ConsumerB 20 #10 = Class #31 // java/lang/Object 21 #11 = Utf8 <init> 22 #12 = Utf8 ()V 23 #13 = Utf8 Code 24 #14 = Utf8 LineNumberTable 25 #15 = Utf8 LocalVariableTable 26 #16 = Utf8 this 27 #17 = Utf8 Lconstants/ConsumerB; 28 #18 = Utf8 getConst 29 #19 = Utf8 ()Ljava/lang/String; 30 #20 = Utf8 SourceFile 31 #21 = Utf8 ConsumerB.java 32 #22 = NameAndType #11:#12 // "<init>":()V 33 #23 = Utf8 java/lang/StringBuilder 34 #24 = Class #32 // constants/ConstantsB 35 #25 = NameAndType #33:#34 // CONSTANT_NAME:Ljava/lang/String; 36 #26 = NameAndType #35:#36 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 37 #27 = NameAndType #37:#38 // SYS_TIME:J 38 #28 = NameAndType #35:#39 // append:(J)Ljava/lang/StringBuilder; 39 #29 = NameAndType #40:#19 // toString:()Ljava/lang/String; 40 #30 = Utf8 constants/ConsumerB 41 #31 = Utf8 java/lang/Object 42 #32 = Utf8 constants/ConstantsB 43 #33 = Utf8 CONSTANT_NAME 44 #34 = Utf8 Ljava/lang/String; 45 #35 = Utf8 append 46 #36 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder; 47 #37 = Utf8 SYS_TIME 48 #38 = Utf8 J 49 #39 = Utf8 (J)Ljava/lang/StringBuilder; 50 #40 = Utf8 toString 51{ 52... 省略 53} 54SourceFile: "ConsumerB.java"
###追記
質問が「定数を利用するクラスのサイズを減らすために、定数クラスをスタティックイニシャライズすべきか」ならば、はい、そのとおりです。
###文字列リテラル
定数が文字列リテラルの場合は、リテラルが初めて使用された時、String.internが実行され、唯一つのインスタンスへの参照に置き換わります。ただし、これが行われるのはクラスのロード時ではありません。
投稿2016/10/22 13:29
編集2016/10/22 14:25退会済みユーザー
総合スコア0
0
ベストアンサー
それなら定数クラスに限らず全てのクラスの全ての定数をstaticイニシャライザで初期化した方が良いのではないか
について簡単に検証してみたところ、
定数クラス以外では、一概に
「スタティックイニシャライザを使った方がクラスファイルのサイズが小さくなる」
とは言えないようです。
まず、以下2通りのコードを用意しました。
■定数
java
1public class Main { 2 3 private static final String JUGEMU = "じゅげむじゅげむごこうのすりきれ..."; // 実際はもっと長い文字列 4 5 public static void main(String[] args) { 6 System.out.println(JUGEMU); 7 System.out.println(JUGEMU); 8 System.out.println(JUGEMU); 9 System.out.println(JUGEMU); 10 System.out.println(JUGEMU); 11 } 12 13}
■スタティックイニシャライザ(以下 S.I. と表記)
java
1public class Main { 2 3 private static final String JUGEMU; 4 5 static { 6 JUGEMU = "じゅげむじゅげむごこうのすりきれ..."; // 実際はもっと長い文字列 7 } 8 9 public static void main(String[] args) { 10 System.out.println(JUGEMU); 11 System.out.println(JUGEMU); 12 System.out.println(JUGEMU); 13 System.out.println(JUGEMU); 14 System.out.println(JUGEMU); 15 } 16 17}
で、System.out.println(JUGEMU);
部分の行数を徐々に増やしながらそれぞれのクラスファイルのサイズを確認してみると、以下のようになりました。
行数 定数 増分 S.I. 増分 5 1022byte - 1080byte - 10 1082byte 60 1145byte 65 20 1202byte 120 1275byte 130 30 1322byte 120 1405byte 130
どういうわけかは分かりませんが、スタティックイニシャライザを使ったコードの方が、ファイルサイズの増え方が大きくなっております。
※検証した環境は
OS : Mac OS X 10.9
JDK : 1.7.0
追記
定数とS.I.それぞれのクラスファイルについて、
System.out.println(JUGEMU);
が5行のときと10行のときに"javap -v"した結果の差分を見ると、
以下のようになりました。
■定数
< Last modified 2016/10/23; size 1022 bytes < MD5 checksum 31799146ce54620a0fa358c75934c51a --- > Last modified 2016/10/23; size 1082 bytes > MD5 checksum e0210a800e994184e19cdda5fffb2dd1 80c80,95 < 40: return --- > 40: getstatic #21 // Field java/lang/System.out:Ljava/io/PrintStream; > 43: ldc #8 // String ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? > 45: invokevirtual #27 // Method java/io/PrintStream.println:(Ljava/lang/String;)V > 48: getstatic #21 // Field java/lang/System.out:Ljava/io/PrintStream; > 51: ldc #8 // String ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? > 53: invokevirtual #27 // Method java/io/PrintStream.println:(Ljava/lang/String;)V > 56: getstatic #21 // Field java/lang/System.out:Ljava/io/PrintStream; > 59: ldc #8 // String ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? > 61: invokevirtual #27 // Method java/io/PrintStream.println:(Ljava/lang/String;)V > 64: getstatic #21 // Field java/lang/System.out:Ljava/io/PrintStream; > 67: ldc #8 // String ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? > 69: invokevirtual #27 // Method java/io/PrintStream.println:(Ljava/lang/String;)V > 72: getstatic #21 // Field java/lang/System.out:Ljava/io/PrintStream; > 75: ldc #8 // String ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? > 77: invokevirtual #27 // Method java/io/PrintStream.println:(Ljava/lang/String;)V > 80: return 87a103,107 > line 12: 48 > line 13: 56 > line 14: 64 > line 15: 72 > line 16: 80 90c110 < 0 41 0 args [Ljava/lang/String; --- > 0 81 0 args [Ljava/lang/String;
■S.I.
< Last modified 2016/10/23; size 1080 bytes < MD5 checksum c3f1c460efa26e7659fba52364f59722 --- > Last modified 2016/10/23; size 1145 bytes > MD5 checksum 81b04a322bcb9ff572487284407addf3 95c95,110 < 45: return --- > 45: getstatic #23 // Field java/lang/System.out:Ljava/io/PrintStream; > 48: getstatic #12 // Field JUGEMU:Ljava/lang/String; > 51: invokevirtual #29 // Method java/io/PrintStream.println:(Ljava/lang/String;)V > 54: getstatic #23 // Field java/lang/System.out:Ljava/io/PrintStream; > 57: getstatic #12 // Field JUGEMU:Ljava/lang/String; > 60: invokevirtual #29 // Method java/io/PrintStream.println:(Ljava/lang/String;)V > 63: getstatic #23 // Field java/lang/System.out:Ljava/io/PrintStream; > 66: getstatic #12 // Field JUGEMU:Ljava/lang/String; > 69: invokevirtual #29 // Method java/io/PrintStream.println:(Ljava/lang/String;)V > 72: getstatic #23 // Field java/lang/System.out:Ljava/io/PrintStream; > 75: getstatic #12 // Field JUGEMU:Ljava/lang/String; > 78: invokevirtual #29 // Method java/io/PrintStream.println:(Ljava/lang/String;)V > 81: getstatic #23 // Field java/lang/System.out:Ljava/io/PrintStream; > 84: getstatic #12 // Field JUGEMU:Ljava/lang/String; > 87: invokevirtual #29 // Method java/io/PrintStream.println:(Ljava/lang/String;)V > 90: return 102a118,122 > line 16: 54 > line 17: 63 > line 18: 72 > line 19: 81 > line 20: 90 105c125 < 0 46 0 args [Ljava/lang/String; --- > 0 91 0 args [Ljava/lang/String;
有意な違いは、
「"定数"バージョンのクラスファイルでは定数JUGEMU
にアクセスする際の命令が ldc であるのに対して、
"S.I."バージョンでは getstatic であること」
だけです。
この結果から、
- 定数を、それを定義したクラスの内部で使用する際はインライン展開されない
- 命令 getstatic は、ldc より1バイト大きい
ということが予想できます。
ちなみに、私の個人的な意見としては、
よほどシビアな要件を課されているのでもない限り、
「たとえ定数クラスであったとしても、スタティックイニシャライザを使う必要はない」
と考えます。
アプリケーションの開発についてもKISSの原則が当てはまると考えるからです。
投稿2016/10/22 15:28
編集2016/10/23 07:51総合スコア4791
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/10/23 07:52
退会済みユーザー
2016/10/23 10:51
0
定数クラス以外のクラスの定数をstaticイニシャライザを使用することで、サイズが小さくでき、
クラスファイルを少しでも小さくしたいならそのようにすれば良いと思います。
一昔前ではガラケーに乗せるJVMで実行させるJavaや、最近ではIoT/M2Mで利用するH/Wで実行するJavaはメモリの制限が厳しい場合があります。その場合は少しでもクラスを小さくすべきと思います。
クラスファイルを小さくする理由がなければあまり拘る部分ではないかな。
投稿2016/10/22 09:07
総合スコア6621
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/10/22 09:29 編集
2016/10/22 09:36
2016/10/22 12:01
2016/10/22 15:04
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。