CAVEAT: Puede que esté desactualizado, pero este es mi entendimiento desde hace unos años:
En general, no hay garantía de cuándo se ejecuta un finalizador, ni siquiera de que se ejecute, aunque algunas JVM le permitirán solicitar un GC completo y una finalización antes de que el programa finalice (lo que, por supuesto, significa que programa tarda más tiempo en salir, y no es el modo de operación predeterminado).
Y se sabía que algunos GC demoran o evitan explícitamente los objetos de GC que tenían finalizadores, con la esperanza de que esto produzca un mejor rendimiento en los puntos de referencia.
Desafortunadamente, estos comportamientos están en conflicto con los motivos originales por los que se recomendaron los finalizadores, y en su lugar se recomienda el uso de métodos de apagado explícitamente llamados.
Si tiene un objeto que realmente debe limpiarse antes de ser descartado, y si realmente no puede confiar en que los usuarios lo hagan, puede valer la pena considerar un finalizador. Pero en general, hay buenas razones por las que no las ves tan a menudo en el código Java moderno como lo hiciste en algunos de los primeros ejemplos.