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#
| Concepto | Qué es | En el ejemplo |
|---|---|---|
| Clase | El molde: describe cómo es un alumno, pero no es ningún alumno concreto | Alumno |
| Objeto | Cada alumno concreto hecho con el molde. Puede haber tantos como quieras | Antonio, María, Juan… |
| Atributo | Cada dato que tiene un objeto | nombre, edad, nivel |
| Método | Lo que sabe hacer un objeto | presentarse() |
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.
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
}
}void main() {
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";
IO.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:
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:
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:
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:
Alumno a1 = new Alumno("Antonio", 17, "DEV");
a1.presentarse(); // Soy Antonio, tengo 17 años y estoy en DEV
System.out.println(a1.esMayorDeEdad()); // falseAhora se entiende el static del tema de funciones:
Con static | Sin static | |
|---|---|---|
| ¿De quién es? | De la clase entera | De 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ían | Sí: 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:
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;
}
}
}| Palabra | Quién puede usarlo |
|---|---|
private | Solo el código de la propia clase |
public | Cualquier parte del programa |
| Método | Para qué | Ejemplo |
|---|---|---|
get… (getter) | Leer un atributo privado | a1.getEdad() |
set… (setter) | Cambiar un atributo privado, comprobando antes que el valor tiene sentido | a1.setEdad(18); |
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 privadaMostrar 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:
@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();
}
}
}void main() {
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:
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 MasterErrores frecuentes#
| Mensaje o síntoma | Qué significa | Cómo se arregla |
|---|---|---|
class Alumno is public, should be declared in a file named Alumno.java | El fichero no se llama como la clase | Renombra el fichero: la clase Alumno va en Alumno.java (mayúsculas incluidas) |
constructor Alumno in class Alumno cannot be applied to given types | El new no da los datos que pide el constructor | new 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 Alumno | Intentas usar un atributo privado desde fuera de la clase | Usa su método: a1.getNombre() |
NullPointerException | Al ejecutar: usas un objeto que no existe. Pasa con Alumno[] clase = new Alumno[3];, que crea los huecos, pero vacíos | Crea cada objeto con new antes de usarlo: clase[0] = new Alumno("Ana", 15, "Master"); |
variable a1 might not have been initialized | Has escrito Alumno a1; sin new | Alumno a1 = new Alumno(…); |
Se imprime algo como Alumno@6d06d69c | La clase no tiene toString | Añade el método toString a la clase |