11 Encapsulamiento
El encapsulamiento es uno de los pilares fundamentales de la Programación Orientada a Objetos (POO). Se refiere a la capacidad de ocultar los detalles internos de un objeto y exponer solo lo necesario para su funcionamiento. Esto asegura que los datos del objeto estén protegidos de accesos no autorizados y se gestionen a través de métodos controlados.
11.0.0.1 Objetivos de Aprendizaje
Al finalizar esta lección, los estudiantes podrán:
Comprender el concepto de encapsulamiento y su importancia en POO.
Implementar encapsulamiento en Java utilizando modificadores de acceso.
Diseñar clases con atributos privados y métodos públicos (getters y setters).
Analizar casos prácticos que demuestren la ventaja del encapsulamiento.
11.0.1 Concepto de Encapsulamiento
Definición:
El encapsulamiento consiste en ocultar los atributos de una clase (haciéndolos privados) y proporcionar acceso a ellos mediante métodos públicos específicos llamados getters (obtener) y setters (establecer).
public class Persona {
// Atributos privados (ocultos)
private String nombre;
private int edad;
// Métodos públicos para acceder a los atributos
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public int getEdad() {
return edad;
}
public void setEdad(int edad) {
if (edad > 0) { // Validación básica
this.edad = edad;
} else {
System.out.println("La edad debe ser positiva.");
}
}
}
11.0.2 Modificadores de Acceso
Java ofrece diferentes modificadores de acceso para controlar la visibilidad de atributos y métodos:
11.0.3 Beneficios del Encapsulamiento
Seguridad: Protege los datos de modificaciones no deseadas.
Control: Permite agregar reglas o validaciones al acceso de datos.
Flexibilidad: Facilita cambios internos en la clase sin afectar al resto del programa.
Modularidad: Promueve un diseño más claro y organizado.
11.0.4 Implementación de Encapsulamiento en Java
11.0.4.1 Pasos para implementar encapsulamiento:
Declarar los atributos de la clase como
private
.Crear métodos públicos
get
yset
para acceder y modificar los atributos.Opcionalmente, agregar validaciones o lógica en los métodos
set
.
Ejemplo con validaciones:
public class CuentaBancaria {
private double saldo;
public double getSaldo() {
return saldo;
}
//método depositar
public void depositar(double cantidad) {
if (cantidad > 0) {
+= cantidad;
saldo } else {
System.out.println("La cantidad debe ser positiva.");
}
}
//método retirar
public void retirar(double cantidad) {
if (cantidad > 0 && cantidad <= saldo) {
-= cantidad;
saldo } else {
System.out.println("Operación inválida. Fondos insuficientes o cantidad negativa.");
}
}
}
public class Main {
public static void main(String[] args) {
= new CuentaBancaria();
CuentaBancaria cuenta
// Depósito
.depositar(500);
cuentaSystem.out.println("Saldo después del depósito: " + cuenta.getSaldo());
// Retiro válido
.retirar(200);
cuentaSystem.out.println("Saldo después del retiro: " + cuenta.getSaldo());
// Intento de retiro inválido
.retirar(1000);
cuenta}
}
11.0.5 Aplicaciones Prácticas del Encapsulamiento
11.0.5.1 Caso Práctico 1: Gestión de estudiantes
Desarrolla una clase Estudiante
que almacene el nombre, la matrícula y el promedio del estudiante. Asegúrate de que:
El promedio esté siempre entre 0 y 10.
El número de matrícula no pueda modificarse una vez establecido.
11.0.5.2 Solución:
public class Persona {
private String nombre;
private int edad;
// Constructor público usa los setters
public Persona(String nombre, int edad) {
setNombre(nombre); // Usa el setter para inicializar con validación
setEdad(edad); // Usa el setter para inicializar con validación
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
if (nombre != null && !nombre.isEmpty()) {
this.nombre = nombre;
} else {
throw new IllegalArgumentException("El nombre no puede ser nulo o vacío.");
}
}
public int getEdad() {
return edad;
}
public void setEdad(int edad) {
if (edad > 0) {
this.edad = edad;
} else {
throw new IllegalArgumentException("La edad debe ser positiva.");
}
}
}
public class Main {
public static void main(String[] args) {
= new Persona("Juan Pérez", 25);
Persona persona1 System.out.println("Persona creada: " + persona1.getNombre() + ", " + persona1.getEdad() + " años");
// Modificar los atributos usando los setters
.setNombre("Ana López");
persona1.setEdad(30);
persona1System.out.println("Nombre actualizado: " + persona1.getNombre());
System.out.println("Edad actualizada: " + persona1.getEdad());
}
}
11.0.6 Datos Inmutables
Los datos inmutables son aquellos cuyo estado no puede cambiar después de su creación. En la Programación Orientada a Objetos (POO), las clases inmutables son especialmente útiles porque proporcionan seguridad, simplicidad y consistencia. Al diseñar una clase inmutable, garantizamos que sus atributos no puedan ser modificados una vez que se han inicializado.
Un dato o clase es inmutable si:
Sus atributos no pueden ser modificados después de ser inicializados.
No se proporcionan métodos que permitan cambiar el estado del objeto (como setters).
El acceso a los atributos se realiza de manera controlada, generalmente a través de getters.
11.0.7 Uso de final
con Variables
Cuando declaras una variable como final
, su valor no puede cambiar después de ser inicializado. Esto es muy útil para garantizar que ciertos datos permanezcan constantes durante la ejecución del programa.
public class EjemploFinalVariable {
public static void main(String[] args) {
final int constante = 10; // Declaración de una variable final
System.out.println("Valor de constante: " + constante);
// constante = 20; // Esto genera un error de compilación: no se puede modificar
}
}
11.0.8 Uso de final
con Métodos
Cuando un método es declarado como final
, significa que no puede ser sobrescrito en clases que heredan de la clase donde se define el método.
class Padre {
public final void metodoFinal() {
System.out.println("Este método no puede ser sobrescrito.");
}
}
class Hijo extends Padre {
// public void metodoFinal() {
// Esto generaría un error de compilación porque el método es final.
// }
}
11.0.9 Uso de final
con Clases
Cuando una clase es declarada como final
, significa que no puede ser extendida (heredada) por otras clases.
public final class ClaseFinal {
public void metodo() {
System.out.println("Esta clase no puede ser extendida.");
}
}
// class SubClase extends ClaseFinal {
// Esto generaría un error porque ClaseFinal es final y no se puede heredar.
// }
11.0.10 Uso de final
con Atributos de Clase
Cuando declaras un atributo de clase como final
, garantiza que solo puede ser asignado una vez, ya sea en su declaración o dentro del constructor.
Ejemplo
public class Persona {
private final String nombre; // Atributo final: inmutable
private final int edad; // Atributo final: inmutable
public Persona(String nombre, int edad) {
this.nombre = nombre; // Se asigna en el constructor
this.edad = edad; // Se asigna en el constructor
}
public String getNombre() {
return nombre;
}
public int getEdad() {
return edad;
}
// No hay setters porque no pueden cambiarse los valores de los atributos finales.
}
11.0.11 Ejercicio Práctico
Instrucciones:
Crea una clase
Libro
con los atributos:titulo
(tipoString
).autor
(tipoString
).isbn
(tipoString
).
Asegúrate de que todos los atributos sean inmutables.
Proporciona un constructor para inicializar los valores y métodos getter para acceder a ellos.
En la clase
Main
, crea al menos dos objetosLibro
y muestra sus datos por consola.