Tabla de Contenidos

Clases y Objetos

La utilización de clases y objetos (ó instancias) es uno de los pilares de la programación orientada a objetos (POO). Debemos aprender a emplear ambos términos de una forma exacta.

Los objetos en programación pueden representar cosas que tenemos en la vida real, pero nunca serán elementos de la vida real.

Sin embargo nos pueden servir para representar esos elementos concretos de una forma abstracta dentro de un programa. La abstracción es algo esencial en la programación orientada a objetos.

Clase

Es la parte de un programa en Java donde se definen las propiedades y operaciones que van a tener los ejemplares (objetos, instancias) de dicha clase. Con una clase se define un nuevo tipo de datos (Scanner, String, Coche).

Se puede entender que una clase son los planos o el molde, con el que se indican todas las características o forma para poder crear un objeto (ejemplar) de esa clase.

Declaración de una clase

modificadores class nombreClase{
   declaracionesDeMiembros;
}

Miembros de una clase

Dentro de una clase, como hemos visto hasta ahora, solo podemos definir dos cosas: métodos y variables.

Cada uno de los elementos de una clase (variables o métodos) se conoce como miembros de la clase.

public class Coche{
   //Atributos: definen el estado del objeto
   String matricula;
   int numRuedas;
   String marca;
   float kilometros;
 
   //Métodos: operaciones del objeto
   void arracar(){
   }
 
   int acelerar(int intensidad){
   }

Además pueden existir otro tipo de miembros de una clase (métodos o variables) que son estáticos static. A este tipo de miembros se les conoce como métodos o variables de clase. Se tratan unos puntos más adelante.

Las variables locales (de ámbito local) son aquellas que no son miembros de la clase, sino que están dentro del cuerpo de un método o bloque de código (entre corchetes). Si recordamos, una variable de ámbito local, solo puede ser accedida desde el mismo ámbito en el que se declara, o en ámbitos inferiores. O sea, dentro de los corchetes (bloque) en el que se declara o en bloques creados dentro del bloque en el que se declara.

Objeto

Un objeto es la materialización de una clase en un elemento o ejemplar de dicha clase. Existe en la memoria del ordenador (se crea) y tiene las propiedades (variables miembro) y las operaciones (métodos miembro) que se han indicado en la definición de la clase.

Un objeto tiene un estado propio, que viene definido por el estado de sus atributos. Si creamos dos objetos de la misma clase, pueden tener estados distintos.

Se puede entender un objeto como el resultado de construir un ejemplar a partir de los planos o molde (clase) que hemos definido anteriormente.

Instanciar una clase: Constructores

Crear un ejemplar (objeto) de una clase se conoce como instanciar una clase. Para crear un objeto o instancia de una clase, debemos llamar o ejecutar su constructor.

Operador new

Se necesita usar el operador new para llamar al constructor de una clase. Se ha usado en otras ocasiones:

Scaner input;
 
//llamada al constructor Scanner()
//el constructor recibe un parámetro con el flujo de entrada
input =  new Scanner(System.in);
 
String cadena;
 
//llamada al constructor String() que crea una cadena vacía.
cadena = new String();
 
//llamada al constructor String(String cadena)
//crea una cadena con el valor del parámetro
cadena = new String("fernando");

Definir un constructor

La llamada a un constructor devuelve siempre la referencia al objeto creado en memoria (heap).

La declaración del constructor de una clase tiene una sintaxis parecida a la de los métodos, pero:

modificadores nombre_clase(){
   //instrucciones
}
 
//Ejemplo
public class Coche{
   private String matricula;
   private int km;
 
   //Constructor de la clase Coche
   public Coche(){
      //instrucciones que se realizan al crear un objeto Coche
   }
}

Los constructores también pueden recibir parámetros, pero recordamos que nunca devuelven un valor.

public Coche(String cod_matricula){
   matricula = cod_matricula;
}

Y así puedo crear instancias de la clase Coche utilizando los constructores que he definido:

public static void main(String[] args){
   Coche miCoche = new Coche();
 
   Coche miOtroCoche = new Coche("FDS-1234");
}

Concluyendo, ¿qué hace el operador new cuando precede al constructor de una clase? Devuelve el valor de la referencia del objeto que se acaba de crear:

// Declaro una variable de la clase Coche
Coche miVariable;
 
// Asigno la referencia de un objeto Coche a la variable
miVariable = new Coche();

La variable, no contiene el objeto sino la referencia (dirección) al lugar de la memoria en el que ha guardado.

Constructor no-args

Se conoce como el constructor que no recibe parámetros. Si en una clase no definimos explícitamente un constructor, Java creará implícitamente un constructor llamado no-args que tiene el siguiente aspecto:

public Coche(){
}

De este modo aunque no definamos un constructor siempre podremos instanciar una clase.

Operaciones de un Constructor

Los constructores se utilizan para inicializar los atributos de una clase que necesiten ser inicializados para ser usados, por ejemplo: arrays, listas, creación de objetos, etc.

public class Alumno{
   private String nombre;
   private int[] notas;
   private File foto;
 
