La palabra clave final se usa en diferentes contextos. En primer lugar, final es un modificador de no acceso aplicable solo a una variable, un método o una clase. Los siguientes son diferentes contextos donde se usa final.
Vídeo explicativo
Final en variables
Cuando una variable se declara con la palabra clave final, su valor no se puede modificar, esencialmente, una constante. Esto también significa que debe inicializar una variable final. Si la variable final es una referencia, esto significa que la variable no se puede volver a vincular para hacer referencia a otro objeto, pero el estado interno del objeto señalado por esa variable de referencia se puede cambiar, es decir, puede agregar o eliminar elementos de la matriz final o colección definitiva. Es una buena práctica representar las variables finales en mayúsculas, utilizando guiones bajos para separar las palabras.
// Variable declarada como final
final int CONSTANTE_INT = 5;
// Variable final en blanco
final int CONSTANTE_INT;
// Variable final estatica
static final double PI = 3.141592653589793;
// Variable final en blanco estatica
static final double PI;
Final en variables
Debemos inicializar una variable final, de lo contrario, el compilador arrojará un error de tiempo de compilación. Una variable final solo se puede inicializar una vez, ya sea a través de un inicializador o una declaración de asignación. Hay tres formas de inicializar una variable final:
- Puede inicializar una variable final cuando se declara. Este enfoque es el más común. Una variable final se denomina variable final en blanco si no se inicializa durante la declaración. A continuación se muestran las dos formas de inicializar una variable final en blanco.
- Una variable final en blanco se puede inicializar dentro de un bloque de inicializador de instancia o dentro del constructor. Si tiene más de un constructor en su clase, debe inicializarse en todos ellos, de lo contrario, se generará un error en tiempo de compilación.
- Una variable static final en blanco se puede inicializar dentro de un bloque static.
Veamos estas dos formas diferentes de inicializar una variable final:
class Constantes {
// inicializacion directa
final int MAX_CONSTANTE_INT = 5;
// una variable final en blanco
final int CONSTANTE_VACIA;
{
CONSTANTE_VACIA = 25;
}
// otra variable final en blanco
final int MIN_CONSTANTE_INT;
// inicializacion directa
static final double PI = 3.141592653589793;
// una variable estática final en blanco
static final double VARIABLE_ESTATICA;
static {
VARIABLE_ESTATICA = 2.3;
}
// constructor para inicializar MIN_CONSTANTE_INT
// Tenga en cuenta que si hay más de uno
// constructor, debes inicializar MIN_CONSTANTE_INT
// en ellos tambien
public Constantes()
{
MINIMUM = -1;
}
}
Final en métodos
Los métodos marcados con final no se pueden anular. Cuando diseñamos una clase y sentimos que un método no debe ser sobreescrito, podemos hacer que este método sea final. También podemos encontrar muchos métodos con final en las bibliotecas principales de Java.
A veces no necesitamos prohibir una extensión de clase por completo, sino solo evitar la sobreescritura de algunos métodos. Un buen ejemplo de esto es la clase Thread. Es posible extender una clase de Thread y así crear una subclase. Pero su método isAlive() es final y no podrá ser sobreescrito.
Vamos a crear una clase Perro y hacer que su método ladrar() sea final:
public class Perro {
public final void ladrar() {
// ...
}
}
Ahora ampliemos la clase Perro e intentemos sobreescribir su método ladrar():
public class Terrier extends Perro {
@override
public void ladrar() {
}
}
Veremos el error del compilador:
- overrides com.programandoenjava.web.Perro.ladrar
- Cannot override the final method from Perro ladrar() method is final and can’t be overridden
Final en clases
Las clases marcadas con la palabra clave final no se pueden extender. Si observamos el código de las bibliotecas centrales de Java, encontraremos muchas clases finales allí. Un ejemplo es la clase String.
Considere la situación si podríamos extender la clase String, sobreescribir cualquiera de sus métodos y sustituir todas las instancias de String con las instancias de nuestra subclase String específica.
El resultado de las operaciones sobre los objetos String se volvería impredecible. Y dado que la clase String se usa en todas partes, es inaceptable. Es por eso que la clase String está marcada como final.
Cualquier intento de heredar de una clase final provocará un error de compilación. Para demostrar esto, creemos la clase final Gato:
public final class Gato {
private int peso;
}
Y tratemos de extenderlo:
public class Persa extends Gato {
}
Veremos el error del compilador:
The type Persa cannot subclass the final class Gato
Ten en cuenta que la palabra clave final en una declaración de clase no significa que los objetos de esta clase sean inmutables. Podemos cambiar los campos del objeto Gato libremente.