如何理解Java中的final、finally、finalize(用法和区别)

这是有关访谈观点的重要问题。
final关键字
final(小写)是Java中的保留关键字。我们不能将其用作标识符, 因为它已被保留。我们可以将此关键字与变量, 方法以及类一起使用。 java中的final关键字具有不同的含义, 具体取决于将其应用于变量, 类或方法。
final变量:变量的值一旦初始化就无法更改。

class A { public static void main(String[] args) { // Non final variable int a = 5 ; // final variable final int b = 6 ; // modifying the non final variable : Allowed a++; // modifying the final variable : // Immediately gives Compile Time error. b++; } }

如果我们将任何变量声明为final, 则因为它是final, 所以无法修改其内容;如果我们对其进行修改, 则会出现编译时错误。
final类:该类不能子类化。当我们将任何类声明为final时,这就意味着我们不能扩展这个类,或者这个类不能被扩展,或者我们不能创建这个类的子类。
final class RR { public static void main(String[] args) { int a = 10 ; } } // here gets Compile time error that // we can't extend RR as it is final. class KK extends RR { // more code here with main method }

final方法:
该方法不能被子类覆盖。每当我们将任何方法声明为final方法时, 这都意味着我们无法覆盖该方法。
class QQ { final void rr() {} public static void main(String[] args) { } }class MM extends QQ {// Here we get compile time error // since can't extend rr since it is final. void rr() {} }

注意 :如果将某个类声明为final, 则默认该类中存在的所有方法都是自动最终的, 但变量不是.
// Java program to illustrate final keyword final class G {// by default it is final. void h() {}// by default it is not final. static int j = 30 ; public static void main(String[] args) { // See modified contents of variable j. j = 36 ; System.out.println(j); } }

输出如下:
36

finally关键字
就像final是保留关键字一样, final同样也是java中的保留关键字, 即我们不能将其用作标识符。最终关键字与尝试/捕获块并保证即使抛出异常也会执行一段代码。最终块将在try和catch块之后但在控制权移回其原始位置之前执行。
// A Java program to demonstrate finally. class Geek { // A method that throws an exception and has finally. // This method will be called inside try-catch. static void A() { try { System.out.println( "inside A" ); throw new RuntimeException( "demo" ); } finally { System.out.println( "A's finally" ); } }// This method also calls finally. This method // will be called outside try-catch. static void B() { try { System.out.println( "inside B" ); return ; } finally { System.out.println( "B's finally" ); } }public static void main(String args[]) { try { A(); } catch (Exception e) { System.out.println( "Exception caught" ); } B(); } }

输出如下:
inside A A's finally Exception caught inside B B's finally

最终可以使用多种情况。讨论如下:
情况1:程序中未发生异常
// Java program to illustrate finally in // Case where exceptions do not // occur in the program class B { public static void main(String[] args) { int k = 55 ; try { System.out.println( "In try block" ); int z = k / 55 ; }catch (ArithmeticException e) { System.out.println( "In catch block" ); System.out.println( "Dividing by zero but caught" ); }finally { System.out.println( "Executes whether exception occurs or not" ); } } }

输出如下:
In try block Executes whether exception occurs or not

在此, 上面的异常不会发生, 但是finally块仍然执行, 因为finally意味着无论是否发生异常都将执行。
以上程序流程:首先, 它从main方法开始, 然后进入try块, 在try中, 因为没有异常发生, 所以流不会捕获到块, 因此流直接从try到finally块。
情况2:发生异常并且对应的catch块匹配
// Java program to illustrate finally in // Case where exceptions occur // and match in the program class C { public static void main(String[] args) { int k = 66 ; try { System.out.println( "In try block" ); int z = k / 0 ; // Carefully see flow dosen't come here System.out.println( "Flow dosen't came here" ); }catch (ArithmeticException e) { System.out.println( "In catch block" ); System.out.println( "Dividing by zero but caught" ); }finally { System.out.println( "Executes whether an exception occurs or not" ); } } }

输出如下:
In try block In catch block Dividing by zero but caught Executes whether an exception occurs or not

在此, 发生上述异常并且找到了相应的捕获块, 但仍然最终块执行, 因为最后意味着执行异常是否发生或是否找到相应的捕获块。
以上程序流程:首先,从main方法开始,然后进入try块,在try中出现一个算术异常,并且相应的catch块也可用,因此流进入catch块。在此之后,流不会再次进入try block,因为一旦try block中出现异常,流就不会再次返回try block。在finally之后,执行,因为finally意味着执行是否发生异常或是否找到相应的catch块。
情况3:发生异常, 并且未找到/匹配相应的捕获块
// Java program to illustrate finally in // Case where exceptions occur // and do not match any case in the program class D { public static void main(String[] args) { int k = 15 ; try { System.out.println( "In try block" ); int z = k / 0 ; }catch (NullPointerException e) { System.out.println( "In catch block" ); System.out.println( "Dividing by zero but caught" ); }finally { System.out.println( "Executes whether an exception occurs or not" ); } } }

输出如下:
In try block Executes whether an exception occurs or not Exception in thread "main":java.lang.ArithmeticException: / by zero followed by stack trace.

在此, 发生以上异常, 并且未找到/匹配相应的捕获块, 但仍最终执行块, 因为finally意味着执行是否发生异常或是否找到/匹配相应的捕获块。
以上程序流程:首先从main方法开始,然后进入try block,在try中出现了一个算术异常,相应的catch块不可用,所以流不会进入catch块。在此之后,流不会再次进入try block,因为一旦try block中出现异常,流就不会再次返回try block。在finally之后,执行,因为finally意味着执行是否发生异常,是否找到相应的catch块/是否匹配。
finally块的应用:所以基本上, 使用finally块是资源释放。表示需要关闭在try块中打开的所有资源, 例如网络连接, 数据库连接, 以免打开时不会丢失资源。因此, 需要在finally块中关闭这些资源。
// Java program to illustrate // use of finally block import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; class K { private static final int SIZE = 10 ; public static void main(String[] args) {PrintWriter out = null ; try { System.out.println( "Entered try statement" ); // PrintWriter, FileWriter // are classes in io package out = new PrintWriter( new FileWriter( "OutFile.txt" )); } catch (IOException e) { // Since the FileWriter in // try block can throw IOException }// Following finally block cleans up // and then closes the PrintWriter.finally { if (out != null ) { System.out.println( "Closing PrintWriter" ); out.close(); } else { System.out.println( "PrintWriter not open" ); } } } }

输出如下:
Entered try statement PrintWriter not open

注意:finally块是防止资源泄漏的关键工具。当关闭文件或以其他方式恢复资源时, 请将代码放在finally块中以确保始终恢复资源。
jdk 1.7如何使使用finally块为可选?
直到jdk 1.6 finally块就像英雄一样, 即建议将其用于资源释放, 但是从jdk 1.7开始, 最后, block现在是可选的(但是你可以使用它)。因为当程序流到达try块的末尾时, 我们在try块中打开的资源将自动释放/关闭。
这种不使用finally块的自动资源回收概念称为try-with-resources语句。
finalize方法
这是一个垃圾回收器总是在删除/销毁符合垃圾回收条件的对象之前调用的方法,以便执行清理活动。清理活动意味着关闭与该对象相关联的资源,如数据库连接、网络连接,或者我们可以说资源回收。记住,它不是一个保留关键字。
一旦finalize方法完成, 垃圾收集器立即销毁该对象。 finalize方法存在于Object类中, 其语法为:
protected void finalize throws Throwable{}

由于Object类包含finalize方法,因此finalize方法对于每个java类都是可用的,因为Object是所有java类的父类。由于finalize方法对每个java类都可用,因此垃圾收集器可以对任何java对象调用finalize方法
现在,对象类中的finalize方法有一个空实现,在类中有清理活动,然后我们必须重写这个方法来定义我们自己的清理活动。
与完成方法有关的案例:
情况1 :
符合垃圾收集条件的对象, 将执行该对象的相应类的finalize方法
class Hello { public static void main(String[] args) { String s = new String( "RR" ); s = null ; // Requesting JVM to call Garbage Collector method System.gc(); System.out.println( "Main Completes" ); }// Here overriding finalize method public void finalize() { System.out.println( "finalize method overriden" ); } }

输出如下:
Main Completes

注意:这里上面的输出只有Main completed而不是" finalize method overrides ",因为垃圾回收器对符合垃圾回收条件的类对象调用finalize方法。上面我们做了->
s = null并且' s '是String类的对象,所以String类finalize方法将被调用,而不是我们的类(i。e,你好类)。所以我们把代码修改成->
Hello s = new Hello(); s = null;

现在我们的类, 即Hello类的finalize方法被调用。输出如下:
finalize method overriden Main Completes

【如何理解Java中的final、finally、finalize(用法和区别)】因此, 基本上, 垃圾收集器会对该符合垃圾收集条件的类对象调用finalize方法, 因此, 如果String对象符合垃圾收集条件, 则String类的finalize方法将被调用, 不是Hello类定型方法。
情况2:
我们可以显式调用finalize方法, 然后像普通方法调用一样执行它, 但是对象不会被删除/销毁
class Bye { public static void main(String[] args) { Bye m = new Bye(); // Calling finalize method Explicitly. m.finalize(); m.finalize(); m = null ; // Requesting JVM to call Garbage Collector method System.gc(); System.out.println( "Main Completes" ); }// Here overriding finalize method public void finalize() { System.out.println( "finalize method overriden" ); } }

输出如下:
finalize method overriden //call by programmer but object won't gets destroyed. finalize method overriden //call by programmer but object won't gets destroyed. Main Completes finalize method overriden //call by Garbage Collector just before destroying the object.

注意:由于finalize是方法而不是保留关键字, 因此我们可以调用finalize方法明确地, 则它会像普通方法调用一样执行, 但不会删除/销毁对象。
情况3:
A部分
如果程序员调用finalize方法, 则在执行finalize方法时会出现一些未经检查的异常。
class Hi { public static void main(String[] args) { Hi j = new Hi(); // Calling finalize method Explicitly. j.finalize(); j = null ; // Requesting JVM to call Garbage Collector method System.gc(); System.out.println( "Main Completes" ); }// Here overriding finalize method public void finalize() { System.out.println( "finalize method overriden" ); System.out.println( 10 / 0 ); } }

输出如下:
exception in thread "main" java.lang.ArithmeticException: / by zero followed by stack trace.

So关键点是:如果程序员调用finalize方法, 则在执行finalize方法时会出现一些未经检查的异常, 那么JVM将通过异常上升来异常终止程序。因此, 在这种情况下, 程序终止为异常.
b部分)
如果垃圾收集器调用finalize方法, 则在执行finalize方法时会出现一些未经检查的异常。
class RR { public static void main(String[] args) { RR q = new RR(); q = null ; // Requesting JVM to call Garbage Collector method System.gc(); System.out.println( "Main Completes" ); }// Here overriding finalize method public void finalize() { System.out.println( "finalize method overriden" ); System.out.println( 10 / 0 ); } }

输出如下:
finalize method overriden Main Completes

So关键点是:如果垃圾收集器调用finalize方法, 而在执行finalize方法时出现一些未经检查的异常, 则JVM忽略该异常和程序的其余部分将正常继续。因此, 在这种情况下, 程序终止为正常并没有异常。
要点:
无法保证调用finalize的时间。在任何地方都没有引用对象之后的任何时候都可以调用它(可能是垃圾回收)。
JVM在执行finalize方法时不会忽略所有异常, 但只会忽略未检查的异常。如果存在相应的catch块, 那么JVM将不会忽略, 并且将执行相应的catch块。
System.gc()只是对JVM的请求, 以执行垃圾回收器。由JVM决定是否调用Garbage Collector。通常, 当Heap区域中的可用空间不足或内存不足时, JVM会调用Garbage Collector。
如果发现任何不正确的地方, 或者想分享有关上述主题的更多信息, 请写评论。

    推荐阅读