   public Alumno(){
      notas = new int[6];
   }
 
   public Alumno(String nombre, String rutaFoto){
      this.nombre = nombre;
      notas = new int[6];
      foto = new File(rutaFoto);
   }
}

Los atributos de la clase (variables de instancia) nunca se inicializan al ser declarados. Esto no se aplica a las variables static (variables de clase) , ya que existen aunque no se llame al constructor.

public class Alumno{
   //Incorrecto y poco estético
   private String nombre = "";
   private int[] notas = new int[10];

Variables de referencia

Toda variable que no es de tipo primitivo es, sin lugar a dudas, una variable de referencia.

Las variables de referencia son todas las variables cuyo tipo de datos es una clase. No contienen al objeto en sí, sino una referencia (dirección) al objeto creado en la memoria del ordenador (heap). Este es uno de los puntos más importantes del lenguaje Java.

Para obtener la referencia de un nuevo objeto usamos su constructor:

//Creo 2 variables de referencia, vacías.
Coche coche1;
Coche coche2;
 
//ahora creo un objeto y guardo su dirección (referencia) en la variable coche1
coche1 = new Coche();
 
//Cambio el valor del campo matrícula del objeto referenciado
coche1.setMatricula("ASD-1234");
 
//Ahora asigno a la variable coche2 el valor de coche1
coche2 = coche1;
 
//En este momento ambas variables tiene la dirección del mismo objeto 
//y (coche1 == coche2) es true
 
//Muestro la matrícula que asigné al objeto desde coche1
System.out.println(coche2.getMatricula()); 
 
//Asigno a coche2 otro valor con la referencia de un nuevo objeto
coche2 = new Coche();
 
//Ahora coche2 tiene un valor distinto: almacena otra dirección 
// y está referenciando a un nuevo objeto

Valor null

El valor null representa el valor de referencia nulo (“sin valor”). Se usa para indicar la ausencia de referencia a un objeto.

Paso de parámetros

Toda variable contiene un valor y cuando se le pasa un parámetro a un método, se le pasa un valor, ya sea un valor de referencia o un valor primitivo.

En otros lenguajes existe el paso de parámetros por referencia frente al paso por valor. Java solo permite el paso de parámetros por valor (valores de referencia o primitivos).

Modificadores de visibilidad o acceso

Para indicar desde qué clases se puede acceder a los miembros de otra clase se usan los llamados modificadores de visibilidad.

Usan al declarar los miembros de una clase (atributos, métodos, o constructores) y definen desde qué otras clases puedo acceder a sus miembros.

Por convenio, los atributos de una clase se deben definir como private.

public class Coche{
   private int matricula;
 
   public int getMatricula(){
      return matricula;
   }
}
 
//Desde el método main de otra clase
public static void main(String[] args){
   Coche unCoche = new Coche();
 
   unCoche.matricula; //No puedo acceder, es private
   unCoche.getMatricula(); //Puedo acceder, es public
}

Métodos getters y setters

Como se acaba de comentar, los atributos de una clase se deben declarar private por convenio. Entonces se necesitan métodos públicos para acceder a los atributos y poder modificarlos. Estos métodos se conocen como getters y setters.

   private String nombre;
   private int edad;
   private boolean casado;
 
   public String getNombre(){
      return nombre;
   }
 
   public void setNombre(String nombre){
      this.nombre = nombre;
   }
 
   ...

Estos métodos se nombran como get cuando quiero obtener el valor, y como set cuando quiero establecerlo. En el caso de los atributos booleanos en lugar de get, se suele indicar is, aunque no afecta al funcionamiento.

La mayoría de IDE's permiten generar getters y setters.

En Eclipse cuando tengamos el cursor del ratón en el editor de una clase, hacemos click derecho y vamos al menu sourceGenerate Getters and Setters.


Palabra reservada: this

La palabra this se usa dentro de un método o de un constructor en una clase y representa el objeto actual: el objeto cuyo método o constructor está siendo llamado.

this contiene una referencia al objeto actual.

Hay 2 razones para usarlo:

public class Persona{
   private String nombre;
   private int edad;
   private int altura;
 
   //Uso this para saber que uno el el atributo
   //Y el otro el parámetro
   public void setNombre(String nombre){
      this.nombre = nombre;
   }
 
   public Persona(String nombre, int edad){
      this.nombre = nombre;
      this.edad = edad;
   }
 
