Java POO explicado con ejemplos del mundo real (sin teoría aburrida)

Aprende Programación Orientada a Objetos en Java usando ejemplos que realmente entiendes: coches, personas, cuentas bancarias. Nada de teoría abstracta.

¿Te han explicado POO con ejemplos de "animales" y "figuras geométricas" que no entiendes cómo aplicar? ¿Sientes que la Programación Orientada a Objetos es solo teoría sin sentido práctico?

Este tutorial es diferente. Vamos a aprender POO en Java creando cosas del mundo real que usas todos los días: cuentas bancarias, coches, sistemas de empleados. Código que funciona, ejemplos que entiendes.

Al final tendrás tu propia mini-aplicación bancaria funcionando. Sin teoría aburrida, solo práctica que te sirve.

🤔 ¿Qué vamos a aprender?

🎯 Al terminar este tutorial sabrás:

Qué es realmente POO (con ejemplos que entiendes)
Crear clases y objetos para situaciones reales
Usar herencia para evitar repetir código
Aplicar encapsulación para proteger datos
Implementar polimorfismo de forma práctica
Crear un sistema bancario funcional

📦 ¿Qué es POO realmente?

Imagínate que tienes que describir un coche a alguien que nunca ha visto uno:

🚗 Propiedades: Color, marca, modelo, velocidad
Acciones: Acelerar, frenar, girar, encender
🔒 Privado: Motor interno, sistema de frenos
👥 Tipos: Deportivo, familiar, eléctrico

Eso es POO: Organizar el código como objetos del mundo real, con características (propiedades) y comportamientos (métodos).

🚗 Tu primera clase: Coche

Vamos a crear una clase Coche. Abre tu IDE (Eclipse, IntelliJ, VS Code) y crea un archivo Coche.java:

public class Coche {
    // ✅ PROPIEDADES (lo que tiene un coche)
    private String marca;
    private String modelo;
    private String color;
    private int velocidad;
    private boolean encendido;

    // ✅ CONSTRUCTOR (cómo crear un coche)
    public Coche(String marca, String modelo, String color) {
        this.marca = marca;
        this.modelo = modelo;
        this.color = color;
        this.velocidad = 0;
        this.encendido = false;
    }

    // ✅ MÉTODOS (lo que puede hacer un coche)
    public void encender() {
        this.encendido = true;
        System.out.println("🚗 El " + marca + " " + modelo + " está encendido");
    }

    public void acelerar(int incremento) {
        if (!encendido) {
            System.out.println("❌ Primero debes encender el coche");
            return;
        }
        
        velocidad += incremento;
        System.out.println("🏎️ Velocidad actual: " + velocidad + " km/h");
    }

    public void frenar(int decremento) {
        velocidad -= decremento;
        if (velocidad < 0) velocidad = 0;
        System.out.println("🛑 Frenando... Velocidad: " + velocidad + " km/h");
    }

    // ✅ GETTERS (para ver las propiedades desde fuera)
    public String getInfo() {
        return marca + " " + modelo + " " + color + " - " + velocidad + " km/h";
    }
}

💡 ¿Por qué "private"?

Encapsulación: Protegemos los datos internos del coche. No puedes cambiar directamente la velocidad desde fuera - tienes que usar acelerar() o frenar(), como en un coche real.

🏃‍♂️ Creando objetos (coches reales)

Ahora vamos a crear coches específicos. Crea Main.java:

public class Main {
    public static void main(String[] args) {
        // ✅ Crear coches específicos
        Coche miCoche = new Coche("Toyota", "Corolla", "Rojo");
        Coche cocheAmigo = new Coche("BMW", "X5", "Negro");

        // ✅ Usar los coches
        System.out.println("=== MI COCHE ===");
        miCoche.encender();
        miCoche.acelerar(50);
        miCoche.acelerar(30);
        miCoche.frenar(20);
        System.out.println(miCoche.getInfo());

        System.out.println("\n=== COCHE DEL AMIGO ===");
        cocheAmigo.acelerar(60); // ❌ Sin encender primero
        cocheAmigo.encender();
        cocheAmigo.acelerar(60);
        System.out.println(cocheAmigo.getInfo());
    }
}

