La palabra clave final
Parte de la sección Programación Orientada a Objetos del Journey de Java de Coddy — lección 13 de 87.
La palabra clave final en Java evita la modificación. Se puede aplicar a variables, métodos y clases, cada uno con un efecto diferente.
Cuando se aplica a una variable, final lo convierte en una constante: una vez asignado, su valor no puede cambiar:
public class Circle {
private final double PI = 3.14159;
private final double radius;
public Circle(double radius) {
this.radius = radius; // Se puede asignar una vez en el constructor
}
public void setRadius(double r) {
// this.radius = r; // ¡Error! No se puede reasignar una variable final
}
}Una variable final debe ser inicializada ya sea en la declaración o en el constructor. Después de eso, cualquier intento de reasignarla provoca un error de compilación.
Para los tipos de referencia, final significa que la referencia no puede apuntar a un objeto diferente, pero el contenido del objeto aún puede cambiar:
private final StringBuilder name = new StringBuilder("John");
// name = new StringBuilder("Jane"); // ¡Error! No se puede reasignar
name.append(" Doe"); // ¡OK! El contenido del objeto puede cambiarEl uso de final con campos comunica la intención claramente: les indica a otros desarrolladores que este valor nunca debe cambiar después de la inicialización. Esto es especialmente útil para valores de configuración, IDs o cualquier dato que deba permanecer constante durante la vida útil de un objeto.
Desafío
FácilConstruyamos un sistema de registro de Student que utilice la palabra clave final para proteger los datos que nunca deben cambiar después de que un estudiante sea registrado.
Crearás dos archivos para organizar tu código:
Student.java: Crea una clase Student donde cierta información quede permanentemente bloqueada una vez establecida:- Un campo
finalstudentId(String) - asignado al momento de la creación y que nunca puede cambiar - Un campo
finalenrollmentYear(int) - el año en que el estudiante se inscribió, también permanente - Un campo privado regular
name(String) - puede actualizarse si el estudiante cambia su nombre - Un constructor que reciba los tres valores e inicialice los campos
- Un getter
getStudentId()para el ID del estudiante - Un getter
getEnrollmentYear()para el año de inscripción - Un getter
getName()y un settersetName()para el nombre - Un método
getInfo()que devuelva:"ID: [studentId] | Name: [name] | Enrolled: [enrollmentYear]"
- Un campo
Main.java: Crea un objeto Student, luego actualiza su nombre y muestra su información. Recibirás cuatro entradas: el ID del estudiante, el nombre inicial, el año de inscripción y un nuevo nombre para actualizar. Imprime dos líneas:- La información del estudiante antes del cambio de nombre
- La información del estudiante después del cambio de nombre
Recibirás cuatro entradas en orden: studentId (String), name (String), enrollmentYear (int), y newName (String).
Observa cómo los campos final protegen el ID y el año de inscripción del estudiante; estos son registros permanentes que no deben cambiar, mientras que el nombre puede actualizarse a través de su setter.
Hoja de referencia
La palabra clave final evita la modificación en Java. Se puede aplicar a variables, métodos y clases.
Cuando se aplica a una variable, final la convierte en una constante: una vez asignada, su valor no puede cambiar:
public class Circle {
private final double PI = 3.14159;
private final double radius;
public Circle(double radius) {
this.radius = radius; // Se puede asignar una vez en el constructor
}
public void setRadius(double r) {
// this.radius = r; // ¡Error! No se puede reasignar una variable final
}
}Una variable final debe inicializarse ya sea en la declaración o en el constructor. Después de eso, cualquier intento de reasignarla provoca un error de compilación.
Para los tipos de referencia, final significa que la referencia no puede apuntar a un objeto diferente, pero el contenido del objeto aún puede cambiar:
private final StringBuilder name = new StringBuilder("John");
// name = new StringBuilder("Jane"); // ¡Error! No se puede reasignar
name.append(" Doe"); // ¡OK! El contenido del objeto puede cambiarEl uso de final con campos comunica la intención: indica que un valor nunca debe cambiar después de la inicialización, lo cual es útil para valores de configuración, IDs o datos constantes.
Pruébalo tú mismo
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Leer entradas
String studentId = scanner.nextLine();
String name = scanner.nextLine();
int enrollmentYear = scanner.nextInt();
scanner.nextLine(); // consumir nueva línea
String newName = scanner.nextLine();
// TODO: Crear un objeto Student con los valores iniciales
// TODO: Imprimir la información del estudiante antes del cambio de nombre
// TODO: Actualizar el nombre del estudiante usando el setter
// TODO: Imprimir la información del estudiante después del cambio de nombre
}
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de OOP
Archivos externosIntroducción a la OOPClases vs. ObjetosLa palabra clave thisMétodosCampos (Atributos)Método constructorSobrecarga de constructoresResumen - Calculadora simple4Herencia
Herencia básica (extends)La palabra clave superSobrescritura de métodos (@Override)Encadenamiento de constructoresLa clase ObjectHerencia simple y multinivelPor qué no hay herencia múltiple de clasesResumen - Jerarquía de empleados7Métodos especiales y la clase Object
Método toString()equals() y hashCode()Método clone()compareTo() y ComparableInterfaz ComparatorResumen - Ordenamiento personalizado2Modificadores de acceso y encapsulamiento
Visión general de los niveles de accesoMétodos Getter y SetterOcultamiento de informaciónLa palabra clave finalResumen - Gestor de cuentas bancarias5Polimorfismo
Fundamentos de sobrecarga de métodosSobrescritura de métodos (tiempo de ejecución)Upcasting y DowncastingEl operador instanceofClases y métodos abstractosResumen - Calculadora de figuras8Conceptos avanzados de POO
Composición vs. HerenciaAgregación vs. ComposiciónClases internas, anidadas y anónimasEnums y métodos de EnumRecords (Java 16+)Clases selladas (Java 17+)11Patrones de diseño - Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón BuilderPatrón ObserverPatrón Strategy3Propiedades de clase y miembros estáticos
Variables de instancia vs. estáticasMétodos estáticosBloques estáticosConstantes (static final)Repaso - Contador y utilidad6Interfaces y Clases Abstractas
Introducción a las InterfacesImplementación de InterfacesImplementación de Múltiples InterfacesMétodos Default y Static en InterfacesClases Abstractas vs InterfacesInterfaces FuncionalesResumen - Sistema de Pagos9Genéricos
Introducción a los genéricosClases genéricasMétodos genéricosParámetros de tipo delimitadosComodines (?, extends, super)Resumen - Contenedor genérico12Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Iterator