   //Pero tambien lo puedo usar para llamar a un constructor ya definido
   public Persona(String nombre, int edad, altura){
      this(nombre,edad);
      this.altura = altura;
   }
 
}

Sobrecarga de métodos y constructores

El concepto de sobrecarga (overload) consiste en crear varios métodos con el mismo nombre. Aplicado a constructores (todos tienen el mismo nombre) consiste en tener varios constructores. Es un aspecto que se trató en el tema de métodos estáticos

Para que pueda sobrecargar constructores o métodos, cada uno debe diferenciarse de otro en el numero, o tipo de los parámetros que recibe.

Java solo nos permite crear más de un método o constructor con el mismo nombre si el número de parámetros o su tipo, es diferente a los de los demás métodos. El valor de retorno no se toma como dato diferenciador.

Ejemplo de sobrecarga de método indexOf() de la clase String:

Modificador static

El modificador static se aplica principalmente a variables o a métodos de una clase. Ya lo hemos visto aplicado a métodos.

Variables miembro estáticas: Una variable estática no se asocia a una instancia de una clase (objeto), sino a la clase misma. Esto es: no hay una copia de la variable estática por cada objeto de la clase, sino una sola copia por clase. A estas variables se les conoce como variables de clase.

Aunque se creen varios objetos de una misma clase, el valor de sus variables estáticas es el mismo para todos los objetos, y si se modifican, se modifican para todos. Solo hay una copia en memoria.

Métodos miembro estáticos: Al igual que las variables estáticas, los métodos estáticos no dependen de un objeto de la clase, y por eso se pueden llamar sin necesidad de crear un objeto de la clase. Se les llama método de clase.

Dado que no es necesario crear un objeto para llamar a estos métodos, no puedes acceder a variables miembro de la clase que no sean estáticas. Esto tiene sentido, ya que si las variables no son estáticas dependen de un objeto, y como hemos dicho, no es necesario un objeto para llamar al método.

Inicialización de atributos de una clase

Desde el punto de vista del lugar donde se declaran existen dos tipos de variables:

Las variables miembro son inicializadas automáticamente, de la siguiente forma:

Las variables miembro pueden inicializarse con valores distintos de los anteriores en su constructor. Las variables locales (de ámbito local) no se inicializan automáticamente. Se les debe asignar un valor antes de ser usadas. Los atributos estáticos solo pueden inicializarse en su declaración (no en un constructor).

Recolector de basura

Cuando ya no se necesita un objeto simplemente puede dejar de referenciarse. No existe una operación explícita para 'destruir' un objeto o liberar el área de memoria usada por él.

Un objeto deja de referenciarse cuando no hay ninguna variable de referencia dentro del programa que contenga su valor de referencia:

Coche miCoche = new Coche();
miCoche = null; //He perdido el valor de la referencia al objeto
 
//Otro ejemplo:
Coche miCoche = new Coche();
miCoche = new Coche(); //Pierdo la referencia del objeto anterior

Una variable solo puede contener un valor, y cuando no hay ninguna variable que guarde la referencia a un objeto, lo habré perdido para siempre.

La liberación de memoria la realiza el recolector de basura (garbage collector), que es una función de la JVM (Java Virtual Machine). El recolector revisa toda el área de memoria del programa y determina qué objetos pueden ser borrados de la memoria porque ya no tienen referencias activas que los apunten.

El recolector de basura actúa cuando la JVM lo determina (tiene un mecanismo de actuación no trivial). No existe un momento concreto en que las áreas de memoria son liberadas, sino que lo determina en cada momento la JVM en función de sus necesidades de espacio.

Clase Object

La clase Object es la clase raíz de la cual heredan todas las clases. Esta herencia es implícita, no es necesario indicarlo en la subclase (mediante extends). Puede que una clase no herede de Object, pero tendrá una superclase que sí lo hace.

La clase Object define una serie de métodos miembro que son heredados por todas las clases. Algunos de estos métodos se redefinen (o sobrescriben) en las clases que definamos.

Encapsulamiento y abstracción

La programación orientada a objetos (POO) es un paradigma de programación basado en unos pilares. Entre ellos podemos encontrar 4 principales: abstracción, encapsulamiento, herencia y polimorfimo.

La abstracción se puede entender como la representación de entidades (pueden ser de la vida real) mediante elementos de un programa. Definimos esos elementos mediante las clases (Coche, Vehiculo, Persona, Pasajero, etc) y los usamos mediante sus objetos.

Además, estos elementos realizan una serie de operaciones, pero nosotros no tenemos por qué saber cómo se realizan están operaciones: el cómo se hace, formará parte de la clase, donde se definen cómo se hacen las cosas. A nosotros una clase nos ofrece una interfaz (métodos) mediante la cual realizar dichas operaciones, sin tener que preocuparnos de entender cómo se hacen o de implementar su funcionamiento cada vez que las usemos. Todo esto se conoce como abstracción.

El concepto de encapsulamiento es otro pilar fundamental de la POO y está ligado a la abstracción. Consiste en aislar los miembros (propiedades y operaciones) del acceso exterior (ponerlos dentro de una clase). Así evitamos que otras partes del programa pueden modificar las propiedades de un objeto de forma errónea, y crearemos una interfaz para trabajar con el objeto y acceder a sus propiedades. Esta interfaz son los métodos. Es la forma en la que el objeto nos permite trabajar con él, sin poder tener acceso a su interior (implementación de operaciones, valor de las propiedades).

La herencia y el polimorfismo se verán más adelante ya que también están ligados entre sí.

Otras palabras reservadas

Las siguientes palabras clave se irán viendo a medida que trabajemos con nuestras clases y ampliando conocimientos:


© 2024 Fernando Valdeón