JavaでのFinal、Final、Finalizeの違い
1. 概要
このチュートリアルでは、3つのJavaキーワードの概要を説明します。 ファイナル、ファイナルと
これらのキーワードは互いに似ていますが、Javaではそれぞれの意味が大きく異なります。 それぞれの目的を学び、少しのコードを通していくつかの例を見ていきます。
2. finalキーワード
まず、 final キーワード、その使用場所、およびその理由を見てみましょう。
ただし、それぞれに同じ効果はありません:
- クラスをfinalにすると、そのクラスを拡張できなくなります。
- final をメソッドに追加すると、そのメソッドをオーバーライドできなくなります。
- 最後に、 final をフィールド、変数、またはパラメーターの前に置くと、参照が割り当てられると変更できなくなります(ただし、参照が可変オブジェクトに対するものである場合、その内部状態は可能です。最終的であるにもかかわらず変更)
final キーワードの詳細については、こちらをご覧ください。
finalキーワードがどのように機能するかをいくつかの例で見てみましょう。
2.1. final フィールド、パラメーター、および変数
2つのintフィールド、 final フィールド、および通常の非finalフィールドを持つParentクラスを作成しましょう。
public class Parent {
int field1 = 1;
final int field2 = 2;
Parent() {
field1 = 2; // OK
field2 = 3; // Compilation error
}
}
ご覧のとおり、コンパイラはfield2に新しい値を割り当てることを禁止しています。
次に、通常の引数と最後の引数を持つメソッドを追加しましょう。
void method1(int arg1, final int arg2) {
arg1 = 2; // OK
arg2 = 3; // Compilation error
}
フィールドと同様に、finalとして宣言されているため、arg2に何かを割り当てることはできません。
これで、これがローカル変数でどのように機能するかを示す2番目のメソッドを追加できます。
void method2() {
final int localVar = 2; // OK
localVar = 3; // Compilation error
}
驚くべきことは何も起こりません。コンパイラは、最初の割り当て後にlocalVarに新しい値を割り当てることを許可しません。
2.2. finalメソッド
ここで、 method2 をfinalにして、 Parent のサブクラスを作成するとします。たとえば、 Child で、両方のスーパークラスメソッドをオーバーライドしようとします。
public class Child extends Parent {
@Override
void method1(int arg1, int arg2) {
// OK
}
@Override
final void method2() {
// Compilation error
}
}
ご覧のとおり、 method1()をオーバーライドしても問題はありませんが、 method2()をオーバーライドしようとするとコンパイルエラーが発生します。
2.3. 最終クラス
最後に、 Child クラスをfinalにして、そのサブクラスGrandChildを作成してみましょう。
public final class Child extends Parent {
// ...
}
public class GrandChild extends Child {
// Compilation error
}
もう一度、コンパイラは文句を言います。 Child クラスは最終的なものであるため、拡張することはできません。
3. 最後にブロック
finally ブロックは、 try /catchステートメントで使用するオプションのブロックです。 このブロックには、例外がスローされるかどうかに関係なく、try/catch構造の後に実行するコードが含まれています。
最後にブロックが含まれていれば、catchブロックなしでtryブロックと一緒に使用することも可能です。 コードは、 try の後、または例外がスローされた後に実行されます。
Javaでの例外処理に関する詳細な記事がありますここ。
次に、短い例でfinallyブロックを示します。 try / catch / finally構造を持つダミーのmain()メソッドを作成します。
public static void main(String args[]) {
try {
System.out.println("Execute try block");
throw new Exception();
} catch (Exception e) {
System.out.println("Execute catch block");
} finally {
System.out.println("Execute finally block");
}
}
このコードを実行すると、次のように出力されます。
Execute try block
Execute catch block
Execute finally block
ここで、catchブロックを削除してメソッドを変更しましょう(そして throws Exception を署名に追加します):
public static void main(String args[]) throws Exception {
try {
System.out.println("Execute try block");
throw new Exception();
} finally {
System.out.println("Execute finally block");
}
}
出力は次のようになります。
Execute try block
Execute finally block
ここでthrownew Exception()命令を削除すると、出力が同じままであることがわかります。 finallyブロックの実行は毎回発生します。
4. finalizeメソッド
そして最後に、 finalize メソッドは、Objectクラスで定義された保護されたメソッドです。 参照されなくなり、ガベージコレクション用に選択されたオブジェクトに対してガベージコレクターによって呼び出されます。
他の非最終メソッドと同様に、このメソッドをオーバーライドして、ガベージコレクターによって収集されたときにオブジェクトが持つ必要のある動作を定義できます。
繰り返しになりますが、finalizeメソッドに関する詳細な記事はここにあります。
それがどのように機能するかの例を見てみましょう。 System.gc()を使用して、JVMがガベージコレクションをトリガーすることを提案します。
@Override
protected void finalize() throws Throwable {
System.out.println("Execute finalize method");
super.finalize();
}
public static void main(String[] args) throws Exception {
FinalizeObject object = new FinalizeObject();
object = null;
System.gc();
Thread.sleep(1000);
}
この例では、オブジェクトの finalize()メソッドをオーバーライドし、オブジェクトをインスタンス化する main()メソッドを作成し、作成された変数をに設定することで参照をすぐに削除します。 null。
その後、 System.gc()を呼び出してガベージコレクターを実行し(少なくとも実行されると予想されます)、1秒待ちます(を確認するためだけです)。 JVM は、ガベージコレクターが finalize()メソッドを呼び出す機会を得る前にシャットダウンしません。
このコード実行の出力は次のようになります。
Execute finalize method
finalize()メソッドの実行は、 JVM の手元にあるガベージコレクションに依存するため、オーバーライドすることは悪い習慣と見なされることに注意してください。 さらに、このメソッドはJava9以降非推奨になりました。
5. 結論
この記事では、 final、finally 、finalizeの3つのJava類似キーワードの違いについて簡単に説明しました。
記事の完全なコードは、GitHubのにあります。