¡Por fin! Después de nombrarlo tantas veces, en el artículo de hoy veremos qué es un Objeto y una Clase . Todo ello, después del salto: Para el artículo de hoy utilizaremos dos ficheros:

Main.java

package net.openwebinars.objetos;

public class Main {

  public static void main(String[] args) {
    Perro a = new Perro("Pablo", 5);
    System.out.println("El perro a se llama " + a.getNombre() + " y tiene " 
                      + a.getEdad() + " años");
    Perro b = new Perro();
    b.setNombre("Luis");
    b.setEdad(7);
    System.out.println(b);
  }

}

Perro.java

package net.openwebinars.objetos;

public class Perro {

  // Atributos
  private String nombre;
  private int edad;

  // Constructor con argumentos
  public Perro(String nombre, int edad) {
    this.nombre = nombre;
    this.edad = edad;
  }

  // Constructor vacío
  public Perro(){};

  public String toString() {
    return ("Nombre: " + getNombre() + " Edad: " + getEdad() + "años");
  }

  public String getNombre() {
    return (this.nombre);
  }

  public void setNombre(String nombre) {
    this.nombre = nombre;
  }

  public int getEdad() {
    return (this.edad);
  }

  public void setEdad(int edad) {
    this.edad = edad;
  }

}

Programación Orientada a Objetos (OOP)

La programación orientada a objetos es un paradigma de la programación ( ¿paradigma? ) mediante el cual las entidades de nuestro programa se definen como clases y objetos . Si bien existen muchos paradigmas distintos, el que más se utiliza actualmente es el de la programación orientada a objetos. Aunque no lo creas, en todos los demás artículos hasta ahora hemos utilizado en todos ellos OOP. ¿Cómo es posible? Primero vamos a definir qué es una clase y un objeto.

Clase

Llamamos clase a la definición de la estructura que tendrá un objeto específico (su comportamiento y su estado). Cada vez que creamos un objeto a partir de una clase, realmente lo que ocurre es que se monta la estructura definida en ella sobre el objeto. En el artículo de hoy tenemos dos clases:
  • Main es la clase principal que hemos utilizado en todos nuestros anteriores programas
  • Perro es una clase en la que se describe a un perro con su nombre y su edad
De forma general, las clases tienen la siguiente estructura:
public class NombreClase {
  // Atributos

  // Constructores

  // Métodos
}
Nota: No es obligado seguir esta estructura; habrá casos donde no necesitemos un constructor o incluso no necesitemos métodos.

Atributos

Los atributos son variables propias de la clase las cuales definen estados de la misma. Si nos fijamos en nuestro fichero de hoy Perro.java tenemos dos atributos:
private String nombre;
private int edad;
La palabra reservada private significa que el atributo con esa palabra no podrá ser accedido fuera de un objeto (directamente). Más adelante veremos las distintas medidas de ámbito, pero por ahora decir que si creamos un objeto de la clase Perro, no podemos asignarle ni obtener valores a los atributos de esa clase desde fuera. La palabra private también se puede utilizar con otras clases y funciones o procedimientos, pero insisto que eso lo veremos más adelante.

Constructores

A la hora de crear un objeto, la máquina virtual de Java internamente intenta "construir" el objeto. Si la clase tiene definida por nosotros constructores , al instanciar un objeto se buscará de entre todos los constructores el más adecuado a la forma de crear el objeto (si se crea con argumentos o no, qué tipo de argumentos se utilizan, etc). En el fichero Perro.java tenemos dos constructores:
// Constructor con argumentos
public Perro(String nombre, int edad) {
  this.nombre = nombre;
  this.edad = edad;
}

// Constructor vacío
public Perro(){};
En el primero, creamos el objeto de forma que le pasamos dos parámetros: una cadena con el nombre del Perro y un número de tipo int con su edad. Un detalle bastante importante: nuestros atributos se llaman "nombre" y "edad", igual a como se llaman los argumentos del constructor. Entonces si hicieramos esto:
// Constructor con argumentos
public Perro(String nombre, int edad) {
  nombre = nombre;
  edad = edad;
}
Resulta bastante ambiguo ya que no sabemos qué variable se refiere al atributo de la clase y qué variable se refiere al argumento. Para resolver esa ambigüedad tenemos lo que en otros lenguajes se le conoce como puntero this .

this

La palabra reservada this hace referencia a la instancia actual de la clase o al objeto en el cual se le llama. De esta forma, si en alguna parte del código quieres referenciarte al objeto mismamente sólo tienes que utilizar this. Te propongo un ejercicio, create la siguiente clase:
// Constructor con argumentos
public Prueba() {
  System.out.println(this);
}
Y luego crea dos, tres, tantos objetos como quieras de la clase Prueba. El mensaje mostrado por pantalla en "System.out.println(this);", ¿será el mismo para todas las clases? Volviendo a los constructores, en la clase Perro tenemos otro constructor:
// Constructor vacío
public Prueba() {};
Con este constructor vacío estamos indicando que podemos crear un objeto de la clase Perro sin estar obligados a pasarle argumentos al constructor. De hecho, cuando no definimos ningún constructor, Java crea un constructor vacío por defecto de esa clase. Nota: Con respecto a los constructores hay que tener en cuenta una cosa: se utilizan sólo para inicializar nuestro objeto; es de mala programación hacer otras cosas dentro de un constructor ya que esa no es su finalidad principal. El siguiente ejemplo estaría mal:
public class Perro {

