final, finalmente y finalizar en Java

Esta es una pregunta importante relacionada con el punto de vista de la entrevista.

palabra clave final

final (minúsculas) es una palabra clave reservada en Java. No podemos usarlo como identificador, ya que está reservado. Podemos usar esta palabra clave con variables, métodos y también con clases. La palabra clave final en java tiene un significado diferente dependiendo de si se aplica a una variable, clase o método.

final con variables: el valor de la variable no se puede cambiar una vez inicializada.

Java

class A {
    public static void main(String[] args)
    {
        // Non final variable
        int a = 5;
 
        // final variable
        final int b = 6;
 
        // modifying the non final variable : Allowed
        a++;
 
        // modifying the final variable :
        // Immediately gives Compile Time error.
        b++;
    }
}

Si declaramos cualquier variable como final, no podemos modificar su contenido ya que es final, y si lo modificamos, obtenemos un error de tiempo de compilación.

final con Clase: La clase no se puede subclasificar. Cada vez que declaramos una clase como final, significa que no podemos extender esa clase o que esa clase no se puede extender, o que no podemos hacer una subclase de esa clase. 

Java

final class RR {
    public static void main(String[] args)
    {
        int a = 10;
    }
}
// here gets Compile time error that
// we can't extend RR as it is final.
class KK extends RR {
    // more code here with main method
}

final con Método: El método no puede ser anulado por una subclase. Cada vez que declaramos cualquier método como final, significa que no podemos anular ese método. 

Java

class QQ {
    final void rr() {}
    public static void main(String[] args)
    {
    }
}
 
class MM extends QQ {
 
    // Here we get compile time error
    // since can't extend rr since it is final.
    void rr() {}
}

Nota: si una clase se declara como final por defecto, todos los métodos presentes en esa clase son automáticamente finales, pero las variables no lo son

Java

// Java program to illustrate final keyword
final class G {
 
    // by default it is final.
    void h() {}
 
    // by default it is not final.
    static int j = 30;
 
public static void main(String[] args)
    {
        // See modified contents of variable j.
        j = 36;
        System.out.println(j);
    }
}

Salida :

36

finalmente palabra clave

Así como final es una palabra clave reservada, de la misma manera, finalmente también es una palabra clave reservada en Java, es decir, no podemos usarla como identificador. La palabra clave final se usa en asociación con un bloque try/catch y garantiza que se ejecutará una sección de código, incluso si se lanza una excepción. El bloque final se ejecutará después de los bloques try y catch, pero antes de que el control vuelva a su origen. 

Java

// A Java program to demonstrate finally.
class Geek {
    // A method that throws an exception and has finally.
    // This method will be called inside try-catch.
    static void A()
    {
        try {
            System.out.println("inside A");
            throw new RuntimeException("demo");
        }
        finally
        {
            System.out.println("A's finally");
        }
    }
 
    // This method also calls finally. This method
    // will be called outside try-catch.
    static void B()
    {
        try {
            System.out.println("inside B");
            return;
        }
        finally
        {
            System.out.println("B's finally");
        }
    }
 
    public static void main(String args[])
    {
        try {
            A();
        }
        catch (Exception e) {
            System.out.println("Exception caught");
        }
        B();
    }
}

Producción:

inside A
A's finally
Exception caught
inside B
B's finally

Hay varios casos en los que finalmente se puede utilizar. Se comentan a continuación:

Caso 1: No se producen excepciones en el programa 

Java

// Java program to illustrate finally in
// Case where exceptions do not
// occur in the program
class B {
    public static void main(String[] args)
    {
        int k = 55;
        try {
            System.out.println("In try block");
            int z = k / 55;
        }
 
        catch (ArithmeticException e) {
            System.out.println("In catch block");
            System.out.println("Dividing by zero but caught");
        }
 
        finally
        {
            System.out.println("Executes whether exception occurs or not");
        }
    }
}

Salida :

In try block  
Executes whether exception occurs or not

Aquí la excepción anterior no ocurre, pero aún así, finalmente, el bloque se ejecuta, ya que finalmente está destinado a ejecutarse, ya sea que ocurra una excepción o no. El flujo del programa anterior : primero comienza desde el método principal y luego va al bloque de prueba y en el intento, ya que no ocurre ninguna excepción, por lo que el flujo no va al bloque de captura, por lo tanto, el flujo va directamente del bloque de prueba al bloque final.