Ejecuta el código (botón Run o F5). Deberías ver:

=== MI COCHE ===
🚗 El Toyota Corolla está encendido
🏎️ Velocidad actual: 50 km/h
🏎️ Velocidad actual: 80 km/h
🛑 Frenando... Velocidad: 60 km/h
Toyota Corolla Rojo - 60 km/h

=== COCHE DEL AMIGO ===
❌ Primero debes encender el coche
🚗 El BMW X5 está encendido
🏎️ Velocidad actual: 60 km/h
BMW X5 Negro - 60 km/h

🏦 Ejemplo real: Sistema bancario

Vamos con algo más útil: una cuenta bancaria. Crea CuentaBancaria.java:

public class CuentaBancaria {
    // ✅ Propiedades privadas (nadie puede tocar tu saldo directamente)
    private String numeroCuenta;
    private String titular;
    private double saldo;
    private boolean activa;

    // ✅ Constructor
    public CuentaBancaria(String numeroCuenta, String titular) {
        this.numeroCuenta = numeroCuenta;
        this.titular = titular;
        this.saldo = 0.0;
        this.activa = true;
        System.out.println("✅ Cuenta creada para: " + titular);
    }

    // ✅ Depositar dinero
    public void depositar(double cantidad) {
        if (!activa) {
            System.out.println("❌ Cuenta inactiva");
            return;
        }

        if (cantidad <= 0) {
            System.out.println("❌ La cantidad debe ser positiva");
            return;
        }

        saldo += cantidad;
        System.out.println("💰 Depósito exitoso: €" + cantidad + ". Nuevo saldo: €" + saldo);
    }

    // ✅ Retirar dinero
    public boolean retirar(double cantidad) {
        if (!activa) {
            System.out.println("❌ Cuenta inactiva");
            return false;
        }

        if (cantidad <= 0) {
            System.out.println("❌ La cantidad debe ser positiva");
            return false;
        }

        if (cantidad > saldo) {
            System.out.println("❌ Fondos insuficientes. Saldo actual: €" + saldo);
            return false;
        }

        saldo -= cantidad;
        System.out.println("💳 Retiro exitoso: €" + cantidad + ". Nuevo saldo: €" + saldo);
        return true;
    }

    // ✅ Transferir a otra cuenta
    public void transferir(CuentaBancaria destino, double cantidad) {
        System.out.println("🔄 Iniciando transferencia de €" + cantidad + " a " + destino.titular);

        if (this.retirar(cantidad)) {
            destino.depositar(cantidad);
            System.out.println("✅ Transferencia completada");
        } else {
            System.out.println("❌ Transferencia fallida");
        }
    }

    // ✅ Getters
    public double getSaldo() { return saldo; }
    public String getTitular() { return titular; }
    public String getNumeroCuenta() { return numeroCuenta; }

    public void mostrarInfo() {
        System.out.println("=== CUENTA " + numeroCuenta + " ===");
        System.out.println("👤 Titular: " + titular);
        System.out.println("💰 Saldo: €" + saldo);
        System.out.println("🔄 Estado: " + (activa ? "Activa" : "Inactiva"));
    }
}

🎮 Probando el sistema bancario

Actualiza tu Main.java:

public class Main {
    public static void main(String[] args) {
        System.out.println("🏦 === SISTEMA BANCARIO === 🏦\n");

        // ✅ Crear cuentas
        CuentaBancaria cuentaAna = new CuentaBancaria("12345", "Ana García");
        CuentaBancaria cuentaLuis = new CuentaBancaria("67890", "Luis Martínez");

        System.out.println("\n--- Ana deposita dinero ---");
        cuentaAna.depositar(1000);
        cuentaAna.depositar(500);

        System.out.println("\n--- Luis también deposita ---");
        cuentaLuis.depositar(800);

        System.out.println("\n--- Ana intenta retirar ---");
        cuentaAna.retirar(200);
        cuentaAna.retirar(2000); // ❌ No tiene suficiente

        System.out.println("\n--- Transferencia entre cuentas ---");
        cuentaAna.transferir(cuentaLuis, 300);

        System.out.println("\n--- Estado final de las cuentas ---");
        cuentaAna.mostrarInfo();
        System.out.println();
        cuentaLuis.mostrarInfo();
    }
}

