Effective Java 一人読書会開始(2章:オブジェクトの生成と消滅編)

| コメント(0) | トラックバック(0)

effective_java.jpg
てなわけで読み始めました。

けどこの本(以前にもチラッと書いたような気がするが...)おそらく原本が長文だらけなんだろうな。どうも日本語訳が馴染めない。気を抜くとすぐに分からなくなる。これは後々読み返しても再び理解し直すまでのコストがでかいなと感じたので、自分なりに解釈した内容を要点として箇条書き風にまとめていこうかなぁと思う。色々書きたいが、本の中身書きすぎてもそれはそれで問題なのが難しいところだ。むぅ。

当然、自分なりの解釈なので間違ってることもあるかもしれない。
違うよ。全然違うよ。と思った方はコメントやらトラバやらで指摘していただければ幸い。

---
1. staticなfactory methodのお話。
new Hoge();ではなくHoge.getInstance();でインスタンスを生成する手法のメリットとデメリットを挙げている。

メリット
-分かり易い名前を付けられる。コンストラクタに渡す引数が多いのは大抵分かりづらい状態になってる。
-コンストラクタに比べて柔軟にインスタンスが返せる。(ex.渡すパラメータによって動的に返すオブジェクトを変更, immutableやsingletonなクラスの場合、method内で使い回すような処理を入れたりとか。)

応用例が分かりづらかった。
返すオブジェクトのクラスをpublicにすることなく、APIで返せるってなんぞ?と思った。
これは実際の実装例を見た方が早い。Collections.synchronizedMapのソースとかマジお勧め。

デメリット
-staticなfactory method + (default | private)なコンストラクタ という合わせ技を使うと自由にサブクラス作れね→継承じゃなくコンポジット促すのでok
-他のstatic methodに埋もれがち。javadocだと目立たね
→命名規則を一般的なものにしてすぐ分かるように。
 *valueOf():このmethodに渡したパラメータと意味的に同じものを返す。Integer等のラッパークラス等に多い。
 *getIncetance():その他

staticなfactory methodかコンストラクタか?
→迷うなら素直にコンストラクタで。

後、Immutableクラス(不変クラス)のインスタンス比較は.equals()じゃなく==でおk というのは目から鱗が落ちた。そらそうか。

--------------
2.singletonなクラスの作り方。
singletonについてはググれば大量に情報が出てくるので割愛。

知らなかったのはsingletonを直列化するときはsingletonクラス内でreadResolve()を実装する必要があるという部分。readResolve()内で唯一のインスタンスを返すコードを書く。これがないと直列復元化したときにsingletonなのにコピーが発生しちゃう。

@see java.io.Serializable