Caso 2: se produce una excepción y coincide el bloque catch correspondiente 

Java

// Java program to illustrate finally in
// Case where exceptions occur
// and match in the program
class C {
    public static void main(String[] args)
    {
        int k = 66;
        try {
            System.out.println("In try block");
            int z = k / 0;
            // Carefully see flow doesn't come here
            System.out.println("Flow doesn't came here");
        }
 
        catch (ArithmeticException e) {
            System.out.println("In catch block");
            System.out.println("Dividing by zero but caught");
        }
 
        finally
        {
            System.out.println("Executes whether an exception occurs or not");
        }
    }
}

Salida :

In try block
In catch block                         
Dividing by zero but caught 
Executes whether an exception occurs or not

Aquí, ocurre la excepción anterior, y se encuentra el bloque catch correspondiente, pero aún así, finalmente, el bloque se ejecuta, ya que finalmente está destinado a ejecutarse, ya sea que ocurra una excepción o no, o si se encuentra o no el bloque catch correspondiente. El flujo del programa anterior : Primero, comienza desde el método principal y luego va al bloque de prueba, y en el intento, ocurre una excepción aritmética, y el bloque de captura correspondiente también está disponible, por lo que el flujo va al bloque de captura. Después de que el flujo no vuelva a intentar bloquear, ya que una vez que se produce una excepción en el bloque de prueba, el flujo no vuelve a intentar bloquear. Después de eso, finalmente, ejecutar ya que finalmente está destinado a ejecutarse ya sea que ocurra una excepción o no, o si se encuentra o no un bloque catch correspondiente.

Caso 3: se produce una excepción y no se encuentra/coincide el bloque catch correspondiente 

Java

// Java program to illustrate finally in
// Case where exceptions occur
// and do not match any case in the program
class D {
    public static void main(String[] args)
    {
        int k = 15;
        try {
            System.out.println("In try block");
            int z = k / 0;
        }
 
        catch (NullPointerException e) {
            System.out.println("In catch block");
            System.out.println("Dividing by zero but caught");
        }
 
        finally
        {
            System.out.println("Executes whether an exception occurs or not");
        }
    }
}

Salida :

In try block  
Executes whether an exception occurs or not
Exception in thread "main":java.lang.ArithmeticException:
/ by zero followed by stack trace.

Aquí se produce la excepción anterior y el bloque catch correspondiente no se encuentra/coincide, pero finalmente el bloque se ejecuta, ya que finalmente está destinado a ejecutarse, ya sea que ocurra una excepción o no, o si el bloque catch correspondiente se encuentra/coincide o no. El flujo del programa anterior : primero comienza desde el método principal y luego va al bloque de prueba y al intentar ocurre una excepción aritmética, y el bloque de captura correspondiente no está disponible, por lo que el flujo no va al bloque de captura. Después de que el flujo no vuelva a intentar bloquear, ya que una vez que ocurre una excepción en el bloque de prueba, el flujo no lo hace.vuelve de nuevo para intentar bloquear. Después de eso, finalmente, ejecutar desde que finalmente se ejecuta ya sea que ocurra una excepción o no, o si el bloque catch correspondiente se encuentra/coincide o no.

Caso 4: finalmente el bloque no se ejecuta independientemente de la excepción que ocurra

Java

// Java program to illustrate finally in
 
// case where finally doesnt get executed
class E{
  public static void main(String[] args){
    try{
      System.out.println("In try block");
      System.exit(0);
    }
    catch(ArithmeticException e){
      System.out.println("In catch block");
    }
    finally{
      System.out.println("finally block");
    }
  }
}

Producción:

In try block

Aquí en el programa anterior, finalmente, el bloque no se ejecuta. Solo hay una situación en la que el bloque finalmente no se ejecutará cuando estemos usando el método System.exit(0) . Cuando estamos usando System.exit (0), la JVM se apaga, por lo tanto, en este caso, el bloque finalmente no se ejecutará. Aquí, el número entre paréntesis se conoce como código de estado. En lugar de cero, podemos tomar cualquier valor entero donde cero significa terminación normal y distinto de cero significa terminación anormal. Ya sea cero o distinto de cero, no hay cambio en el resultado y el efecto es el mismo con respecto al programa. 

