Programación Orientada a Objetos en Java: Conceptos Clave

Programación Orientada a Objetos en Java: Conceptos Clave

Cuando empecé a aprender Java, confieso que me abrumaban todos esos términos nuevos: clases, objetos, herencia, polimorfismo, encapsulamiento, abstracción… ¡era como aprender un idioma desde cero! Si te pasa algo parecido, tranquilo, a todos nos pasa. Lo importante es irlos entendiendo poco a poco y ver cómo todo cobra sentido con la práctica. Aquí te dejo una guía sencilla y cercana para que esos conceptos de la POO en Java te resulten más claros (y útiles).


Clases y Objetos

Clases

Una clase es como un plano, el molde para crear objetos. Imagina que tienes la receta de tu comida favorita: los ingredientes y los pasos son la clase, ¡y el platillo listo es el objeto!
Si eres más visual, piensa en el plano de un vehículo. Esa clase tendrá atributos como marca, modelo y color, y métodos como encender, frenar y acelerar.

💡 Info: En programación, usamos objetos para representar cosas reales. Con las clases, definimos las características y comportamientos de lo que queremos crear.
public class Vehiculo {
  // Atributos de la clase
  String marca;
  String modelo;
  String color;

  // Métodos de la clase
  void encendido() {
    System.out.println("El vehiculo está encendido.");
  }

  void frenar() {
    System.out.println("El vehiculo está frenando.");
  }

  void acelerar() {
    System.out.println("El vehiculo está acelerando.");
  }
}

Objetos

El objeto es la “instancia” de la clase, es decir, lo que realmente usas en el programa. Si la clase es el plano, el objeto es el vehículo que construyes a partir de ese plano.

public class Main {
  public static void main(String[] args) {
    Vehiculo miVehiculo = new Vehiculo();
    miVehiculo.marca = "Toyota";
    miVehiculo.modelo = "Yaris Cross";
    miVehiculo.color = "Rojo";

    // Usar los métodos del objeto
    miVehiculo.encendido();
    miVehiculo.acelerar();
    miVehiculo.frenar();
  }
}

Herencia

Aquí me gusta usar la analogía de los autos “tuneados” (mi hermana es fan): a partir de un vehículo base, puedes agregarle nuevas características sin tener que crear todo desde cero. En Java, la herencia permite crear nuevas clases que reutilizan y amplían el comportamiento de otras. Así evitas duplicar código y haces tu vida más sencilla.