  private String nombre;
  private int edad;

  public Perro(String nombre, int edad) {
    this.nombre = nombre;
    this.edad = edad;
    ladrar();
  }

  public void ladrar() {
    System.out.println("guau");
  }

}
Si quieres llamar al procedimiento ladrar() justo después de inicializarlo, no lo llames dentro del constructor; primero creas el objeto y luego llamas a ladrar().

Métodos

En artículos anteriores vimos lo que eran las funciones y los procedimientos. Cuando definimos una subrutina propia de una clase decimos que estamos declarando un método de la clase. Igual que sucedía con los atributos, los métodos pueden no ser accedidos fuera de la clase utilizando la palabra reservada private. Esto se utiliza para evitar que se puedan llamar a métodos críticos fuera de la clase (métodos que pueden afectar a los atributos de un objeto). En nuestra clase Perro tenemos 5 métodos:
public String toString() {
  return ("Nombre: " + getNombre() + " Edad: " + getEdad() + "años");
}

public String getNombre() {
  return (this.nombre);
}

public void setNombre(String nombre) {
  this.nombre = nombre;
}

public int getEdad() {
  return (this.edad);
}

public void setEdad(int edad) {
  this.edad = edad;
}

Métodos Getter y Setter

Los métodos Getter y Setter son aquellos definidos por el propio usuario cuya única finalidad es obtener/modificar el valor de un atributo al cual referencian. Aunque su nombre se puede definir de la forma que queramos, lo normal es seguir este formato:
public tipoDato getNombreAtributo() {
  return (nombreAtributo);
}

public void setNombreAtributo(tipoDato nombreAtributo) {
  this.nombreAtributo = nombreAtributo;
}
Según la documentación de Oracle, se debe de utilizar los getter incluso dentro de una misma clase para acceder a los atributos tal y como muestro a continuación:
public String toString() {
  return ("Nombre: " + getNombre() + " Edad: " + getEdad() + "años");
}
La idea de ello radica en que si cambiamos el nombre o el tipo de dato de un atributo es más fácil cambiar su método getter o setter que todas las veces que nombramos el atributo en nuestro código. Tiene la desventaja de que puede ralentizar a la larga un poco nuestro código al tener que añadir tantas llamadas a funciones en lugar de acceder directamente al atributo. Una forma de solucionar eso es llamar una sola vez al método y guardar su contenido (en el caso del getter) en una variable temporal:
public String toString() {
  String nom = getNombre();
  int ed = getEdad();
  return ("Nombre: " + nom + " Edad: " + ed + "años");
}

Método toString()

Existen métodos especiales los cuales podemos modificar para que se comporten como nosotros queramos. Uno de ellos es el método toString() . Modificar el método toString() implica que cuando intentemos convertir nuestro objeto en una cadena String (por ejemplo para mostrarlo por pantalla) se buscará el método toString(). Si está definido en su clase, se mostrará la cadena que devuelva nuestro método. Por ello, el método toString() siempre debe devolver una cadena String. Viendo eso en nuestra clase Perro, al imprimir un objeto de esa clase se mostrará el siguiente mensaje:
Nombre: Luis Edad: 7años
Si quieres conocer una lista completa de qué métodos podemos sobreescribir, puedes mirar la documentación de Oracle

Objetos

Un objeto no es más que una instanciación de una clase. La forma de crear un objeto es la siguiente:
NombreClase nombreObjeto = new NombreClase();
Si nos fijamos en el fichero Main.java vemos que en ella se crean dos objetos de la clase Perro:
public static void main(String[] args) {
  Perro a = new Perro("Pablo", 5);
  System.out.println("El perro a se llama " + a.getNombre() + " y tiene " 
                    + a.getEdad() + " años");
  Perro b = new Perro();
  b.setNombre("Luis");
  b.setEdad(7);
  System.out.println(b);
}
Tal y como comenté al explicar las clases, si te fijas estamos creando dos objetos de la clase Perro utilizando los dos constructores definidos en esa clase:
public static void main(String[] args) {
  // Constructor con argumentos
  Perro a = new Perro("Pablo", 5);
  // Constructor vacío
  Perro b = new Perro();
}
En el caso del constructor vacío, para asignarle los valores a los atributos nombre y edad utilizamos los métodos setter de la clase Perro:
Perro b = new Perro();
b.setNombre("Luis");
b.setEdad(7);
Finalmente podemos ver al intentar mostrar por pantalla el objeto b , se invoca al método toString() definido en la clase mostrando el mensaje indicado anteriormente. Si no hubieramos definido dicho método, el mensaje mostrado sería algo así:
net.openwebinars.objetos.Perro@422ede
Por ello es ideal modificar el método toString() para a la hora de depurar mostrar el estado en ese momento de nuestro objeto. Y hasta aquí llegamos con el capítulo de hoy. No se olviden de que tienen a su disposición el curso de la asignatura en cursos.openwebinars.net donde pueden hacer ejercicios y/o cuestionarios a modo de autoaprendizaje. No duden en preguntar todo aquello que no entiendan y espero verles en el próximo artículo donde aprenderemos a manejar ámbitos y a entender cómo funciona nuestro hilo de ejecución . ¡Un saludo!

Listado de capítulos

En esta sección se listan todos los capítulos de este pequeño curso:
  1. Introducción
  2. Hello World!
  3. Variables y Operadores
  4. Control de Flujo
  5. Funciones y Procedimientos
  6. Recursividad y Divide y Vencerás