#ファイナライザを避ける
ファイナライザ(finalizer)は、予測不可能で、たいていは危険であり、一般には必要ない。有効な使用方法は多少あるけれども基本的に使わないほうがいい、というお話。
##用語集
###ファイナライザ
finalize()メソッドのこと。
finalize()メソッドは、Java言語におけるすべてのクラスの継承元であるjava.lang.Object クラスに定義されている。
すなわち、すべてのクラスは finalize()メソッドを持つ。
ガベージコレクタが走ったタイミングで呼ばれるメソッド。
###ネイティブピア(native peer)
通常のオブジェクトがネイティブメソッドを通して委譲を行うネイティブオブジェクト。
通常のオブジェクトではないので、ガベージコレクタは感知せず通常のオブジェクトが回収される時にネイティブピアを回収できない。
###ファイナライザ連鎖(finalizer chaining)
クラスがファイナライザを持ちサブクラスがそれをオーバーライドしているならば、そのサブクラスのファイナライザはスーパークラスのファイナライザを手作業で呼び出さなければならない(例2)
###ファイナライザガーディアン(finalizer guardian)
サブクラスの実装者がスーパークラスのファイナライザをオーバーライドして、スーパークラスのファイナライザを呼び出すのを忘れたとしたら、スーパークラスのファイナライザは決して呼ばれることがない。このような不注意あるいは悪意のあるサブクラスから防御するためにファイナライザガーディアンが有効である(例3)
##サンプルコード
//終了メソッドの実行を保証するtry-finallyブロック
Foo foo = new Foo(...);
try {
//fooで行わなければならない処理を行う
...
} finally {
foo.terminate(); //明示的終了メソッド
}
//手作業によるファイナライザ連鎖
@Override protected void finalize() throws Throwable {
try {
... //サブクラスのファイナライザ処理
} finally {
super.finalize();
}
}
//ファイナライザガーディアンのイデオム
public class Foo {
//このオブジェクトの唯一の目的は、外側のFooオブジェクトのファイナライズを行うこと
private final Object finalizerGuardian = new Object() {
@Override protected void finalize() throws Throwable {
... //外側のFooオブジェクトをファイナライズする
}
};
... // 残りは省略
}
##ファイナライザの2つの欠点
###1つ目 即座にファイナライザが実行される保証がない
・ファイナライザが実行されるまでの時間はどれだけの長さにもなりえるので、時間的に制約のあることをファイナライザで行うべきではない
・重要な永続性のある状態を更新するのにファイナライザに決して頼るべきではない
###2つ目 ファイナライザの使用には深刻なパフォーマンスのペナルティがある
・ファイナライザを持つオブジェクトの生成と解放は、そうでないオブジェクトと比べて約430倍遅い
##ファイナライザを書く代わりにどのような実装をすべきか?
・明示的終了メソッド(explicit termination method)を提供する(例1)
・終了を保証するために明示的終了メソッドはたいていtry-finally構文と一緒に使用される
##ファイナライザが有効な場合
###1つ目 明示的終了メソッドの呼び出しをオブジェクトの所有者が忘れた場合の「安全ネット」として振舞う場合
###2つ目 ネイティブピア(native peer)を回収する場合
#続く
【Effective Javaを読む】 第3章 項目8 『equalsをオーバーライドする時は一般契約に従う』
https://qiita.com/Natsukii/items/bcc5846dbfa69bfda9b0