Introducción a POO

La Programación Orientada a Objetos (POO) es un paradigma de programación que organiza el código en "objetos" que combinan datos y comportamientos relacionados. En Java, todo es un objeto (excepto los tipos primitivos).

Clase

Plantilla o molde que define las propiedades y comportamientos de un objeto. Es como un plano de construcción.

Analogía: La clase "Coche" define que todos los coches tienen marca, modelo y pueden acelerar.

Objeto

Instancia concreta de una clase. Es la "cosa real" creada a partir del molde.

Ejemplo: Un coche específico: "Toyota Corolla 2023" con matrícula ABC123.

Clases y Objetos

Simulador de Clases y Objetos

public class Coche { // Atributos (propiedades) private String marca; private String modelo; private int año; private double velocidad; // Constructor public Coche(String marca, String modelo, int año) { this.marca = marca; this.modelo = modelo; this.año = año; this.velocidad = 0.0; } // Métodos (comportamientos) public void acelerar(double incremento) { velocidad += incremento; } public void frenar(double decremento) { velocidad = Math.max(0, velocidad - decremento); } public void mostrarInfo() { System.out.println("Coche: " + marca + " " + modelo + " (" + año + ") - Velocidad: " + velocidad + " km/h"); } }
Objeto: miCoche
marca "Toyota"
modelo "Corolla"
año 2023
velocidad 0.0

// Crear un objeto (instancia) de la clase Coche
Coche miCoche = new Coche("Toyota", "Corolla", 2023);

// Usar métodos del objeto
miCoche.acelerar(50);
miCoche.mostrarInfo();

Constructores


public class Estudiante {
    private String nombre;
    private int edad;
    private String carrera;
    
    // 1. Constructor por defecto (sin parámetros)
    public Estudiante() {
        this.nombre = "Sin nombre";
        this.edad = 18;
        this.carrera = "No asignada";
    }
    
    // 2. Constructor con parámetros
    public Estudiante(String nombre, int edad, String carrera) {
        this.nombre = nombre;
        this.edad = edad;
        this.carrera = carrera;
    }
    
    // 3. Constructor de copia
    public Estudiante(Estudiante otro) {
        this.nombre = otro.nombre;
        this.edad = otro.edad;
        this.carrera = otro.carrera;
    }
    
    // 4. Constructor con algunos parámetros (sobrecarga)
    public Estudiante(String nombre, int edad) {
        this(nombre, edad, "Ingeniería"); // Llama a otro constructor
    }
}

Demostración Interactiva

Modificadores de Acceso

public

Público

Accesible desde cualquier clase en cualquier paquete

public class MiClase {
    public int valor; // Accesible desde cualquier parte
}
private

Privado

Solo accesible dentro de la misma clase

public class MiClase {
    private int secreto; // Solo esta clase puede acceder
}
protected

Protegido

Accesible en la misma clase, paquete y subclases

public class MiClase {
    protected int familiar; // Clase, paquete y herederas
}
(default)

Por Defecto

Accesible solo en el mismo paquete (sin modificador)

public class MiClase {
    int paquete; // Solo clases en el mismo paquete
}
Modificador Misma Clase Mismo Paquete Subclase Cualquier Lugar
private
(default)
protected
public

Encapsulamiento

Getters y Setters: Protegiendo los Datos

El encapsulamiento oculta los detalles internos de una clase y solo expone una interfaz controlada mediante métodos públicos (getters y setters).


public class CuentaBancaria {
    // Atributos PRIVADOS (encapsulados)
    private String titular;
    private double saldo;
    private String numeroCuenta;
    
    // Constructor
    public CuentaBancaria(String titular, double saldoInicial) {
        this.titular = titular;
        this.saldo = saldoInicial;
        this.numeroCuenta = generarNumeroCuenta();
    }
    
    // GETTERS (métodos para leer)
    public String getTitular() {
        return titular;
    }
    
    public double getSaldo() {
        return saldo;
    }
    
    public String getNumeroCuenta() {
        return numeroCuenta;
    }
    
    // SETTERS con validación (métodos para modificar)
    public void setTitular(String titular) {
        if (titular != null && !titular.trim().isEmpty()) {
            this.titular = titular;
        }
    }
    
    public void depositar(double cantidad) {
        if (cantidad > 0) {
            saldo += cantidad;
        }
    }
    
    public boolean retirar(double cantidad) {
        if (cantidad > 0 && cantidad <= saldo) {
            saldo -= cantidad;
            return true;
        }
        return false;
    }
    
    private String generarNumeroCuenta() {
        // Método privado, solo usado internamente
        return "ES" + (int)(Math.random() * 1000000000);
    }
}
Cuenta Bancaria
titular "Juan Pérez"
saldo 1000.0 €
numeroCuenta "ES123456789"

Herencia

Árbol de Herencia: Sistema de Personas

La herencia permite crear nuevas clases basadas en clases existentes, reutilizando y extendiendo su funcionalidad.

Persona

Clase Base
nombre String
edad int
presentarse()
caminar()

Estudiante

extends Persona
matricula String
promedio double
estudiar()
presentarse()

Profesor

extends Persona
especialidad String
salario double
enseñar()
calificar()

// CLASE BASE
public class Persona {
    protected String nombre;
    protected int edad;
    
