美文网首页
final、finally、finalize 的区别

final、finally、finalize 的区别

作者: flynnny | 来源:发表于2021-02-24 00:17 被阅读0次

    1. final

    在 java 中,final 可以用来修饰类,方法和变量(成员变量或局部变量)。下面将对其详细介绍。

    1.1 修饰类

    当用 final 修饰类的时,表明该类不能被其他类所继承。当我们需要让一个类永远不被继承,此时就可以用 final 修饰,但要注意:
    final 类中所有的成员方法都会隐式的定义为 final 方法。

    1.2 修饰方法

    使用 final 方法的原因主要有两个:
    (1) 把方法锁定,以防止继承类对其进行更改。
    (2) 效率,在早期的 java 版本中,会将 final 方法转为内嵌调用。但若方法过于庞大,可能在性能上不会有多大提升。因此在最近版本中,不需要 final 方法进行这些优化了。
    final 方法意味着“最后的、最终的”含义,即此方法不能被重写。

    注意:若父类中 final 方法的访问权限为 private,将导致子类中不能直接继承该方法,因此,此时可以在子类中定义相同方法名的函数,此时不会与重写 final 的矛盾,而是在子类中重新地定义了新方法。

    class A{
     private final void getName(){
     }
    }
    public class B extends A{
     public void getName(){
     }
     public static void main(String[]args){
     System.out.println("OK");
     }
    }
    

    1.3 修饰变量

    final 成员变量表示常量,只能被赋值一次,赋值后其值不再改变。类似于 C++中的 const。
    当 final 修饰一个基本数据类型时,表示该基本数据类型的值一旦在初始化后便不能发生变化;如果final 修饰一个引用类型时,则在对其初始化之后便不能再让其指向其他对象了,但该引用所指向的对象的内容是可以发生变化的。本质上是一回事,因为引用的值是一个地址,final 要求值,即地址的值不发生变化。
    final 修饰一个成员变量(属性),必须要显示初始化。这里有两种初始化方式,一种是在变量声明的时候初始化;第二种方法是在声明变量的时候不赋初值,但是要在这个变量所在的类的所有的构造函数中对这个变量赋初值。
    当函数的参数类型声明为 final 时,说明该参数是只读型的。即你可以读取使用该参数,但是无法改变该参数的值。

    1.png

    *在 java 中,String 被设计成 final 类,那为什么平时使用时,String 的值可以被改变呢?
    字符串常量池是 java 堆内存中一个特殊的存储区域,当我们建立一个 String 对象时,假设常量池不存在该字符串,则创建一个,若存在则直接引用已经存在的字符串。当我们对 String 对象值改变的时候,例如 String a="A"; a="B" 。a 是 String 对象的一个引用(我们这里所说的 String 对象其实是指字符串常量),当 a=“B”执行时,并不是原本 String 对象("A")发生改变,而是创建一个新的对象("B"),令 a引用它。

    2. finally

    finally 作为异常处理的一部分,它只能用在 try/catch 语句中,并且附带一个语句块,表示这段语句最终一定会被执行(不管有没有抛出异常),经常被用在需要释放资源的情况下。(×)(这句话其实存在一定的问题)
    *很多人都认为 finally 语句块一定会执行,但真的是这样么?答案是否定的,例如下面这个例子:

    2.png

    当我们去掉注释的三行语句,执行结果为:

    3.png

    为什么在以上两种情况下都没有执行 finally 语句呢,说明什么问题?
    只有与 finally 对应的 try 语句块得到执行的情况下,finally 语句块才会执行。以上两种情况在执行try 语句块之前已经返回或抛出异常,所以 try 对应的 finally 语句并没有执行。
    但是,在某些情况下,即使 try 语句执行了,finally 语句也不一定执行。例如以下情况:

    4.png

    finally 语句块还是没有执行,为什么呢?因为我们在 try 语句块中执行了 System.exit (0) 语句,终止了 Java 虚拟机的运行。那有人说了,在一般的 Java 应用中基本上是不会调用这个 System.exit(0)方法的。OK !没有问题,我们不调用 System.exit(0) 这个方法,那么 finally 语句块就一定会执行吗?
    再一次让大家失望了,答案还是否定的。当一个线程在执行 try 语句块或者 catch 语句块时被打断(interrupted)或者被终止(killed),与其相对应的 finally 语句块可能不会执行。还有更极端的情况,就是在线程运行 try 语句块或者 catch 语句块时,突然死机或者断电,finally 语句块肯定不会执行了。可能有人认为死机、断电这些理由有些强词夺理,没有关系,我们只是为了说明这个问题。

    易错点
    在 try-catch-finally 语句中执行 return 语句。我们看如下代码:

    5.png

    答案:4,4,4 。 为什么呢?
    首先 finally 语句在改代码中一定会执行,从运行结果来看,每次 return 的结果都是 4(即 finally 语句),仿佛其他 return 语句被屏蔽掉了。
    事实也确实如此,因为 finally 用法特殊,所以会撤销之前的 return 语句,继续执行最后的 finally块中的代码。

    3. finalize

    finalize()是在 java.lang.Object 里定义的,也就是说每一个对象都有这么个方法。这个方法在 gc启动,该对象被回收的时候被调用。其实 gc 可以回收大部分的对象(凡是 new 出来的对象,gc 都能搞定,一般情况下我们又不会用 new 以外的方式去创建对象),所以一般是不需要程序员去实现 finalize 的。特殊情况下,需要程序员实现 finalize,当对象被回收的时候释放一些资源,比如:一个 socket 链接,在对象初始化时创建,整个生命周期内有效,那么就需要实现 finalize,关闭这个链接。

    *使用 finalize 还需要注意一个事,调用 super.finalize();

    一个对象的 finalize()方法只会被调用一次,而且 finalize()被调用不意味着 gc 会立即回收该对象,所以有可能调用 finalize()后,该对象又不需要被回收了,然后到了真正要被回收的时候,因为前面调用过一次,所以不会调用 finalize(),产生问题。 所以,推荐不要使用 finalize()方法,它跟析构函数不一样。

    相关文章

      网友评论

          本文标题:final、finally、finalize 的区别

          本文链接:https://www.haomeiwen.com/subject/jjhjfltx.html