Java基础(013):final、finally、finalize的区别和注意点

注:这三者压根就没什么联系,纯粹只是长得像而已 ^_^
  • final 关键字:用于声明属性/变量,方法和类,分别表示属性/变量不可变,方法不可覆盖,类不可继承。按照字面理解就是最终的,不再改变的,不可能被改变的。防止改变有两个原因:设计或者效率[1]。下面针对 final 可能使用的三个地方分别进行说明:数据、方法、类
    • final 数据:也即是变量,包括属性(静态和非静态)、方法入参、局部变量,在声明时给定初值(使用前需要进行初始化),后续不能改变其初值(基本类型)或者指向别的引用(但是引用的对象的内部属性可能变更)。常见的有final常量、方法的final入参、方法或者语句块的final变量,主要是为了初始化之后不再被修改,当作是一个不变原值或者原始引用。
      • 对于编译时就确定的 final 常量,编译期可以将常量带入计算中,减少一些运行时负担,例如 final 基本类型常量,其实编译时就是确定的一个常量数值,如果有参与运算,是可以直接替换的,有点像宏定义。
    • final 参数,意味着在方法中不能改变执行的对象或者基本变量,使得该参数一直保持原始值,只用于读取,主要应用场景如传递数据给匿名内部类。
    • final 方法:只能直接使用,不能重载。
      • 这是前面[1]提到final方法的第一个原因:设计,不希望或者无需子类重写,可以防止方法被子类通过重写而破坏,确保方法的行为不会因为继承而改变,偏离设计初衷。
      • 第二个原因是效率,和虚拟机有关。早期的Java实现,final方法声明告诉JVM可将方法调用转化为内嵌,以便消除方法调用的开销从而提升性能,当然如果遇到方法很大代码膨胀,可能就看不到内嵌带来的性能提升了。
      • 最近的Java版本中,JVM已经可以探测到这些情况并做好相关的优化。这类性能问题应该让编译器和JVM进行处理。我们只需要记住:只有在为了明确禁止重写方法时才使用 final 声明。
    • final 类:不能派生出子类,即不能作为父类被继承,可以防止被继承带来的破坏,因此一个类不能既被声明为 abstract ,又被声明为 final。
      • 之所以声明为 final 类,是因为类的设计就是永远不需要改动,或者出于安全考虑不希望它有子类。[1]
      • 扩展:JDK中哪些类是不能继承的?
        • 用final修饰的类都是不能被继承的。
        • 一般比较基本的类型或防止扩展类无意间破坏原来方法的实现的类型都可以考虑设计成final类,在JDK中,String和StringBuffer等都是比较基本的类型,不考虑扩展(或者没必要扩展),因此设计上,String、StringBuffer等类都是不能继承的 final 类。
  • finally :异常处理语句结构的一部分,表示总是执行。无论try中发生什么(除非JVM关闭),finally都会得到执行。
    • 在异常处理时提供finally块来执行任何清除操作。如果抛出一个异常,那么相匹配的catch子句就会执行,然后控制就会进入finally块,除非JVM被关闭(System.exit(1)等),通常用作释放外部资源(不会被垃圾回收器回收的资源),例如关闭文件流。
    • 当要把除内存之外的资源恢复到它们的初始状态,且无论 try 中是否出现异常都需要执行时,就要用到 finally 子句。这种需要清理的资源包括:已经打开的文件或网络连接,在屏幕上画的图形,甚至可以是外部世界的某个开关。[5]
    • finally 的相关问题[Java基础(014):Java的异常结构和相关注意点]:
      • finally 中抛出异常会覆盖原来的异常
      • finally 和 return 的执行顺序
        • finally 中使用 return 会直接返回而使得即使发生了异常,也会像没有异常输出一样
        • finally 中使用 return 返回值会直接使用该返回值,忽略前面的返回值
      • finally 不会执行的几种场景
        • try/catch 中使用 System.exit(其他类似) 会直接退出,不会再执行 finally 中的语句。
        • finally 中使用 System.exit(其他类似) 会直接退出。
  • finalize:Object 类的 finalize() 方法
    • 当垃圾回收器准备回收对象的内存时,首先会调用其 finalize() 方法,并在下一轮的垃圾回收动作发生时,才会真正回收对象占用的内存。所以如果打算使用 finalize(),就能在垃圾回收时做一些重要的清理工作。
    • Java允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。
    • 它是在Object类中定义的,因此所有的类都继承了它。子类覆盖finalize()方法以整理系统资源或者执行其他清理工作。
    • 注意,无法确切地保证垃圾回收器何时调用该方法,也无法保证调用不同对象finalize()方法的顺序。
    • finalize() 的用途应当是与内存及其回收有关。
    • 参考[2]
参考:
  • [1]《On Java 8》第8章 复用 【final 关键字】
  • [2]《On Java 8》第6章 初始化和清理 【垃圾回收器】

posted @ 2021-03-14 23:23  心明谭  阅读(174)  评论(0编辑  收藏  举报