Métodos de objetos de Java: finalize ()

M

 

Introducción

Este artículo es una continuación de una serie de artículos que describen los métodos a menudo olvidados de la clase Object base del lenguaje Java. Los siguientes son los métodos del objeto Java base que están presentes en todos los objetos Java debido a la herencia implícita de Object.

  • Encadenar
  • a clase
  • es igual a
  • código hash
  • clon
  • finalizar (estás aquí)
  • esperar y notificar

El enfoque de este artículo es el Object#finalize() método que se utiliza durante el proceso de recolección de basura internamente por la máquina virtual Java (JVM). Tradicionalmente, el método ha sido anulado por subclases de Object cuando la instancia de clase necesita cerrar o purgar recursos del sistema, como conexiones de base de datos y controladores de archivos. Sin embargo, los expertos en lenguaje Java llevan mucho tiempo argumentando que la finalize() para realizar operaciones como la destrucción de recursos no es una buena idea.

De hecho, el Oracle Java oficial docs declarar que el finalize() El método en sí ha quedado obsoleto, por lo que se etiqueta para su eliminación en futuras versiones del lenguaje, ya que los mecanismos subyacentes para la creación de objetos y la recolección de basura se han reevaluado. Recomiendo encarecidamente seguir los consejos para dejar el finalize() método no implementado.

Además, quiero dejar en claro que el objetivo principal de este artículo es proporcionar una guía para migrar el código existente que implementa finalize() a la construcción preferida de implementar el AutoClosable interfaz junto con la construcción emparejada try-with-resource introducida en Java 7.

Ejemplo de implementación de Finalizar

Aquí hay un ejemplo que puede ver en algún código heredado donde finalize ha sido anulado para proporcionar la funcionalidad de limpiar un recurso de base de datos cerrando una conexión a una base de datos SQLite dentro de una clase llamada PersonDAO. Tenga en cuenta que este ejemplo utiliza SQLite, que requiere un controlador Java Database Connectivity Connector (JDBC) de terceros, que deberá descargarse de Aquí y agregado a la ruta de clases si desea seguir adelante.

import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class MainFinalize {

    public static void main(String[] args) {
        try {
            PersonDAO dao = new PersonDAO();
            Person me = new Person("Adam", "McQuistan", LocalDate.parse("1987-09-23"));
            dao.create(me);
        } catch(SQLException e) {
            e.printStackTrace();
        }
    }
    
    /* PersonDAO implementing finalize() */
    static class PersonDAO {
        private Connection con;
        private final Path SQLITE_FILE = Paths.get(System.getProperty("user.home"), "finalize.sqlite3");
        private final String SQLITE_URL = "jdbc:sqlite:" + SQLITE_FILE.toString();
        
        public PersonDAO() throws SQLException {
            con = DriverManager.getConnection(SQLITE_URL);
            
            String sql = "CREATE TABLE IF NOT EXISTS people ("
                    + "id integer PRIMARY KEY,"
                    + "first_name text,"
                    + "last_name text,"
                    + "dob text);";
            Statement stmt = con.createStatement();
            stmt.execute(sql);
        }
        
        void create(Person person) throws SQLException {
            String sql = "INSERT INTO people (first_name, last_name, dob) VALUES (?, ?, ?)";
            PreparedStatement stmt = con.prepareStatement(sql);
            stmt.setString(1, person.getFirstName());
            stmt.setString(2, person.getLastName());
            DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");
            stmt.setString(3, person.getDob().format(fmt));
            stmt.executeUpdate();
        }
        
        @Override
        public void finalize() {
            try {
                con.close();
            } catch(SQLException e) {
                System.out.println("Uh, oh ... could not close db connection");
            }
        }
    }
    
    /* Simple Person data class */
    static class Person {
        private final String firstName;
        private final String lastName;
        private final LocalDate dob;
        
        Person(String firstName, String lastName, LocalDate dob) {
            this.firstName = firstName;
            this.lastName = lastName;
            this.dob = dob;
        }
        
        String getFirstName() {
            return firstName;
        }

        String getLastName() {
            return lastName;
        }

        LocalDate getDob() {
            return dob;
        }
    }
}

Como mencioné anteriormente, este no es el método preferido para cerrar un recurso y, de hecho, debe desaconsejarse enérgicamente. En su lugar, uno debe implementar un código similar al de la PersonDAO#finalize método dentro del AutoClosable#close método como se muestra a continuación en el ejemplo que sigue.

