C++をはじめとした他のプログラミング言語を触れていた方の中には、Javaのデストラクタ機能ってないのかと検索したことがある方も多いのではないでしょうか。
本記事では、デストラクタについての概要とJavaでの類似概念として良く名前のあがるfinalizeの処理についてご紹介していきます。
目次
Javaにデストラクタはない
結論としてJavaにデストラクタの処理はありません。
ではなぜJavaにデストラクタがないのかについてももう少し詳しく確認していきましょう。
そもそもデストラクタとは
デストラクタは、オブジェクトを破棄してメモリを開放するための処理で、C++などの言語では必須処理の1つです。
Javaでもオブジェクトを初期化する際に利用する「コンストラクター」を聞いたことがある方も多いと思いますが、その反対の処理を担うのがデストラクタです。
なぜJavaにはデストラクタがないの?
Javaにデストラクタがない理由の1つとして「ガベージコレクター」の存在が挙げられます。
ガベージコレクターは、一定の条件を満たす使用しなくなったオブジェクトを自動で消去してくれる機能で、プログラマーがメモリ管理をする必要がない便利機能です。
Javaではメモリ管理を行う必要がないとの設計思想からデストラクタが提供されていない要因とも考えられます。
Javaのデストラクタ類似処理finalize
Javaではデストラクタの機能がないと説明しましたが、類似処理として提供されている機能に「finalize」の存在があげられます。
厳密にはデストラクタとは異なる機能ですが、どういった処理が行われているのか確認しておきましょう。
finalizeメソッド
ガベージコレクターでは、オブジェクトを削除する際finalizeメソッドを呼びだしています。
このfinalizeメソッドを利用することにより、デストラクタに近いメモリ解放の処理を実施することが可能です。
ただしJava9以降では非推奨とされており、finalizeを利用することでガベージコレクターの機能を低下させる可能性があることからも、積極的に利用すべきではないとされています。
デストラクタとfinalizeの違い
Javaではガベージコレクターによるオブジェクトの削除管理を行っているため、ガベージコレクターが削除対象と判定するまでは例え利用されていないオブジェクトでも削除されない可能性があります。
つまりメモリ不足に陥らず、削除対象と判定されない場合finalizeの処理が呼ばれない可能性も十分に考えられます。
デストラクタであればオブジェクトを利用し終われば必ず削除処理によりメモリ解放が行われているのに対し、finalizeではあくまでガベージコレクターの判定任せといった点で厳密には処理が異なります。
Javaサンプルで類似デストラクタの処理を確認
実際に推奨されていない使い方ではありますが、finalizeメソッドをオーバーライドしてcloseメソッドを明示的に記述した場合の処理の流れを確認してみましょう。
サンプルコード
package sample; public class Main { public static void main(String[] args) { Finalize finalize = new Finalize(); finalize = null; System.gc(); } } class Finalize { public Finalize() { System.out.println("コンストラクタの呼び出し"); } public void close() { System.out.println("クローズ処理実行"); } @Override protected void finalize() throws Throwable{ try{ System.out.println("オーバーライドしたfinalize処理の呼び出し"); this.close(); } finally { super.finalize(); } } }
実行すると下記のような結果を得ることが出来ます。
コンストラクタの呼び出し オーバーライドしたfinalize処理の呼び出し クローズ処理実行
コード解説
サンプルでは生成したオブジェクトをnullに設定した上で、8行目でガベージコレクションを強制的に呼び出しています。
これによりオーバーライドしたfinalize処理が呼び出されており、25行目に記載したcloseメソッドの呼び出しが実施されていることが出力結果から分かります。
このようにオーバーライドさせたfinalizeにデータベースなどのclose処理を記述することも可能ではありますが、上述しているようにfinalizeメソッドはガベージコレクターの判定任せなところがあるため現実的な使い方ではありません。
明示的にリソースをcloseする
このようにデストラクタを利用することが出来ず、finalizeも実行タイミングが不確定であるため、明示的にリソースを閉じたい場合にはclose処理をtry~catch~finallyで記述する方法が現実的です。
package sample; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; public class Main { public static void main(String[] args) throws FileNotFoundException, IOException { FileOutputStream output = new FileOutputStream("sample.txt"); try { output.write(null); } finally { output.close(); } } }
14行目のfinallyの記述内で、ストリーム処理を明示的にクローズしていることが確認出来ます。
またJava7以降ではtry~with~resourcesの記述方法によりもう少し簡潔に記述することが出来るようになっています。
package sample; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; public class Main { public static void main(String[] args) throws FileNotFoundException, IOException { try (FileOutputStream output = new FileOutputStream("sample.txt")) { output.write(null); } } }
さいごに: Javaではデストラクタの代替としてfinalizeは利用しない
本記事では、Javaにおけるデストラクタの意味合いとfinalize処理との違いについてご紹介してきました。
結論としてJavaにはデストラクタの処理は存在せず、類似処理のfinalizeも厳密にはデストラクタとは異なります。
Java9以降ではfinalizeをオーバーライドして利用する方法も非推奨となっているため、基本的には利用しないことを念頭においてJava開発に取り組んでいきましょう。
今回はファイルの出力処理をサンプルとして記述しましたが、データベースも同様の方法でクローズ処理を記述します。