Post Top Ad

Your Ad Spot

viernes, 5 de julio de 2019

Métodos del objeto de Java: finalize()

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 de objetos 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.
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 de Java (JVM). Tradicionalmente, el método ha sido reemplazado por las subclases de Object cuando la instancia de la clase necesita cerrar o purgar los recursos del sistema, como las conexiones de base de datos y los controladores de archivos. Sin embargo, los expertos en lenguaje Java han estado argumentando durante mucho tiempo que la anulación finalize()para realizar operaciones como la destrucción de recursos no es una buena idea.
De hecho, los documentos oficiales de Oracle Java afirman que el finalize()método en sí ha quedado en desuso, por lo que se etiquetó 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 sin implementar.
Además, quiero dejar claro que el objetivo principal de este artículo es proporcionar la guía para migrar el código existente que se implementa finalize()en el constructo preferido de implementar la AutoClosableinterfaz junto con el constructo emparejado try-with-resource introducido en Java 7.

Ejemplo de Implementación Finalizar

Este es un ejemplo que puede ver en algún código heredado donde finalize ha sido anulado para proporcionar la funcionalidad de limpieza de un recurso de base de datos al cerrar una conexión a una base de datos SQLite dentro de una clase llamada PersonDAOTenga en cuenta que este ejemplo utiliza SQLite, que requiere un controlador de conector de conectividad de base de datos Java (JDBC) de terceros, que deberá descargarse desde aquí y agregarse a la ruta de clase si desea seguirlo.
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, se debe desalentar. En su lugar, se debe implementar un código similar al del PersonDAO#finalizemétodo dentro del AutoClosable#closemétodo como se muestra a continuación en el ejemplo que sigue.

Una mejor solución: Intente con recursos y AutoCloseable

Java 7 introdujo la AutoCloseableinterfaz junto con una mejora de la construcción tradicional de prueba / captura que proporciona una solución superior para limpiar los recursos que se encuentran en un objeto. Al usar esta combinación de AutoClosablee intentar con los recursos, el programador tiene un mayor control sobre cómo y cuándo se liberará un recurso, lo que a menudo era impredecible al invalidar el Object#finalize()método.
El ejemplo que sigue toma lo anterior PersonDAOe implementa la AutoCloseable#closeinterfaz para cerrar la conexión de la base de datos. El método principal luego 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 diseño de probar con recursos con un poco más de detalle. Esencialmente, el bloque try-with-resource se traduce en un bloque try / catch / finally completo como se muestra a continuación, pero, con la ventaja de ser más limpio, cada vez que use una clase de implementación AutoCloseable#close, usará un try-with-resource en lugar de reimplementando el bloque finally en un try / catch / finally como se muestra a continuación. Tenga en cuenta que la java.sql.Connectionclase implementa 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 como tal:
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 al hecho de que no se sugiere que uno deba implementarlo. Para contrastar la falta de profundidad empleada en el finalize()método, describí un enfoque preferible para resolver el problema de la limpieza de recursos utilizando el AutoClosabledúo de "try-with-resources".
Como siempre, gracias por leer y no tenga miedo de comentar o criticar a continuación.

No hay comentarios.:

Publicar un comentario

Dejanos tu comentario para seguir mejorando!

outbrain

Páginas