    public void presentarse() {
        System.out.println("Hola, soy " + nombre);
    }
}

// HERENCIA: Estudiante ES UNA Persona
public class Estudiante extends Persona {
    private String matricula;
    
    // Sobrescritura de método
    @Override
    public void presentarse() {
        super.presentarse(); // Llama al método de la clase base
        System.out.println("Soy estudiante con matrícula: " + matricula);
    }
    
    public void estudiar() {
        System.out.println(nombre + " está estudiando...");
    }
}

// HERENCIA: Profesor ES UNA Persona
public class Profesor extends Persona {
    private String especialidad;
    
    @Override
    public void presentarse() {
        System.out.println("Soy el profesor " + nombre + 
                         ", especialista en " + especialidad);
    }
    
    public void enseñar() {
        System.out.println(nombre + " está enseñando " + especialidad);
    }
}

Polimorfismo

Un Nombre, Múltiples Formas

El polimorfismo permite que objetos de diferentes clases sean tratados como objetos de una clase común.

Rectángulo

Clase que calcula área y perímetro

Círculo

Clase que calcula área y perímetro

Triángulo

Clase que calcula área y perímetro


// INTERFAZ COMÚN
public interface Figura {
    double calcularArea();
    double calcularPerimetro();
}

// CLASES QUE IMPLEMENTAN LA INTERFAZ
public class Rectangulo implements Figura {
    private double base, altura;
    
    @Override
    public double calcularArea() {
        return base * altura;
    }
    
    @Override
    public double calcularPerimetro() {
        return 2 * (base + altura);
    }
}

public class Circulo implements Figura {
    private double radio;
    
    @Override
    public double calcularArea() {
        return Math.PI * radio * radio;
    }
    
    @Override
    public double calcularPerimetro() {
        return 2 * Math.PI * radio;
    }
}

// USO POLIMÓRFICO
Figura[] figuras = new Figura[3];
figuras[0] = new Rectangulo();
figuras[1] = new Circulo();
figuras[2] = new Triangulo();

// Mismo método, diferentes comportamientos
for (Figura figura : figuras) {
    System.out.println("Área: " + figura.calcularArea());
    System.out.println("Perímetro: " + figura.calcularPerimetro());
}

Clases Abstractas e Interfaces

Comparación: Abstractas vs Interfaces


// EJEMPLO DE CLASE ABSTRACTA
public abstract class Animal {
    // Atributos
    protected String nombre;
    protected int edad;
    
    // Constructor
    public Animal(String nombre, int edad) {
        this.nombre = nombre;
        this.edad = edad;
    }
    
    // Método abstracto (DEBE ser implementado)
    public abstract void hacerSonido();
    
    // Método concreto (YA está implementado)
    public void dormir() {
        System.out.println(nombre + " está durmiendo...");
    }
    
    // Método final (NO puede ser sobrescrito)
    public final void respirar() {
        System.out.println(nombre + " está respirando");
    }
}

// EJEMPLO DE INTERFACE
public interface Volador {
    // Constante (implícitamente public static final)
    int ALTURA_MAXIMA = 10000;
    
    // Método abstracto (implícitamente public abstract)
    void volar();
    
    // Método default (Java 8+)
    default void aterrizar() {
        System.out.println("Aterrizando...");
    }
    
    // Método static (Java 8+)
    static void mostrarInfo() {
        System.out.println("Interface para objetos que vuelan");
    }
    
    // Método private (Java 9+)
    private void logVuelo() {
        System.out.println("Registrando vuelo...");
    }
}

// CLASE QUE USA AMBOS
public class Pajaro extends Animal implements Volador {
    
    public Pajaro(String nombre, int edad) {
        super(nombre, edad);
    }
    
    @Override
    public void hacerSonido() {
        System.out.println("¡Pío pío!");
    }
    
    @Override
    public void volar() {
        System.out.println(nombre + " está volando a gran altura");
    }
}
Característica Clase Abstracta Interface
Atributos ✓ (cualquier modificador) Solo constantes (public static final)
Constructores
Métodos abstractos ✓ (0 o más) ✓ (todos hasta Java 8)
Métodos concretos ✓ (default/static desde Java 8)
Herencia múltiple ✗ (solo una clase) ✓ (múltiples interfaces)
Modificadores Cualquiera Solo public (implícitamente)

Sobrecarga vs Sobrescritura

Juego: Identifica la Diferencia

Arrastra los métodos a la categoría correcta:

sumar(int a, int b)
Mismo nombre, diferente firma
@Override toString()
Mismo nombre y firma en clase hija
calcular(double x, double y)
Diferentes parámetros
@Override equals(Object obj)
Implementación específica
imprimir(String texto, int veces)
Múltiples versiones
@Override hacerSonido()
Comportamiento polimórfico
Sobrecarga (Overloading)

Mismo nombre de método, diferente firma (parámetros)

Sobrescritura (Overriding)

Mismo nombre y firma en clase hija (herencia)

Puntaje: 0/6

// SOBRECARGA (OVERLOADING) - En la MISMA clase
public class Calculadora {
    
