【Java】コンストラクタの代わりにStaticFactoryMethodを使う
オブジェクト指向の言語では、コンストラクタという概念がある。
一般的に以下のような形で記述する。
package staticFactoryMethod; public class Task { private int num; public Task(int num) { this.num = num; } }
ただ、コンストラクタは以下の仕様を実現することが出来ない。
- 生成されるインスタンスの数を1つに制限する
- 引数の値に応じてサブクラスを戻り値にする
そこで検討すべきであるのが、StaticFactoryMethodである。
インスタンスの生成を制限
package staticFactoryMethod; public class Singleton { private static Singleton singleton = new Singleton(); private Singleton() {} public static Singleton getInstance() { return singleton; } }
こんな感じに記述すれば、
SingletonクラスのインスタンスはgetInstanceメソッドを通じてしか取得されないし、
返されるインスタンスはいつも同じである。(Singletonパターンに該当)
package staticFactoryMethod; public class StartUp { public static void main(String[] args) { // エラーになる // Singleton singleton = new Singleton(); // いつでも同じインスタンスを取得可能 Singleton singleton = Singleton.getInstance(); Singleton singleton2 = Singleton.getInstance(); // 実行結果: true System.out.println(singleton==singleton2); } }
サブクラスを戻り値にして実装を隠す
また、引数の値に応じて異なるクラスを返すことが出来る。
使用者は自分がSmallTaskを手に入れたのかBigTaskを手に入れたのかを
気にせずに使うことが出来る。(実装を隠せる)
package staticFactoryMethod; public abstract class Task { public static Task newInstance(int num) { return num>10 ? new BigTask(num) : new SmallTask(num); } }
package staticFactoryMethod; public class StartUp { public static void main(String[] args) { Task task = Task.newInstance(64); // 実行結果: class staticFactoryMethod.BigTask System.out.println(task.getClass()); } }
【Java】String, StringBuffer, StringBuilderの使い分け
Java初心者が見かける3つの文字列系のクラスについて使い分けや特徴を書いていく。
まず結論から書くと、
String
使いどころ:
文字列を頻繁に変化(再代入、+演算子での連結など)させなくてよい場合
メリット:
メモリの確保が最小限
デメリット:
操作に対するパフォーマンスが極めて悪い(処理が遅い)
StringBuffer
使いどころ:
マルチスレッド環境下で、同じ文字列にアクセスして変更や結合などを頻繁に行う場合
メリット:
Stringより文字列の操作が速い
デメリット:
可変長であるため、初めに余分にメモリを確保する
StringBuilderよりパフォーマンスが落ちる
StringBuilder
使いどころ:
シングルスレッド環境下で、文字列の変更や結合などを頻繁に行う場合
メリット:
Stringより文字列の操作が速い(最速!)
デメリット:
可変長であるため、初めに余分にメモリを確保する
マルチスレッド環境下では実行結果が保証されない
ということで3つを使い分けるといいらしい。
とはいえスレッドセーフで文字列を変更しなければならない場合なんてそうないらしく、
Stringか操作が一番早いStringBuilderを使うのが多いとのこと。
各クラスの詳細
ここからは、各クラスについてもう少し深く掘り下げてみる。
String
Stringは固定長でimmutable(不変)な文字列を扱うクラスである。
つまり、本来は文字列を再代入したり連結できないのだが、暗黙的に新しいインスタンスを生成1している。
新しいインスタンスの生成(new)は重い処理であるためパフォーマンスが低くなる。
package stringClasses; public class StringTest { public static void main(String[] args) { String str = "俺はStringだ"; // 再代入すると内部では、新しくインスタンスが生成される // str = new String("俺はStringなのか"); str = "俺はStringなのか"; // 結合も内部では、結合した後の文字列で // 新しくインスタンスを生成している // str = new String("俺はStringなのか?"); str += "?"; } }
100回の連結にかかる時間も計測しておこう。
package stringClasses; public class StringTime { public static void main(String[] args) { String str=new String(); // 結合前の時刻を記録 long startTime = System.nanoTime(); for(int i = 0; i < 100; i++) { str += "!"; } // 結合後の時刻を記録 long endTime = System.nanoTime(); // かかった時間を表示 System.out.println(endTime - startTime); } }
出力結果(ナノ秒): 113000
StringBufferとStringBuilder
StringBufferとStringBuilderの違いは、スレッドセーフか否かである。
実際に実装を確認してみても、大きな違いはsynchronized修飾子2があるかどうか。
最後に処理速度を比較しておこう。
まずはStringBufferから。
package stringClasses; public class StringBufferTime { public static void main(String[] args) { StringBuffer strbf = new StringBuffer(); // 結合前の時刻を記録 long startTime = System.nanoTime(); for (int i = 0; i < 100; i++) { strbf.append("!"); } // 結合後の時刻を記録 long endTime = System.nanoTime(); // かかった時間を表示 System.out.println(endTime - startTime); } }
出力結果(ナノ秒): 31300
本当に記述方法が合ってるのか不安になるレベルで変わる。
次にStringBuilder。
package stringClasses; public class StringBuilderTime { public static void main(String[] args) { StringBuilder strbl = new StringBuilder(); // 結合前の時刻を記録 long startTime = System.nanoTime(); for (int i = 0; i < 100; i++) { strbl.append("!"); } // 結合後の時刻を記録 long endTime = System.nanoTime(); // かかった時間を表示 System.out.println(endTime - startTime); } }
出力結果(ナノ秒): 29100
使い分け超大事だなと実感した。
【Java】+演算子、StringBuilder、StringBufferの違い(実際に測ってみた。)、文字列系の型 | プログラミングマガジン
【Java】StringBuilderとStringBufferの違いをスレッドセーフの観点で検証してみた - カタカタブログ