chapter2
Post on 21-Jun-2015
262 Views
Preview:
TRANSCRIPT
Effective Java 第2章 オブジェクトの生成と消滅
2
目次
項目1 コンストラクタの代わりに static ファクトリーメソッドを検討する
項目2 数多くのコンストラクタパラメータに直面した時にはビルダーを検討する
項目3 privateのコンストラクタかenum型でシングルトン特性を強制する
項目4 privateのコンストラクタでインスタンス化不可能を強制する
項目5 不必要なオブジェクトの生成を避ける
項目6 廃れたオブジェクトを取り除く
項目7 ファイナライザを避ける
3
項目1 コンストラクタの代わりに static ファクトリーメソッドを検討する
• static ファクトリーメソッドとは
• クラスのインスタンスを返す staticのメソッドのこと
• static ファクトリーメソッドの長所
• 名前を持つことができる
• 適切な名前をつけたstatic ファクトリーメソッドは使いやすい
• コンストラクタで同じシグネチャを持てない、という制約からも解放される
(シグネチャ:引数の数, 型が同じメソッド)
4
• メソッドが呼び出されるごとに新たなオブジェクトを生成する必要がない
• 不必要に重複したオブジェクトの生成を回避でき、パフォーマンスが向上する
• パラメータ化された型のインスタンス生成の面倒くささを低減できる
java7から型推論が追加されたため、あまり重要でないかも?
長い
スッキリ!
5
• メソッドの戻り値型の、任意のサブタイプ(サブクラス)のオブジェクトで返すことができる
• どのクラスを返すか、という柔軟性が広がる
• EnumSetの例
• enum型が64個以下の要素を持つ … 単一のlongにより実現されているRegularEnumSet
• enum型が65個以上の要素を持つ … long配列で実現されているJumboEnumSetを返す
6
• static ファクトリーメソッドの短所
• サブクラスが作れない
• staticファクトリーメソッドのみ提供し、public やprotectedのコンストラクタを持たせなくした場合におこる
• 対策: 継承ではなく、コンポジションで設計する(項目16)
• staticファクトリーメソッドであることが目立たない
• コンストラクタと違い、他のメソッドに埋もれてしまう
• 対策: クラスやドキュメントで注意をひいたり、標準の命名規則に従う
valueOf…型変換メソッド
of…valueOfの簡潔な代替
getInstance … 返すオブジェクトが再利用オブジェクトの可能性あり
newInstance … 都度新しく生成したオブジェクト
など
7
項目2 数多くのコンストラクタパラメータに直面した時には ビルダーを検討する
• 数多くのパラメータを持つクラスのインスタンスを生成する場合、どのすればよいか?
• 何番目が何のパラメータかわかりにくい
• 必須パラメータかオプションパラメータかわかりにくい
• 方法は以下の3種類
• テレスコーピング・コンストラクタ
• JavaBeansパターン
• ビルダーパターン
8
テレスコーピングコンストラクタ
• 同じ名前のコンストラクタを複数用意するパターンで、必須パラメータだけのもの、必須パラメータ+オプションパラメータのものなど、複数のコンストラクタを用意する。
• 多くのパラメータがあると、
コードを書くのや読むのが困難
9
JavaBeansパターン
• パラメータなしのコンストラクタを呼び出して、それからセッターメソッドを呼び出し、各パラメータを設定
• 生成が複数の呼び出しに分割されるので、
JavaBeansは不整合な状態になりえる
10
ビルダーパターン
• ビルダーオブジェクトを取得→ビルダーオブジェクトのセッターを呼び出してパラメータを設定→buildメソッドを呼び出してオブジェクトを生成
• 読みやすく、書きやすい
• 可変長のパラメータを持つことができる
• Abstract Factoryになりうる
• パフォーマンス重視の場合は、問題になるかも?
• 多くのパラメータを設計する際は、ビルダーパターンは良い選択
11
12
項目3 privateのコンストラクタかenum型でシングルトン特性を 強制する
• シングルトン
• 1度しかインスタンスが生成されないクラス
• シングルトンの実装方法は以下の3種類
• privateコンストラクタ + public static final field
• privateコンストラクタ + public static factory
• enum型の単一要素
13
privateコンストラクタ + public static final field • privateのコンストラクタは Elvis.INSTANCEを初期化するために1度だけよ
ばれる
• リフレクションに注意
• Javaクラスからフィールドやメソッドなどの情報を取得する API
• privateのコンストラクタを呼び出すことができる
• シリアライズに注意
• 単にimplements Srializableを実装すればよいわけではない
• すべてのインスタンスフィールドを transientと宣言して、 readResolveメソッドを提供しなければならない(デシリアライズ時に新たなインスタンスが生成されるのを防ぐため)
( transient … そのフィールドを
シリアライズ対象外にする )
14
privateコンストラクタ + public static factory
• 注意点は前と同じ
• 柔軟性がある
• APIを変えずにシングルトン ⇔ マルチインスタンスを変更できる
15
enum型の単一要素
• いまのところベスト
• リフレクション対策○
• シリアライズ対策○
16
項目4 privateのコンストラクタでインスタンス化不可能を強制する
• java.jang.Mathやjava.util.Collectionsのようなユーティリティクラスは、インスタンス化は意味がないため、それを明確に表現するべきである
• クラスを抽象化するアプローチ
• サブクラスを作るとインスタンス化できてしまう
• 継承するために設計されているとユーザに誤解させる
• privateのコンストラクタによるアプローチ
• こちらのほうがよい
17
項目5 不必要なオブジェクトの生成を避ける
• オブジェクトの生成はコストがかかる
• 機能的に同じオブジェクトが必要な場合は、再利用するのがよい
• 文字列リテラルの場合
• これをやってはいけない
• 実行されるごとに不要なインスタンスが生成される
• こちらの場合、実行されるごとに新たなインスタンスを生成せず、1つのStringインスタンスを使用
• 同じ文字列リテラルを別のコードが持っている場合、JVMが再利用してくれることが保証されている
18
• staticファクトリーメソッド(項目1)の利用
• 不必要なオブジェクトを回避できる
• 変更されないと分かっている可変オブジェクトを再利用する
• 1946~1964年の間にその人が生まれたかを判別するisBabyBoomerメソッドの例
• これをやってはいけない
19
• メソッドが呼び出されるたびに、不必要に新たなCalendarインスタンス、TimeZoneインスタンス、Dateインスタンスが生成されてしまう
• static初期化子で改良した例
20
• オートボクシングによるラッパーインスタンス生成を回避する
• これをやってはいけない
• long i をLong sumに加算するごとに1つのインスタンスが生成される
• sumの宣言はLongからlongに変更する(実行時間が約1/7に短縮)
21
• オブジェクトプールは多用しない
• コードを乱雑に、メモリ量を増加させる原因になる
• データベースコネクションなど、生成コストが高いものを除いては、JVMに任せる
• 防御的コピー(項目39)
• 新たなオブジェクトを生成すべき場合には、既存のオブジェクトを再利用しない
• オブジェクトを再利用した場合、悪質なバグやセキュリティホールにつながる危険があるため、オブジェクトの再利用には、十分は注意を払う必要がある
22
項目6 廃れたオブジェクト参照を取り除く
• メモリリークを引き起こしかねないコードの例
23
• スタックが大きくなってその後小さくなっても、スタックから取り出されたオブジェクトはGCされない
• スタックが参照されることのないオブジェクトの参照を保持している(廃れた参照)
• 配列の要素が無効かどうかはプログラマにしか分からない
• null を代入して回避
• 過度にnullを入れるのは、プログラムを不必要に乱雑にするため、好ましくない
• 狭いスコープで変数を定義できればそうして、JVMにGCさせるようにする
24
• キャッシュよるメモリリーク
• オブジェクト参照を一旦キャッシュへ入れてしまうと、オブジェクト参照がそこにあることを忘れがち、オブジェクトが不必要になってもキャッシュに残りがち
• リスナーやコールバックによるメモリリーク
• コールバックを登録し、解除し忘れて蓄積してしまう
→WeakHashMapを使うとよい
• キーを「弱参照」で格納
• マップ以外のどこからもキーが参照されなくなると、GCの対象となる
25
項目7 ファイナライザを避ける
• ファイナライザとは
• オブジェクトが消滅するときに呼び出されるメソッド
• なんらかの後処理が必要なときに使用
• Objectクラスで定義されている finalize()メソッドをオーバーライドして実装
• ファイナライザの問題点
• 即時性があるわけではなく、JVMによってタイミングが大きく異なる
• 永続性のある情報の更新に使用してはいけない
• パフォーマンスが悪くなる
• ファイナライザを持つオブジェクトの生成と解放は、それを持たないものに比べて430倍遅かった
26
• 代替案: 明示的終了メソッドを提供する
• InputStreamのcloseや、Timerのcancel など
• try-finally構文を用いて使用するのが一般的(java7では try-with-resourcesが追加されている)
• ファイナライザが有効な場合
• 安全ネット
• 明示的終了メソッドの呼び出しを忘れた、または呼び出しに失敗した場合の保険
• クライアントのバグであることを示すために、警告を記録すべき
27
• ネイティブピア
• 通常のオブジェクトがネイティブメソッドを通して委譲を行うオブジェクトのこと
• 通常のオブジェクトが回収されるときに、ネイティブピアを一緒に回収できないので、ネイティブピアが重要な資源を保持していない場合、その回収にファイナライザを使ってもよい
• ファイナライザガーディアン
• サブクラスの super.finalize()の呼び忘れ対策
• スーパークラスのfinalize()をオーバーライドして super.finalize()を実行しないと 、サブクラスのファイナライズは実行されない
• 上記の有効な場合以外は、ファイナライザの実装は避けるべき
top related