M茅todos de objetos 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 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.

    Etiquetas:

    Deja una respuesta

    Tu direcci贸n de correo electr贸nico no ser谩 publicada. Los campos obligatorios est谩n marcados con *