Java: Programación Orientada a Objetos

Aprende los pilares de la programación orientada a objetos en Java: clases, encapsulación, herencia, polimorfismo y abstracción para código eficiente.

Aprende Java GRATIS y certifícate

Programación Orientada a Objetos en Java

La programación orientada a objetos (POO) representa un paradigma fundamental que revolucionó el desarrollo de software al permitir modelar problemas del mundo real mediante la creación de entidades que encapsulan datos y comportamientos. Java, desde su concepción, fue diseñado como un lenguaje completamente orientado a objetos, donde prácticamente todo elemento del programa es un objeto o forma parte de una clase.

Este paradigma se basa en cuatro pilares fundamentales: encapsulación, herencia, polimorfismo y abstracción. Estos conceptos no son meramente teóricos, sino herramientas prácticas que permiten crear código más organizado, reutilizable y mantenible.

Clases y objetos: los cimientos de la POO

Una clase actúa como un molde o plantilla que define las características y comportamientos que tendrán los objetos creados a partir de ella. Los objetos, por su parte, son instancias específicas de una clase que existen en memoria durante la ejecución del programa.

public class Vehiculo {
    // Atributos (características)
    private String marca;
    private String modelo;
    private int año;
    
    // Constructor
    public Vehiculo(String marca, String modelo, int año) {
        this.marca = marca;
        this.modelo = modelo;
        this.año = año;
    }
    
    // Métodos (comportamientos)
    public void acelerar() {
        System.out.println("El vehículo está acelerando");
    }
    
    public String obtenerInformacion() {
        return marca + " " + modelo + " (" + año + ")";
    }
}

La instanciación de objetos se realiza mediante el operador new, que reserva memoria y ejecuta el constructor correspondiente:

Vehiculo miCoche = new Vehiculo("Toyota", "Corolla", 2023);
Vehiculo motoMoto = new Vehiculo("Honda", "CBR", 2022);

// Uso de los objetos
miCoche.acelerar();
System.out.println(miCoche.obtenerInformacion());

Encapsulación: protegiendo la integridad de los datos

La encapsulación constituye el mecanismo mediante el cual se ocultan los detalles internos de implementación de una clase, exponiendo únicamente una interfaz controlada para interactuar con los objetos. Este principio se implementa principalmente a través de modificadores de acceso y métodos getter/setter.

Los modificadores de acceso en Java determinan la visibilidad de los miembros de una clase:

  • private: accesible únicamente dentro de la misma clase
  • protected: accesible dentro del mismo paquete y clases hijas
  • public: accesible desde cualquier parte del programa
  • package-private (sin modificador): accesible dentro del mismo paquete
public class CuentaBancaria {
    private double saldo;  // Encapsulado - no accesible directamente
    private String numeroCuenta;
    
    public CuentaBancaria(String numeroCuenta, double saldoInicial) {
        this.numeroCuenta = numeroCuenta;
        this.saldo = saldoInicial;
    }
    
    // Método público para acceder al saldo (getter)
    public double getSaldo() {
        return saldo;
    }
    
    // Método público para modificar el saldo de forma controlada
    public boolean depositar(double cantidad) {
        if (cantidad > 0) {
            saldo += cantidad;
            return true;
        }
        return false;
    }
    
    public boolean retirar(double cantidad) {
        if (cantidad > 0 && cantidad <= saldo) {
            saldo -= cantidad;
            return true;
        }
        return false;
    }
}

Herencia: reutilización y especialización

La herencia permite crear nuevas clases basadas en clases existentes, heredando sus atributos y métodos. La clase base se denomina superclase o clase padre, mientras que la clase derivada se conoce como subclase o clase hija.

public class Animal {
    protected String nombre;
    protected int edad;
    
    public Animal(String nombre, int edad) {
        this.nombre = nombre;
        this.edad = edad;
    }
    
    public void dormir() {
        System.out.println(nombre + " está durmiendo");
    }
    
    public void comer() {
        System.out.println(nombre + " está comiendo");
    }
}

public class Perro extends Animal {
    private String raza;
    
