finalize()从Java9起已被弃用,调用时机不确定且不保证执行。多态调用间接影响对象生命周期与finalize()行为:构造时父类调用子类重写方法会导致字段未初始化;GC依据实际类型执行finalize(),引用类型不影响可达性分析。应避免在父类构造器中调用可重写方法,改用try-with-resources管理资源。
先说几个核心判断:finalize() 机制的设计初衷是对象回收前的清理钩子,但在实际运行中表现很不可靠——调用时机不确定、不保证执行,从 Java 9 开始官方已将其弃用。它的真正用途是释放非内存资源(例如文件句柄、JNI 内存),但绝对不能用它替代 try-with-resources 这类现代资源管理方式。
多态调用本身并不会直接参与垃圾回收的决策,但它会间接影响对象的生命周期判断和 finalize() 的执行行为——尤其是在涉及父类引用、子类构造与重写方法时,容易出现看似“矛盾”的日志输出(例如 radius=0 后又变成 1)。本质上,这是构造顺序与多态调用时机共同作用的结果。
长期稳定更新的攒劲资源: >>>点此立即查看<<<
当子类构造器中调用 super() 时,父类构造器会率先执行。如果父类构造器里调用了被子类重写的方法(例如 getString()),JVM 会按运行时类型(即子类)去调用——哪怕此时子类的字段(如 radius)还未初始化。
举例说明:circle 构造中调用 super(a,b) 会触发 point 构造,而 point 构造里又调用了 getString()——实际执行的是 circle.getString(),但此时 radius 尚未赋值,因此输出的是 "radius=0"。
这并不是程序缺陷,而是 Java 多态的底层机制决定的规范行为:子类字段仍处于默认值状态(int 为 0,Object 为 null),但方法已按子类逻辑执行。
当对象进入垃圾回收队列、准备执行 finalize() 时,如果该方法被子类重写,JVM 依然会依据实际类型来调用。因此日志中看到的是子类的 finalize() 和它重写的 getString(),而不是父类的版本。
这里必须强调一点:finalize() 从 Java 9 开始就被标记为 deprecated,不应再作为资源清理的主要手段。GC 并不保证 finalize() 一定会执行,也不保证执行时机,更不保证执行次数——可能只执行一次,也可能完全不执行。
上例中 circle 的 finalize() 输出 radius=1/2,这是因为此时对象已经完全构造完毕,字段值已经有效。
多态往往伴随着向上转型,例如 Point c1 = new Circle(...) 这种写法。但变量声明类型并不影响 GC 的判断——JVM 看的是对象实际内存是否还有强引用指向它,而不是声明成什么类型。
c1 和 c2 都是父类引用,但指向的是子类实例。当被设为 null 之后,只要没有其他强引用存在,对象就满足“不可达”条件。
System.gc() 只是一个建议,告诉 JVM 尽快回收,但并不保证立即执行。finalize() 的调用时机完全由 GC 线程决定,而且可能延迟。即便存在多层继承链,只要整个对象图从 GC Roots 不可达,整块对象内存(包括父类字段和子类字段)就会一并回收。
了解了这些底层机制,就能避免几个常见的理解误区:
finalize() 来释放资源——改用 try-with-resources 或 Cleaner 才是正道Point)与运行时类型(Circle)对方法调用的影响侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述