質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

解決済

3回答

8058閲覧

定数は全てstaticイニシャライザで初期化するか

lupus_dingo

総合スコア257

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

0グッド

0クリップ

投稿2016/10/22 08:45

お世話になってます。

定数クラスはstaticイニシャライザで初期化すべきととある書籍で読みました。

理由はクラスファイルのサイズが大きくなるかららしいのですが、それなら定数クラスに限らず全てのクラスの全ての定数をstaticイニシャライザで初期化した方が良いのではないかと思うのですがどうでしょうか?

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答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
KiyoshiMotoki

総合スコア4791

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2016/10/22 23:33

興味深いです。私も試してみます。javapでクラスの内部を調べてみませんか。
KiyoshiMotoki

2016/10/23 07:52

vornan19様 コメントありがとうございます。 回答欄に追記しました。 確認願います。
退会済みユーザー

退会済みユーザー

2016/10/23 10:51

ありがとうございます。いわゆるインライン展開だと思っていたけれどそうではなかった。メソッドの命令に直接文字列リテラルが埋め込まれるわけではなく、コンスタントプールに1つだけ文字列リテラルが存在しています。これがJavaのクラス構造です。文字列リテラルを使おとすると、すでに文字列が確定している場合はldc命令を使えますが、S.I.を使う場合は、クラスのロード時まで文字列リテラルが確定しないので、自己参照する(getstatic)しかない。ここまでは確認できました。 では、Javaのインライン展開と言っていることはなんでしょうか。それは、定数を利用するクラスに定数そのものがコピーされてしまうことです。定数クラスと同じ構造が、定数を参照するクラスにも現れます。S.I.を使う定数を他のクラスが利用する場合はgetstaticを使わざるを得ません。これに対して定数が確定している場合は、定数がそのまま参照クラスにコピーされldc命令を使えます。このコピーのことをインライン展開と呼んでいます。クラスのサイズが大きくなるのは、定数を定義するクラスではなく、定数を参照クラスです。クラスのサイズ問題だけでなく、定数を変更すると参照クラスを再コンパイルしないと定数の参照が変わらないという問題も発生します。
guest

0

定数クラス以外のクラスの定数をstaticイニシャライザを使用することで、サイズが小さくでき、
クラスファイルを少しでも小さくしたいならそのようにすれば良いと思います。
一昔前ではガラケーに乗せるJVMで実行させるJavaや、最近ではIoT/M2Mで利用するH/Wで実行するJavaはメモリの制限が厳しい場合があります。その場合は少しでもクラスを小さくすべきと思います。
クラスファイルを小さくする理由がなければあまり拘る部分ではないかな。

投稿2016/10/22 09:07

moonphase

総合スコア6621

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KiyoshiMotoki

2016/10/22 09:29 編集

横から失礼します。 moonphase様 > 定数クラス以外のクラスの定数をstaticイニシャライザを使用することで、サイズが小さくでき る理由は、 「定数がインライン展開されるのを防げるから」 という認識でよろしいでしょうか? であれば、staticイニシャライザを使用した場合は使用しない場合に比べて クラスファイル(および、クラスがロードされたときに使用するメモリ)のサイズを小さくできる反面、実行速度が落ちる可能性がありますね。
moonphase

2016/10/22 09:36

コメントありがとうございます。 > 定数クラス以外のクラスの定数をstaticイニシャライザを使用することで、サイズが小さくでき いえ、これは試してないので知りません。もし小さくなるなら〜 との前置きです。 もちろん実行速度は犠牲になるでしょうね。 実際に業務などでサイズを工夫する場合、最終的にはメモリの要件、速度の要件を満たせばいいだけですので、そこはその人が判断すればいいかな。
lupus_dingo

2016/10/22 12:01

回答ありがとうございます。 > 定数クラス以外のクラスの定数をstaticイニシャライザを使用することで、 すみません、定数クラス以外は、といは定数クラスでもそれ以外のクラスでもという認識で合っているでしょうか?
KiyoshiMotoki

2016/10/22 15:04

moonphase様 返信ありがとうございます。 > これは試してないので知りません。もし小さくなるなら〜 との前置きです。 失礼、早とちりしましたw。 > そこはその人が判断すればいいかな。 仰る通りですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問