Aplicación del bloque «finally «: Básicamente, el uso del bloque «finally» es la desasignación de recursos . Esto significa que todos los recursos, como conexiones de red y conexiones de base de datos, que abrimos en el bloque de prueba deben cerrarse para que no perdamos nuestros recursos abiertos. Por lo tanto, esos recursos deben cerrarse en el bloque final. 

Java

// Java program to illustrate
// use of finally block
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
 
class K {
private static final int SIZE = 10;
    public static void main(String[] args)
    {
 
        PrintWriter out = null;
        try {
            System.out.println("Entered try statement");
 
            // PrintWriter, FileWriter
            // are classes in io package
            out = new PrintWriter(new FileWriter("OutFile.txt"));
        }
        catch (IOException e) {
            // Since the FileWriter in
            // try block can throw IOException
        }
 
        // Following finally block cleans up
        // and then closes the PrintWriter.
 
        finally
        {
            if (out != null) {
                System.out.println("Closing PrintWriter");
                out.close();
            } else {
                System.out.println("PrintWriter not open");
            }
        }
    }
}

Salida :

Entered try statement
PrintWriter not open

Nota : el bloque finalmente es una herramienta clave para prevenir fugas de recursos. Al cerrar un archivo o recuperar recursos, coloque el código en un bloque finalmente para asegurarse de que el recurso siempre se recupere.

¿Cómo jdk 1.7 hace que el uso del bloque final sea opcional?

Hasta que jdk 1.6 finalmente bloquee es como un héroe, es decir, se recomienda usarlo para la desasignación de recursos, pero desde jdk 1.7 en adelante finalmente, el bloque ahora es opcional (sin embargo, puede usarlo). Dado que los recursos que abrimos en el bloque de prueba se desasignarán/cerrarán automáticamente cuando el flujo del programa llegue al final del bloque de prueba. Este concepto de desasignación automática de recursos sin usar el bloque final se conoce como declaración de prueba con recursos .

Finalizar método

Es un método que Garbage Collector siempre llama justo antes de la eliminación/destrucción del objeto que es elegible para Garbage Collection, para realizar la actividad de limpieza . La actividad de limpieza significa cerrar los recursos asociados con ese objeto, como Conexión de base de datos, Conexión de red, o podemos decir desasignación de recursos. Recuerde, no es una palabra clave reservada. Una vez que el método finalizado se completa inmediatamente, Garbage Collector destruye ese objeto. El método finalizar está presente en la clase Object y su sintaxis es:

protected void finalize throws Throwable{}

Dado que la clase Object contiene el método de finalización, el método de finalización está disponible para cada clase de Java, ya que Object es la superclase de todas las clases de Java. Dado que está disponible para todas las clases de Java, Garbage Collector puede llamar al método de finalización en cualquier objeto de Java Ahora, el método de finalización que está presente en la clase de Objeto, tiene una implementación vacía, en nuestra clase las actividades de limpieza están ahí, entonces tenemos anular este método para definir nuestras propias actividades de limpieza. Casos relacionados con el método de finalización:

Caso 1: el objeto que es elegible para la recolección de basura, se ejecutará el método de finalización de clase correspondiente de ese objeto 

Java

class Hello {
    public static void main(String[] args)
    {
        String s = new String("RR");
        s = null;
 
        // Requesting JVM to call Garbage Collector method
        System.gc();
        System.out.println("Main Completes");
    }
 
    // Here overriding finalize method
    public void finalize()
    {
        System.out.println("finalize method overridden");
    }
}

Salida :

Main Completes

Nota : Aquí, la salida anterior vino solo como Main Completes y no como un «método de finalización anulado» porque el recolector de basura llama al método de finalización en ese objeto de clase que es elegible para la recolección de basura. Aquí arriba hemos hecho-> s = null y ‘s’ es el objeto de la clase String, por lo que se llamará al método de finalización de la clase String y no a nuestra clase (es decir, clase Hello). Así que modificamos nuestro código como->

Hello s = new Hello();
s = null;

Ahora se llama a nuestra clase, es decir, al método de finalización de la clase Hello. 

Salida :