    // Versión 1: dos enteros
    public int sumar(int a, int b) {
        return a + b;
    }
    
    // Versión 2: tres enteros
    public int sumar(int a, int b, int c) {
        return a + b + c;
    }
    
    // Versión 3: dos doubles
    public double sumar(double a, double b) {
        return a + b;
    }
    
    // Versión 4: array de enteros
    public int sumar(int[] numeros) {
        int total = 0;
        for (int num : numeros) {
            total += num;
        }
        return total;
    }
}

// SOBRESCRITURA (OVERRIDING) - En clase HIJA
public class Animal {
    public void hacerSonido() {
        System.out.println("El animal hace un sonido");
    }
}

public class Perro extends Animal {
    @Override  // Anotación recomendada
    public void hacerSonido() {
        System.out.println("¡Guau guau!");
    }
}

public class Gato extends Animal {
    @Override
    public void hacerSonido() {
        System.out.println("¡Miau miau!");
    }
}

// USO POLIMÓRFICO
Animal miAnimal = new Perro();
miAnimal.hacerSonido();  // Output: ¡Guau guau!

Ejercicios Prácticos

Sistema de Biblioteca

Crea un sistema para una biblioteca con las siguientes clases:

  1. Libro: Clase base con atributos título, autor, ISBN y estado (disponible/prestado)
  2. LibroDigital: Hereda de Libro, añade formato (PDF, EPUB) y tamaño en MB
  3. Usuario: Clase con nombre, ID y lista de libros prestados
  4. Biblioteca: Clase principal que gestiona libros y usuarios

Requisitos: Encapsulamiento, herencia, polimorfismo y sobreescritura de métodos.

Sistema de Figuras Geométricas

Implementa un sistema de figuras geométricas usando interfaces y clases abstractas:

  1. Interface Figura con métodos calcularArea() y calcularPerimetro()
  2. Clase abstracta FiguraGeometrica que implemente algunos métodos comunes
  3. Clases concretas: Circulo, Rectangulo, Triangulo
  4. Clase CalculadoraAreas que use polimorfismo

Sistema de Empleados

Crea un sistema de nómina para diferentes tipos de empleados:

  1. Clase abstracta Empleado con atributos básicos y método abstracto calcularSalario()
  2. Clases concretas: EmpleadoTiempoCompleto, EmpleadoPorHoras, EmpleadoComision
  3. Interface Bonificable con método aplicarBonificacion()
  4. Sobrecarga de métodos para diferentes tipos de contratación

Juego de Cartas con POO

Implementa un sistema para un juego de cartas usando todos los conceptos de POO:

  1. Clase Carta con valor y palo
  2. Clase abstracta Jugador con estrategias de juego
  3. Interfaces: Jugable, Barajable
  4. Clase Mazo que use composición (lista de Cartas)
  5. Sobrecarga para diferentes reglas del juego
  6. Polimorfismo para diferentes tipos de jugadores

Quiz de Conceptos

Pregunta 1: ¿Qué es una clase en POO?

Un objeto específico con valores concretos
Un plano o molde para crear objetos
Un método que se ejecuta al crear un objeto
Una variable que almacena datos

Resumen y Buenas Prácticas

Conceptos Clave de POO

  • Clase: Molde/plano para crear objetos
  • Objeto: Instancia concreta de una clase
  • Encapsulamiento: Ocultar detalles internos (private + getters/setters)
  • Herencia: Reutilizar y extender funcionalidad (extends)
  • Polimorfismo: Múltiples formas a través de una interfaz común
  • Abstracción: Mostrar solo lo esencial, ocultar complejidad

Buenas Prácticas en POO

  • Usa nombres descriptivos para clases y métodos
  • Sigue el principio de responsabilidad única
  • Prefiere composición sobre herencia cuando sea posible
  • Usa modificadores de acceso apropiados
  • Documenta tus clases y métodos con JavaDoc
  • Usa interfaces para definir contratos claros
  • Evita clases demasiado grandes ("god classes")

Errores Comunes

  • Olvidar el encapsulamiento (atributos públicos)
  • Herencia excesiva (demasiados niveles)
  • No usar @Override en sobrescritura
  • Confundir sobrecarga con sobrescritura
  • Crear clases "todo en uno"
  • No validar datos en setters
  • Usar herencia para compartir código en lugar de para relaciones "es-un"

Hoja de Referencia Rápida - POO

Definir Clase:
public class MiClase {
    private atributo;
    
    public MiClase() {}
    
    public void metodo() {}
}
Herencia:
public class Hijo extends Padre {
    @Override
    public void metodo() {
        super.metodo(); // Llama al padre
    }
}
Interface:
public interface MiInterface {
    void metodo();
}

public class Clase implements MiInterface {
    @Override
    public void metodo() {}
}
Abstracta:
public abstract class Abstracta {
    public abstract void metodo();
    
    public void concreto() {}
}

Test Final de POO

Pregunta 1 de 5

¿Cuál de estos NO es un pilar de la POO?

Encapsulamiento
Herencia
Polimorfismo
Iteración