Una mejor solución: Probar con recursos y autocerrable

Java 7 introdujo el AutoCloseable interfaz junto con una mejora de la construcción tradicional try / catch que proporciona una solución superior para limpiar los recursos que en un objeto. Al usar esta combinación de AutoClosable y probar con recursos, el programador tiene un mayor control sobre cómo y cuándo se liberará un recurso, lo que a menudo era impredecible al anular el Object#finalize() método.

El ejemplo que sigue toma el anterior PersonDAO e implementa el AutoCloseable#close interfaz para cerrar la conexión a la base de datos. Luego, el método principal utiliza la construcción try-with-resources en lugar del try / catch anterior para manejar la limpieza.

public class MainFinalize {

    public static void main(String[] args) {
        try (PersonDAO dao = new PersonDAO()) {
            Person me = new Person("Adam", "McQuistan", LocalDate.parse("1987-09-23"));
            dao.create(me);
        } catch(SQLException e) {
            e.printStackTrace();
        }
    }
    
    /* PersonDAO implementing finalize() */
    static class PersonDAO implements AutoCloseable {
        private Connection con;
        private final Path SQLITE_FILE = Paths.get(System.getProperty("user.home"), "finalize.sqlite3");
        private final String SQLITE_URL = "jdbc:sqlite:" + SQLITE_FILE.toString();
        
        public PersonDAO() throws SQLException {
            con = DriverManager.getConnection(SQLITE_URL);
            
            String sql = "CREATE TABLE IF NOT EXISTS people ("
                    + "id integer PRIMARY KEY,"
                    + "first_name text,"
                    + "last_name text,"
                    + "dob text);";
            Statement stmt = con.createStatement();
            stmt.execute(sql);
        }
        
        void create(Person person) throws SQLException {
            String sql = "INSERT INTO people (first_name, last_name, dob) VALUES (?, ?, ?)";
            PreparedStatement stmt = con.prepareStatement(sql);
            stmt.setString(1, person.getFirstName());
            stmt.setString(2, person.getLastName());
            
            DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");
            stmt.setString(3, person.getDob().format(fmt));
            stmt.executeUpdate();
        }
        
        @Override
        public void close() {
            System.out.println("Closing resource");
            try {
                con.close();
            } catch(SQLException e) {
                System.out.println("Uh, oh ... could not close db connection");
            }
        }
    }
    
    /* Simple Person data class */
    static class Person {
        // everything remains the same here ...
    }
}

Vale la pena explicar el constructo try-with-resources con un poco más de detalle. Esencialmente, el bloque try-with-resource se traduce en un bloque completo try / catch / finalmente como se muestra a continuación, pero con el beneficio de ser más limpio cada vez que usa una clase que implementa AutoCloseable#close utilizaría un try-with-resource en lugar de volver a implementar el bloque finalmente en un try / catch /. finalmente como se muestra a continuación. Tenga en cuenta que el java.sql.Connection implementos de clase AutoCloseable#close.

try {
    Connection con = DriverManager.getConnection(someUrl);
    // other stuff ...
} catch (SQLException e) {
    // logging ... 
} finally {
   try {
        con.close();
   } catch(Exception ex) {
        // logging ...
   }
}

Sería mejor implementado así:

try (Connection con = DriverManager.getConnection(someUrl)) {
    // do stuff with con ...
} catch (SQLException e) {
    // logging ... 
}

Conclusión

En este artículo describí a propósito el Object#finalize() método de manera fugaz debido a que no se sugiere que uno deba implementarlo. Para contrastar la falta de profundidad gastada en el finalize() método He descrito un enfoque preferido para resolver el problema de la limpieza de recursos utilizando el AutoClosable y dúo de prueba con recursos.

Como siempre, gracias por leer y no dude en comentar o criticar a continuación.

About the author

Ramiro de la Vega

Bienvenido a Pharos.sh

Soy Ramiro de la Vega, Estadounidense con raíces Españolas. Empecé a programar hace casi 20 años cuando era muy jovencito.

Espero que en mi web encuentres la inspiración y ayuda que necesitas para adentrarte en el fantástico mundo de la programación y conseguir tus objetivos por difíciles que sean.

Add comment

Sobre mi

Últimos Post

Etiquetas

Esta web utiliza cookies propias para su correcto funcionamiento. Al hacer clic en el botón Aceptar, aceptas el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Más información
Privacidad