Codemaker Java

Java Tema 6

Programación orientada a objetos

Hasta ahora, cada dato iba en su variable: un String para el nombre, un int para la edad… Pero si tu programa gestiona los alumnos de una academia, cada alumno tiene nombre, edad y nivel, y con 30 alumnos necesitarías tres arrays que tendrías que mantener a la par a mano.

La programación orientada a objetos (POO) te deja crear tu propio tipo de dato, Alumno, que junta todo lo de un alumno en un solo sitio. Igual que existe String, ahora existirá Alumno.

Clases y objetos#

ConceptoQué esEn el ejemplo
ClaseEl molde: describe cómo es un alumno, pero no es ningún alumno concretoAlumno
ObjetoCada alumno concreto hecho con el molde. Puede haber tantos como quierasAntonio, María, Juan…
AtributoCada dato que tiene un objetonombre, edad, nivel
MétodoLo que sabe hacer un objetopresentarse()

Consejo

Si has visto los apuntes de SQL, esto te sonará: una clase es como una tabla, cada objeto es una fila y cada atributo es una columna. Por eso, cuando un programa lee una base de datos, suele convertir cada fila en un objeto.

Crear una clase#

Cada clase va en su propio fichero, que se llama igual que la clase: la clase Alumno va en Alumno.java, en la misma carpeta que el programa principal.

Java
public class Alumno {
    // Atributos: los datos de cada alumno
    String nombre;
    int edad;
    String nivel;
}

Crear objetos: new#

Desde el programa principal, cada alumno se crea con new, y a sus atributos se llega con un punto:

public class App {
    public static void main(String[] args) {
        Alumno a1 = new Alumno();
        a1.nombre = "Antonio";
        a1.edad = 17;
        a1.nivel = "DEV";

        Alumno a2 = new Alumno();
        a2.nombre = "María";
        a2.edad = 16;
        a2.nivel = "DEV";

        System.out.println(a1.nombre + " tiene " + a1.edad + " años");   // Antonio tiene 17 años
    }
}

a1 y a2 son dos objetos distintos hechos con el mismo molde: cada uno tiene sus propios datos.

El constructor#

Dar los datos uno a uno es largo, y es fácil olvidarse alguno. El constructor es un método especial que se ejecuta al hacer new y rellena los atributos de una vez. Se llama igual que la clase y no lleva tipo de retorno:

Java
public class Alumno {
    String nombre;
    int edad;
    String nivel;

    public Alumno(String nombre, int edad, String nivel) {
        this.nombre = nombre;
        this.edad = edad;
        this.nivel = nivel;
    }
}

this.nombre es el atributo del objeto; nombre a secas es el parámetro que llega. La línea this.nombre = nombre; se lee: «guarda en mi nombre el nombre que me han dado».

Ahora crear un alumno cabe en una línea:

Java
Alumno a1 = new Alumno("Antonio", 17, "DEV");
Alumno a2 = new Alumno("María", 16, "DEV");

Métodos: lo que sabe hacer un objeto#

Los métodos de una clase son funciones que trabajan con los datos de ese objeto. Se escriben dentro de la clase, sin static:

Java
public class Alumno {
    String nombre;
    int edad;
    String nivel;

    public Alumno(String nombre, int edad, String nivel) {
        this.nombre = nombre;
        this.edad = edad;
        this.nivel = nivel;
    }

    public void presentarse() {
        System.out.println("Soy " + nombre + ", tengo " + edad + " años y estoy en " + nivel);
    }

    public boolean esMayorDeEdad() {
        return edad >= 18;
    }
}

Y se llaman con el punto, sobre un objeto concreto:

Java
Alumno a1 = new Alumno("Antonio", 17, "DEV");
a1.presentarse();                         // Soy Antonio, tengo 17 años y estoy en DEV
System.out.println(a1.esMayorDeEdad());   // false

Ahora se entiende el static del tema de funciones:

Con staticSin static
¿De quién es?De la clase enteraDe cada objeto
¿Cómo se llama?Sin crear objetos: doble(5), Math.random()Sobre un objeto: a1.presentarse()
¿Usa los atributos?No puede: no sabe de qué objeto seríanSí: los del objeto sobre el que se llama

Encapsulación: private, get y set#

Tal y como está, cualquiera puede escribir a1.edad = -5; y el programa se lo traga. Para protegerlo, los atributos se hacen privados y se accede a ellos a través de métodos, que pueden comprobar los datos:

Java
public class Alumno {
    private String nombre;
    private int edad;
    private String nivel;

    public Alumno(String nombre, int edad, String nivel) {
        this.nombre = nombre;
        this.edad = edad;
        this.nivel = nivel;
    }

    public String getNombre() {
        return nombre;
    }

    public int getEdad() {
        return edad;
    }

    public void setEdad(int edad) {
        if (edad > 0) {
            this.edad = edad;
        }
    }
}
PalabraQuién puede usarlo
privateSolo el código de la propia clase
publicCualquier parte del programa
MétodoPara quéEjemplo
get… (getter)Leer un atributo privadoa1.getEdad()
set… (setter)Cambiar un atributo privado, comprobando antes que el valor tiene sentidoa1.setEdad(18);
Java
Alumno a1 = new Alumno("Antonio", 17, "DEV");
a1.setEdad(-5);                      // no hace nada: la edad no puede ser negativa
a1.setEdad(18);
System.out.println(a1.getEdad());    // 18
// a1.edad = 20;                     ← error: edad es privada

Mostrar un objeto: toString#

Si haces System.out.println(a1), Java no sabe cómo mostrar un alumno y saca algo como Alumno@6d06d69c. Para decirle cómo, se añade a la clase el método toString:

Java
@Override
public String toString() {
    return nombre + " (" + nivel + ")";
}

Ahora System.out.println(a1) imprime Antonio (DEV). La línea @Override avisa a Java de que estás cambiando un método que ya existía; no es obligatoria, pero ayuda a detectar errores.

Arrays de objetos#

Un array puede guardar objetos igual que guarda números. Así se maneja una clase entera:

public class App {
    public static void main(String[] args) {
        Alumno[] clase = {
            new Alumno("Antonio", 17, "DEV"),
            new Alumno("María", 16, "DEV"),
            new Alumno("Juan", 14, "Master")
        };

        for (int i = 0; i < clase.length; i++) {
            clase[i].presentarse();
        }
    }
}

Imprime:

Salida
Soy Antonio, tengo 17 años y estoy en DEV
Soy María, tengo 16 años y estoy en DEV
Soy Juan, tengo 14 años y estoy en Master

Errores frecuentes#

Mensaje o síntomaQué significaCómo se arregla
class Alumno is public, should be declared in a file named Alumno.javaEl fichero no se llama como la claseRenombra el fichero: la clase Alumno va en Alumno.java (mayúsculas incluidas)
constructor Alumno in class Alumno cannot be applied to given typesEl new no da los datos que pide el constructornew Alumno("Antonio", 17, "DEV"), en el orden del constructor. Si has escrito un constructor con datos, new Alumno() ya no vale
nombre has private access in AlumnoIntentas usar un atributo privado desde fuera de la claseUsa su método: a1.getNombre()
NullPointerExceptionAl ejecutar: usas un objeto que no existe. Pasa con Alumno[] clase = new Alumno[3];, que crea los huecos, pero vacíosCrea cada objeto con new antes de usarlo: clase[0] = new Alumno("Ana", 15, "Master");
variable a1 might not have been initializedHas escrito Alumno a1; sin newAlumno a1 = new Alumno(…);
Se imprime algo como Alumno@6d06d69cLa clase no tiene toStringAñade el método toString a la clase