finalize method overridden
Main Completes

Básicamente, Garbage Collector llama al método finalize en ese objeto de clase que es elegible para la recolección de Garbage. Entonces, si un objeto String es elegible para la recolección de basura, entonces se llamará al método de finalización de la clase String y no al método de finalización de la clase Hello .

Caso 2: podemos llamar al método de finalización explícitamente, luego se ejecutará como una llamada de método normal, pero el objeto no se eliminará/destruirá 

Java

class Bye {
    public static void main(String[] args)
    {
        Bye m = new Bye();
 
        // Calling finalize method Explicitly.
        m.finalize();
        m.finalize();
        m = null;
 
        // Requesting JVM to call Garbage Collector method
        System.gc();
        System.out.println("Main Completes");
    }
 
    // Here overriding finalize method
    public void finalize()
    {
        System.out.println("finalize method overridden");
    }
}

Salida :

finalize method overridden 
//call by programmer but object won't gets destroyed.
finalize method overridden 
//call by programmer but object won't gets destroyed.
Main Completes
finalize method overridden 
//call by Garbage Collector just before destroying the object.

Nota : como finalizar es un método y no una palabra clave reservada, podemos llamar al método finalizar explícitamente , luego se ejecutará como una llamada de método normal, pero el objeto no se eliminará/destruirá.

Caso 3: 

Parte a) Si el programador llama al método de finalización mientras ejecuta el método de finalización, surge alguna excepción no verificada. 

Java

class Hi {
    public static void main(String[] args)
    {
        Hi j = new Hi();
 
        // Calling finalize method Explicitly.
        j.finalize();
 
        j = null;
 
        // Requesting JVM to call Garbage Collector method
        System.gc();
        System.out.println("Main Completes");
    }
 
    // Here overriding finalize method
    public void finalize()
    {
        System.out.println("finalize method overridden");
        System.out.println(10 / 0);
    }
}

Salida :

exception in thread "main" java.lang.ArithmeticException:
/ by zero followed by stack trace.

Entonces, el punto clave es: si el programador llama al método de finalización mientras ejecuta el método de finalización, surge una excepción no verificada, entonces JVM finaliza el programa de manera anormal al generar una excepción. Entonces, en este caso, la finalización del programa es Anormal .

Parte b) Si el recolector de basura llama al método de finalización mientras ejecuta el método de finalización, surge alguna excepción no verificada. 

Java

class RR {
    public static void main(String[] args)
    {
        RR q = new RR();
        q = null;
 
        // Requesting JVM to call Garbage Collector method
        System.gc();
        System.out.println("Main Completes");
    }
 
    // Here overriding finalize method
    public void finalize()
    {
        System.out.println("finalize method overridden");
        System.out.println(10 / 0);
    }
}

Salida :

finalize method overridden
Main Completes

Entonces, el punto clave es que si Garbage Collector llama al método de finalización mientras ejecuta el método de finalización, surge una excepción no verificada, entonces JVM ignora esa excepción y el resto del programa continuará normalmente. Entonces, en este caso, la finalización del programa es Normal y no anormal.

Puntos importantes:

  • No hay garantía sobre el momento en que se llama a finalizar. Se puede llamar en cualquier momento después de que el objeto no se refiera a ninguna parte (se puede recolectar basura).
  • JVM no ignora todas las excepciones al ejecutar el método de finalización, pero solo ignora las excepciones no verificadas . Si el bloque catch correspondiente está allí, JVM no ignorará ningún bloque catch correspondiente y se ejecutará.
  • System.gc() es solo una solicitud a JVM para ejecutar el recolector de basura. Depende de JVM llamar a Garbage Collector o no. Por lo general, JVM llama a Garbage Collector cuando no hay suficiente espacio disponible en el área Heap o cuando la memoria es baja.

Este artículo es una contribución de Rajat Rawat y Manav . Si te gusta GeeksforGeeks y te gustaría contribuir, también puedes escribir un artículo usando write.geeksforgeeks.org o enviar tu artículo por correo a review-team@geeksforgeeks.org . Escriba comentarios si encuentra algo incorrecto o si desea compartir más información sobre el tema tratado anteriormente.

Publicación traducida automáticamente

Artículo escrito por GeeksforGeeks-1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

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