    public Perro(String nombre, int edad, String raza) {
        super(nombre, edad);  // Llamada al constructor de la superclase
        this.raza = raza;
    }
    
    // Método específico de la subclase
    public void ladrar() {
        System.out.println(nombre + " está ladrando");
    }
    
    // Sobrescritura de método
    @Override
    public void comer() {
        System.out.println(nombre + " está comiendo croquetas");
    }
}

La palabra clave super permite acceder a miembros de la superclase, especialmente útil en constructores y cuando se necesita llamar a la versión original de un método sobrescrito.

Polimorfismo: flexibilidad en tiempo de ejecución

El polimorfismo permite que objetos de diferentes clases respondan de manera específica a la misma interfaz o método. En Java, esto se manifiesta principalmente a través de la sobrescritura de métodos y el enlace dinámico.

public class Forma {
    public double calcularArea() {
        return 0;
    }
    
    public void dibujar() {
        System.out.println("Dibujando una forma genérica");
    }
}

public class Circulo extends Forma {
    private double radio;
    
    public Circulo(double radio) {
        this.radio = radio;
    }
    
    @Override
    public double calcularArea() {
        return Math.PI * radio * radio;
    }
    
    @Override
    public void dibujar() {
        System.out.println("Dibujando un círculo");
    }
}

public class Rectangulo extends Forma {
    private double ancho, alto;
    
    public Rectangulo(double ancho, double alto) {
        this.ancho = ancho;
        this.alto = alto;
    }
    
    @Override
    public double calcularArea() {
        return ancho * alto;
    }
    
    @Override
    public void dibujar() {
        System.out.println("Dibujando un rectángulo");
    }
}

El polimorfismo permite tratar objetos de diferentes tipos de manera uniforme:

Forma[] formas = {
    new Circulo(5.0),
    new Rectangulo(4.0, 6.0),
    new Circulo(3.0)
};

for (Forma forma : formas) {
    forma.dibujar();  // Se ejecuta el método específico de cada clase
    System.out.println("Área: " + forma.calcularArea());
}

Abstracción: simplificando la complejidad

La abstracción permite definir contratos o interfaces que las clases deben cumplir, sin especificar la implementación concreta. Java proporciona clases abstractas e interfaces para implementar este concepto.

Las clases abstractas no pueden ser instanciadas directamente y pueden contener métodos abstractos que deben ser implementados por las subclases:

public abstract class Empleado {
    protected String nombre;
    protected double salarioBase;
    
    public Empleado(String nombre, double salarioBase) {
        this.nombre = nombre;
        this.salarioBase = salarioBase;
    }
    
    // Método abstracto - debe ser implementado por subclases
    public abstract double calcularSalario();
    
    // Método concreto - puede ser usado por todas las subclases
    public void mostrarInformacion() {
        System.out.println("Empleado: " + nombre);
        System.out.println("Salario: " + calcularSalario());
    }
}

public class EmpleadoTiempoCompleto extends Empleado {
    public EmpleadoTiempoCompleto(String nombre, double salarioBase) {
        super(nombre, salarioBase);
    }
    
    @Override
    public double calcularSalario() {
        return salarioBase;
    }
}

public class EmpleadoPorHoras extends Empleado {
    private int horasTrabajadas;
    
    public EmpleadoPorHoras(String nombre, double tarifaPorHora, int horasTrabajadas) {
        super(nombre, tarifaPorHora);
        this.horasTrabajadas = horasTrabajadas;
    }
    
    @Override
    public double calcularSalario() {
        return salarioBase * horasTrabajadas;
    }
}

La programación orientada a objetos en Java proporciona un framework conceptual que facilita el diseño de aplicaciones complejas mediante la organización lógica del código en entidades cohesivas y reutilizables.

Empezar curso de Java

Lecciones de este módulo de Java

Lecciones de programación del módulo Programación Orientada a Objetos del curso de Java.

Ejercicios de programación en este módulo de Java

Evalúa tus conocimientos en Programación Orientada a Objetos con ejercicios de programación Programación Orientada a Objetos de tipo Test, Puzzle, Código y Proyecto con VSCode.