⚠️ Advertencia: No repitas código (principio DRY: Don't Repeat Yourself). Usa herencia cuando varias clases comparten comportamientos.
// Clase base o superclase
public class Animal {
  void comer() {
    System.out.println("El animal está comiendo.");
  }
}

// Clase derivada o subclase
public class Perro extends Animal {
  void ladrar() {
    System.out.println("El perro está ladrando. Guau!");
  }
}

public class Main {
  public static void main(String[] args) {
    Perro miPerro = new Perro();
    miPerro.comer();   // Heredado de Animal
    miPerro.ladrar();  // Propio de Perro
  }
}

Polimorfismo

El polimorfismo suena complicado, pero en realidad es simplemente la capacidad de tratar objetos de diferentes clases de la misma manera, usando una clase base común.

Sobrecarga

Permite definir varios métodos con el mismo nombre, pero diferentes parámetros. Así puedes “personalizar” una acción según lo que necesites.

public class Vehiculo {
  public void mover(String direccion) {
    System.out.println("El vehículo se mueve hacia " + direccion);
  }

  public void mover(int velocidad) {
    System.out.println("El vehículo se mueve a " + velocidad + " km/h");
  }

  public void mover(int x, int y) {
    System.out.println("El vehículo se mueve en las coordenadas (" + x + ", " + y + ")");
  }
}

Anulación (Override)

Permite que una subclase cambie el comportamiento de un método heredado de la superclase.

// Clase base
public class Vehiculo {
  public void mover() {
    System.out.println("El vehículo se está moviendo");
  }
}

// Subclase Auto
public class Auto extends Vehiculo {
  @Override
  public void mover() {
    System.out.println("El auto está conduciendo en la carretera");
  }
}

// Subclase Bicicleta
public class Bicicleta extends Vehiculo {
  @Override
  public void mover() {
    System.out.println("La bicicleta está pedaleando");
  }
}
💡 Info: Cuando sobrescribes un método, la subclase puede aportar su propio comportamiento. ¡Eso es polimorfismo de verdad!

Encapsulamiento

Piensa en el encapsulamiento como los controles de tu vehículo: puedes acelerar, frenar, o ver el nivel de combustible, pero no puedes modificar directamente cómo funciona el motor interno. En Java, esto se logra usando atributos privados y métodos públicos para accederlos.

public class Vehiculo {
  private int velocidad;
  private int nivelCombustible;

  public Vehiculo(int velocidadInicial, int nivelCombustibleInicial) {
    this.velocidad = velocidadInicial;
    this.nivelCombustible = nivelCombustibleInicial;
  }

  public int getVelocidad() {
    return velocidad;
  }

  public void setVelocidad(int nuevaVelocidad) {
    if (nuevaVelocidad >= 0) {
      this.velocidad = nuevaVelocidad;
    } else {
      System.out.println("La velocidad no puede ser negativa");
    }
  }

  public int getNivelCombustible() {
    return nivelCombustible;
  }

  public void setNivelCombustible(int nuevoNivelCombustible) {
    if (nuevoNivelCombustible >= 0 && nuevoNivelCombustible <= 100) {
      this.nivelCombustible = nuevoNivelCombustible;
    } else {
      System.out.println("El nivel de combustible debe estar entre 0 y 100");
    }
  }

  public void mostrarEstado() {
    System.out.println("Velocidad: " + velocidad + " km/h");
    System.out.println("Nivel de combustible: " + nivelCombustible + "%");
  }
}
🚫 Peligro: ¡Evita exponer directamente los atributos! Usa métodos públicos para mantener el control y proteger tus datos.

Abstracción

La abstracción es como mirar el tablero del carro: ves solo lo esencial (velocidad, combustible), pero no te preocupas por el detalle del motor. En Java, esto se logra usando clases abstractas e interfaces.

Clases Abstractas

Una clase abstracta es un modelo genérico, no puedes crear objetos de ella, pero sí de sus subclases. Sirve para definir lo que “debe” hacer una familia de clases, pero no cómo lo hacen.

// Clase abstracta Vehiculo
public abstract class Vehiculo {
  public abstract void moverse();

  public void mostrarEstado() {
    System.out.println("El vehículo está listo para moverse");
  }
}

public class Auto extends Vehiculo {
  @Override
  public void moverse() {
    System.out.println("El auto está conduciendo en la carretera");
  }
}

public class Bicicleta extends Vehiculo {
  @Override
  public void moverse() {
    System.out.println("La bicicleta está pedaleando cuesta arriba");
  }
}

Interfaces

Una interfaz define “qué” debe hacer una clase, pero no “cómo”. Es como un contrato: si lo implementas, debes cumplir con todos los métodos definidos.

public interface Vehiculo {
  void moverse();

  void detenerse();
}

public class Auto implements Vehiculo {
  @Override
  public void moverse() {
    System.out.println("El auto está conduciendo en la carretera");
  }

  @Override
  public void detenerse() {
    System.out.println("El auto se ha detenido");
  }
}

public class Bicicleta implements Vehiculo {
  @Override
  public void moverse() {
    System.out.println("La bicicleta está pedaleando en el carril izquierdo");
  }

  @Override
  public void detenerse() {
    System.out.println("La bicicleta se ha detenido");
  }
}

Conclusión

Todos estos conceptos suenan complejos al principio, pero son las piezas básicas para programar bien en Java (¡y en muchos otros lenguajes!). Con la práctica, verás cómo puedes construir sistemas más robustos, fáciles de mantener y hasta divertidos de crear.