🧬 Herencia: Tipos de cuentas especializadas

Las cuentas de ahorro tienen intereses, las cuentas corrientes tienen sobregiro. Herencia nos permite crear tipos especializados sin repetir código.

Crea CuentaAhorro.java:

public class CuentaAhorro extends CuentaBancaria {
    // ✅ Nueva propiedad específica para ahorro
    private double tasaInteres;

    // ✅ Constructor que llama al padre
    public CuentaAhorro(String numeroCuenta, String titular, double tasaInteres) {
        super(numeroCuenta, titular); // Llama al constructor padre
        this.tasaInteres = tasaInteres;
        System.out.println("💰 Cuenta de ahorro creada con " + (tasaInteres*100) + "% interés anual");
    }

    // ✅ Método nuevo: calcular intereses
    public void aplicarInteres() {
        double interes = getSaldo() * tasaInteres;
        depositar(interes);
        System.out.println("📈 Interés aplicado: €" + interes);
    }

    // ✅ Sobreescribir método: restricción de retiros
    @Override
    public boolean retirar(double cantidad) {
        if (cantidad > 500) {
            System.out.println("⚠️  Las cuentas de ahorro no permiten retiros mayores a €500 por transacción");
            return false;
        }
        return super.retirar(cantidad); // Llama al método padre
    }

    public double getTasaInteres() { return tasaInteres; }
}

🏃‍♀️ Polimorfismo en acción

El polimorfismo permite que diferentes tipos de objetos respondan al mismo método de forma diferente. Actualiza Main.java:

public class Main {
    public static void main(String[] args) {
        System.out.println("🏦 === SISTEMA BANCARIO AVANZADO === 🏦\n");

        // ✅ Polimorfismo: diferentes tipos, misma referencia
        CuentaBancaria cuenta1 = new CuentaBancaria("001", "Pedro");
        CuentaBancaria cuenta2 = new CuentaAhorro("002", "María", 0.03); // 3%

        // ✅ Array de cuentas diferentes
        CuentaBancaria[] cuentas = {cuenta1, cuenta2};

        // ✅ Depositar en todas las cuentas
        for (CuentaBancaria cuenta : cuentas) {
            cuenta.depositar(1000);
        }

        System.out.println("\n--- Probando comportamiento diferente ---");
        
        // ✅ Misma acción, comportamiento diferente (POLIMORFISMO)
        cuenta1.retirar(600); // ✅ Cuenta normal: OK
        cuenta2.retirar(600); // ❌ Cuenta ahorro: Límite

        System.out.println("\n--- Aplicando intereses (solo ahorro) ---");
        
        // ✅ Verificar tipo específico para método específico
        if (cuenta2 instanceof CuentaAhorro) {
            CuentaAhorro cuentaAhorro = (CuentaAhorro) cuenta2;
            cuentaAhorro.aplicarInteres();
        }

        System.out.println("\n--- Estado final ---");
        for (CuentaBancaria cuenta : cuentas) {
            cuenta.mostrarInfo();
            System.out.println();
        }
    }
}

🎯 Los 4 pilares de POO explicados

🏠 Encapsulación: Private = datos protegidos, solo acceso por métodos
🧬 Herencia: CuentaAhorro extends CuentaBancaria = reutilizar código
🎭 Polimorfismo: Mismo método, comportamiento diferente según tipo
📦 Abstracción: Usar objetos sin conocer implementación interna

🛠️ Proyecto final: Banco completo

Vamos a crear un sistema que gestione múltiples cuentas. Crea Banco.java:

import java.util.ArrayList;
import java.util.Scanner;

public class Banco {
    private String nombre;
    private ArrayList<CuentaBancaria> cuentas;
    private Scanner scanner;

    public Banco(String nombre) {
        this.nombre = nombre;
        this.cuentas = new ArrayList<>();
        this.scanner = new Scanner(System.in);
    }

    public void crearCuenta() {
        System.out.println("\n=== CREAR NUEVA CUENTA ===");
        
        System.out.print("Nombre del titular: ");
        String titular = scanner.nextLine();
        
        System.out.print("Número de cuenta: ");
        String numeroCuenta = scanner.nextLine();
        
        System.out.println("Tipo de cuenta:");
        System.out.println("1. Cuenta Normal");
        System.out.println("2. Cuenta de Ahorro");
        System.out.print("Selecciona (1-2): ");
        
        int tipo = scanner.nextInt();
        scanner.nextLine(); // Limpiar buffer
        
        CuentaBancaria nuevaCuenta;
        
        if (tipo == 2) {
            System.out.print("Tasa de interés (ej: 0.03 para 3%): ");
            double tasa = scanner.nextDouble();
            scanner.nextLine();
            nuevaCuenta = new CuentaAhorro(numeroCuenta, titular, tasa);
        } else {
            nuevaCuenta = new CuentaBancaria(numeroCuenta, titular);
        }
        
        cuentas.add(nuevaCuenta);
        System.out.println("✅ Cuenta creada exitosamente!");
    }

    public void mostrarCuentas() {
        if (cuentas.isEmpty()) {
            System.out.println("❌ No hay cuentas registradas");
            return;
        }
        
        System.out.println("\n=== TODAS LAS CUENTAS ===");
        for (int i = 0; i < cuentas.size(); i++) {
            System.out.println((i+1) + ". ");
            cuentas.get(i).mostrarInfo();
            System.out.println();
        }
    }

    public void mostrarMenu() {
        System.out.println("\n🏦 === " + nombre + " ===");
        System.out.println("1. Crear cuenta");
        System.out.println("2. Ver todas las cuentas");
        System.out.println("3. Salir");
        System.out.print("Selecciona una opción: ");
    }

    public void ejecutar() {
        while (true) {
            mostrarMenu();
            int opcion = scanner.nextInt();
            scanner.nextLine();

            switch (opcion) {
                case 1:
                    crearCuenta();
                    break;
                case 2:
                    mostrarCuentas();
                    break;
                case 3:
                    System.out.println("👋 ¡Gracias por usar " + nombre + "!");
                    return;
                default:
                    System.out.println("❌ Opción inválida");
            }
        }
    }

    public static void main(String[] args) {
        Banco miBanco = new Banco("StudyCode Bank");
        miBanco.ejecutar();
    }
}

🎉 ¡Ya dominas POO en Java!

🏆 Lo que has aprendido:

  • Clases y objetos: Crear plantillas y usar instancias específicas
  • Encapsulación: Proteger datos con private y métodos públicos
  • Herencia: Reutilizar código con extends
  • Polimorfismo: Diferentes comportamientos según el tipo
  • Aplicación real: Sistema bancario funcional

🚀 Próximos pasos

🗄️ Conectar con bases de datos (JDBC)
🌐 Crear APIs REST con Spring Boot
📱 Interfaces gráficas con JavaFX

💡 Consejos para seguir practicando:

  • Crea más clases: Estudiante, Producto, Empleado
  • Practica herencia: Vehículo → Coche, Moto, Camión
  • Experimenta: Modifica el código, rompe cosas, arregla cosas
  • Proyectos reales: Sistema de biblioteca, inventario, etc.

🎯 ¿Listo para crear aplicaciones completas?

Ya dominas POO. Es hora de crear aplicaciones web profesionales con bases de datos y APIs.

👉 Java Spring Boot: Tu primera API REST desde cero

Aprende a crear APIs profesionales con Spring Boot, conectar bases de datos y desplegar en producción