本では挙げられていなかったが、マルチスレッド環境下でもsingletonが複数存在する問題がある。(参考:http://www.atmarkit.co.jp/fjava/javatips/075java007.html)

--------------
3.utilクラス
java.lang.Mathのようなpublic staticだらけのクラスのコンストラクタはprivateにしてnew不能にする。

--------------
4.オブジェクトの再利用
-immutableなオブジェクトは再利用できる。
良くない例としてnew String("hoge");が挙げられている。これは"hoge"という不変クラスのコピーを作るので
javadocに書いてあるように使っちゃイカンという話。

-不変クラスにコンストラクタとstatic factory methodの両方が用意されてる場合はstatic factory methodの方が工夫されてたりするのでそっち選んだ方がイイ。
(この辺は1.と絡むなぁ...。)
本当かと思ってIntegerのvalueOf(int i)のソースを見てみた。

public static Integer valueOf(int i) {
	final int offset = 128;
	if (i >= -128 && i <= 127) { // must cache
		return IntegerCache.cache[i + offset];
	}
	return new Integer(i);
}
 -128 ~ 127はキャッシュ使ってた!しらんかった!

-よく呼ぶmethod内でのnewは避ける!
isXXX()内で判定基準として使われるオブジェクトは使い回せ。

-ちょっと脇道にそれてLazy Initializationに関する話が少々。

-可変だけど再利用できる例
"ある特定のオブジェクトに対する" GoFアダプターパターンのアダプターに当たるクラス。
(この辺分かりづらかった。要するにアダプターは内部に保持してる移譲先オブジェクト,アダプティーの代替インターフェースを提供してるだけなので、使う側からしたら1コのアダプターさえあれば十分だよねという話。)

-再利用の執着に注意
オブジェクトの再利用によるペナルティ(bug,security hole) >> 不要なコピーによるペナルティ(メモリの無駄喰い)
これは頭に入れておこうとおもた。

--------------
5.メモリリーク
起こしやすい例
-クラスが独自のobject poolを保有してるとき。→使わなくなった参照にはnullを。
-キャッシュ機構を自分で実装したとき。→WeakHashMapを使え。

注意しなきゃイカンのは「やべ、今までnullなんて突っ込んだこと無かった!」と思ってはいけないことだろう。参照にnullをあえて設定するのは上記ぐらいのもんで、通常よく使うローカル変数とかはスタックフレームを抜ければまず間違いなく解放される。

WeakHashMapなんてクラス、初めて知ったよ。参照の種類やReferenceクラスに関しては過去にブックマークしたEwigkeitさんのこの記事が詳しい。
実装の仕方はjavadocに載ってる。

実装上の注意: WeakHashMap 内の値オブジェクトは、通常の強参照によって保持されます。このため、値のオブジェクトが直接的にも間接的にも強くそれ自体のキーを参照しないようにしてください。そうすれば、キーが破棄されないようになります。値のオブジェクトが WeakHashMap 自体を介してそのキーを間接的に参照するようにしてください。つまり、値のオブジェクトはほかのキーオブジェクトを必ず参照し、その関連付けられている値のオブジェクトが今度は最初の値のオブジェクトのキーを必ず参照します。この問題に対応する 1 つの方法は、値自体を m.put(key, new WeakReference(value)) のように、挿入前に WeakReferences にラップし、次に各 get でラップ解除することです。

取り出すときはこんな感じだろう。
a.)WeakReference wRef =m.get(key);
b.)wRefのnullチェック
c.)wRef.get();

ただし、この方法はGC依存なのでいつ消えるかわからんなんてヤダ!って人のためにEffective Javaでは代替案も示してくれている。LinkedHashMapを継承する方法だ。使い方としてはremoveEldestEntryメソッドのドキュメントを見ればよいのだが、イマイチ分かりづらかった。自分はremoveEldestEntry()とコレを使用しているaddEntry()のソースを見た方が分かり易かった。

試しに組んでみた。

追加時に一番古いもんから消えて行くからWeakHashMapよりは動きが予想できる。

--------------
6.finalizerの話
Object.finalize()を継承する方法は×。
GC依存なのでいつ実行されるのか、そもそも実行されるのかどうかも分からない。
→Stream系クラスに見られるようなclose()とかを用意しておき、利用者がfinally句でこれを呼び出すようにするのが正解。

Object.finalize()の存在意義は?
 -close等が呼ばれなかったときの保険
 -(いつ回収しても良いような)あまり重要でないネイティブリソースを持つオブジェクトならok

finalize実装時の注意点
 -finalize()をきちんと実装したあるクラスの子クラスを作るときは子のfinalize()内でfinally句併用でsuper.finalize()を呼ぶ。P22にコード例あり。

この注意点を無視した子クラスから親を守る方法としてfinalizer guardianというパターン手法の説明を後半でしている。P23にコード例あり。

あるクラスHoge内に匿名クラスを作り、この中でHogeの後始末を行うfinalize()を実装し、この匿名クラスのオブジェクトを一個だけHogeのインスタンスフィールドhogeFinalizerGuardianとして持たせてやる。そうしてやるとHogeと匿名クラスのインスタンスが1対1の関係になるので、仮にHogeのインスタンスがGC対象となったらhogeFinalizerGuardianも一緒にGC対象となるってわけ。もし子クラスを定義されちゃっても必ずhogeFinalizerGuardianもくっついてくるので、子は親のfinalizerを呼ばなくて済む。

コレ考えたヤツは頭いいなぁと思った。

#一応2章はココまで。3章に進みます

トラックバック(0)

トラックバックURL: https://hoge.sub.jp/blog-cgi/mt/mt-tb.cgi/1556

このブログ記事について

このページは、Lyoが2008年4月 8日 22:48に書いたブログ記事です。

ひとつ前のブログ記事は「寝まくり...のつもりが」です。

次のブログ記事は「飲み会でした」です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

月別 アーカイブ

OpenID対応しています OpenIDについて
Powered by Movable Type 7.9.3