Prog. Fundamentos Programación Orientada a Objetos (POO)

De MediaWiki
Ir a la navegación Ir a la búsqueda

Introducción

  • Antes del paradigma de la POO se utilizaba la programación estructurada.
Este tipo de programación se basa en un diseño descendente (metodología top-down) y se utilizan para implementar los programas tres estructuras básicas: secuencial-condicional-repetitiva.
El programa consistía normalmente en un único archivo donde se encontraba todo el código fuente que daba una solución informática al problema del mundo real, en el que los datos se declaraban a nivel 'global' y definimos un conjunto de funciones, el que se trataban dichos datos y se informaba al usuario.
Este es un tipo de programación orientado al tratamiento de datos, a los procedimientos.
Cuando diseñas un programa utilizando este tipo de metodología piensas en las operaciones y partiendo de ellas, diseñas el programa.
Por ejemplo, si quiero dar una solución informática a la gestión de un centro de enseñanza, podría pensar (metodología top-down):
  • Necesito una gestión de los alumnos del centro.
  • Necesito una gestión de los profesores del centro.
  • Dentro de la gestión de alumnos tendré:
  • Alta => función que pida los datos y los añada
  • Baja => función que pida un identificador y de baja
  • Modificación => función que pida los datos y los modifique
  • Consulta => función que busque datos en base a diferentes criterios.
  • Dentro de la gestión de profesores tendré:
  • Alta => función que pida los datos y los añada
  • Baja => función que pida un identificador y de baja
  • Modificación => función que pida los datos y los modifique
  • Consulta => función que busque datos en base a diferentes criterios.


Los datos estarán definidos a nivel global, utilizando una estructura adecuada para guardarlos, como los arrays.



  • En el paradigma de la POO, el diseño se realiza en base a los datos y utilizando un concepto mucho más 'humano' como es el de objeto.
Un objeto es cualquier cosa que puedas identificar en el mundo real y sobre la que quieres guardar información y tratarla.
Ejemplo de objetos:
  • Un coche Opel negro matrícula 1234-ABC
  • Una matriz matemática
  • Un globo de color verde que lleva un niño
  • El ordenador con el que estás trabajando
En la POO cada de las características que posee un objeto se denomina atributo (en la Prog. Estructurada se llaman variables).
Todos los objetos 'coches' tienen un conjunto de atributos comunes (matrícula, km recorridos, color,....) por lo que en programación, para poder crear esos objetos utilizamos otro concepto denominado clase.
Una clase define todos los atributos que van a tener los objetos, pero no sus valores, sino los tipos y nombres de los atributos.
Así puedo definir:
  • Una clase Coche que tenga los atributos 'matrícula', 'km', 'color', 'estado'
  • Una clase Matriz que tenga los atributos 'fila','columna'
  • Una clase Globo, que tenga los atributos 'color','tamaño'
  • Una clase Computador que tenga los atributos 'cpu','hd','memoria','velocidadActual'


En una clase, a parte de definir los atributos, también se definen las funciones (métodos en la POO) que se pueden aplicar a los datos definidos en esa clase.
Por ejemplo:
  • La clase Globo puede tener un método 'hinchar' y otro 'deshinchar' que modificará el valor del atributo 'tamaño'.
  • La clase Coche puede tener un método 'recorrer' que modifique el valor del atributo 'km', un método 'arranca','parar','acelerar','frenar' que cambie el atributo 'estado'.
  • La clase Matriz puede tener un método 'sumar' para que modifique el valor del atributo 'matriz'.
  • La clase Computador puede tener un método 'cambiarVelocidad' que modifique el valor del atributo 'velocidadActual'.


En el ejemplo anterior de un centro de enseñanza, si aplicamos esta metodología tendríamos:
  • Una clase 'Alumno' que representaría cada uno de los alumnos
  • Con un método 'crear' que guardaría los datos de un nuevo alumno.
  • Con un método 'modificar' que modificaría los datos de un alumno.
  • El método 'eliminar' no sería necesario en esta clase ya que cuando un objeto no se utiliza, Java lo elimina con el proceso 'garbage collection'.
  • Una clase 'Profesor' que representaría cada uno de los profesores.
  • Con un método 'crear' que guardaría los datos de un nuevo profesor.
  • Con un método 'modificar' que modificaría los datos de un profesor.
  • El método 'eliminar' no sería necesario en esta clase ya que cuando un objeto no se utiliza, Java lo elimina con el proceso 'garbage collection'.
  • Una clase Centro que representaría el centro donde trabajan y estudian los alumnos y profesores.
Dentro de esta clase definiríamos:
  • Un atributo que guardaría un array de 'objetos Alumnos' que representa todos los alumnos del centro.
  • Un atributo que guardaría un array de 'objetos Profesores' que representa todos los profesores del centro.
  • Un método 'crearAlumno' que crearía un nuevo objeto Alumno (llamando al método crear) y lo añadiría al array de Alumnos.
  • Un método 'crearProfesor' que crearía un nuevo objeto Profesor (llamando al método crear) y lo añadiría al array de Profesores.
  • Un método 'eliminarAlumno' que borraría un objeto Alumno del array de Alumnos.
  • Un método 'eliminarProfesor' que borraría un objeto Profesor del array de Profesores.
  • Un método 'modificarAlumno' que en base a un identificador (por ejemplo el nif) buscaría el objeto alumno dentro del array de Alumnos y llamaría al método 'modificar' de ese objeto.
  • Un método 'modificarProfesor' que en base a un identificador (por ejemplo el nif) buscaría el objeto profesor dentro del array de Profesores y llamaría al método 'modificar' de ese objeto.
  • Un método 'listarAlumnos' que en base a un criterio (nombre, nif,...) recorra el array de Alumnos, mostrando aquellos objetos cuyo criterio coincida con los valores guardados en cada uno de los objetos.






POO


Conceptos


Clases

  • Una clase es un conjunto de datos (atributos) y métodos que manejan esos datos.
Para llegar a identificar una clase debemos hacer uso de la abstracción
La abstracción es el proceso por el que identificamos las características más importantes de un objeto, sin especificar ningún valor concreto. Solamente identificamos las propiedades comunes a todos los objetos así como las operaciones que vamos a realizar sobre dichas propiedades.
  • Los atributos y los métodos definen las características de algún tipo de objeto.
  • Por ejemplo, podemos describir un círculo por su punto (x,y) de su centro y por el valor de su radio.
Cada círculo es diferente de los demás (en el centro o en el radio), pero como clase, todos los círculos tienen una serie de propiedades que sirven para dar una definición de círculo.
Es decir, una clase consta de métodos (procedimientos) y atributos (variables), que se encuentran relacionados formando un todo que llamamos clase.


  • Importante:
  • El nombre de las clases en Java debe comenzar con la primera letra en mayúsculas y las siguientes primeras letras de cada palabra si tuviera más de una. Por ejemplo: ChaletAdosado
  • El nombre debe estar en singular


  • En Java, la creación de una clase consta de una cabecera y de un cuerpo de la clase.
  • La declaración general de una clase es la siguiente:
 1 access class Nombre_Clase [extends NombreClase] [implements NombreClase,....] { // Comienzo del código de la clase
 2 
 3   [access] [static] tipo variable_de_instancia1;    // Declaramos variables
 4   .....
 5   [access] [static] tipo variable_de_instanciaN;
 6   .....
 7 
 8   [access] [static] tipo método1 (lista de parámetros) {   // Declaramos métodos
 9      .....
10   }
11      .....
12   [access] [static] tipo métodoN (lista de parámetros) {
13      .....
14   }
15 } // Fin del código de la clase
  • Línea 1: Cabecera de la clase. Se indica el nombre de la clase así como un conjunto de modificadores que iremos viendo más adelante.
  • Líneas 3-14: Cuerpo de la clase. Conjunto de instrucciones que se encuentran entre '{' y '}' y que conforman los atributos y métodos de la clase.


Más adelante veremos qué significan todas estas palabras clave ...
Por ejemplo:
1 public class Alumno{
2   String nif;
3   byte edad;
4 
5   public void mostrarEdad(){....}  // Imprime la edad
6 
7 }  // Fin de la clase


  • El uso de clases va a permitirnos disponer de una modularidad.
Podremos hacer uso de clases dentro de nuestros programas y reutilizarlas en diferentes proyectos en los que nos haga falta la funcionalidad que ofrecen.
Además podremos modificar una clase sin que el resto de clases se vean afectadas.



Objetos

  • Una vez que se ha definido la clase del círculo, se pueden hacer cosas con ella.
Para usar los métodos y acceder a los datos, se necesita un círculo específico, no sirve la clase; se requiere una instancia de la clase: un objeto Círculo.
La diferencia entre clase y objetos se puede ver al comparar un molde para hacer cookies con las cookies. El molde tiene todas las características de las cookies, pero no es ninguna galleta en particular.
Cada cookie (objeto) es una instancia de la clase de cookie (molde). El proceso de crear un objeto de una clase se llama instanciar.


  • Cada objeto tendrá:
  • Un identificador que permitirá distinguir un objeto de otro aunque tengan los mismos valores en sus atributos (por ejemplo, el nombre del objeto Coche coche1);
  • Un estado que estará conformado por los valores de sus atributos en un momento determinado.
  • Un comportamiento que son las acciones que podemos realizar sobre el objeto y lo conforma el conjunto de métodos de la clase a la que pertenece.




Métodos

  • Los métodos son funciones que pueden llamarse dentro de la clase o por otras clases.
Son las acciones que se pueden realizar con un objeto dado.
Hay muchas cosas que se pueden hacer con círculos: calcular un circunferencia, el área, ver si un determinado punto está dentro o fuera, etc.


  • Los métodos definen el comportamiento de un objeto.
Dependiendo a que clase pertenezca el objeto tendrá un comportamiento u otro.


  • Constituyen la interfaz del objeto, es decir, la forma que tienen otros objetos o clases de comunicarse con un objeto y pedirle información o que realice cierta acción. Podríamos decir que constituyen la forma de enviar mensajes a un determinado objeto.
Por ejemplo, si dispongo de un objeto la clase Empleado que dispone de un método imprimirCuentaCorriente() y desde otro objeto de la clase Empresa de nombre empresa1, quiero enviar un mensaje al objeto empleado1, tendría que poner dentro del código de la clase Empresa: empleado1.imprimirCuentaCorriente();
El objeto de la clase Empresa está mandando un 'mensaje' al objeto de la clase Empleado. El objeto de la clase Empleado ejecutará las sentencias que se encuentren en el método imprimirCuentaCorriente() como respuesta a ese mensaje.


  • La única forma de poder modificar los valores de los atributos de un objeto debe ser a través de sus métodos.
De esta forma nos aseguramos que la manipulación de los datos de un objeto se realice de forma correcta.


  • Al estar los métodos 'encerrados' dentro de la clase y a la necesidad de llamarlos a través de un objeto, es por lo que se dice que están encapsulados dentro del objeto. Pasa lo mismo con los datos.

Programando clases-objetos-métodos


Clases

Para aclarar todos estos conceptos, llevaremos a cabo el siguiente ejercicio.
Supongamos que queremos implementar la siguiente clase:
Prog fund poo 1.jpg
Esta clase tiene dos atributos (variables) que son el nombre y la edad.
La forma de definir Java en esta clase es la siguiente:
1 public class Europeo { // Comienza el código de la clase 
2 
3 
4 
5 } //Termina el código de la clase
  • Nota: La clase tiene un modificador de acceso público (public) para indicar que la clase puede ser utilizada por otras clases. Más adelante revisaremos estos conceptos.
En nuestro caso, la clase Europeo tiene dos variables, que deben definirse.
Para definir una variable dentro de una clase de la manera más simple: Tipo nombre_de_variable;
En nuestro caso:
1 public class Europeo { // Comienza el código de la clase 
2   String nombre;
3   int edad;
4 
5 
6 } //Termina el código de la clase


  • Recuerda: Las variables 'miembro' (las que están definidas a nivel de clase) sí se inicializan si no les damos un valor inicial. Si son de tipo numérico se inicializan a 0, si son booleanas a false y si son de referencia o cadena se inicializan a null.
  • Recuerda: Las variables 'miembro' son 'visibles' (se puede acceder a su valor) en toda la clase.


  • En este momento, hemos definido el molde de las cookies.
Es decir, definimos la clase, pero esta clase no tiene ningún valor concreto para los atributos nombre y edad.
Los valores concretos están asociados con los objetos.
Prog fund poo 2.jpg


  • Importante: Una vez que nuestra clase está escrita, es necesario guardarla en un archivo con el mismo nombre de la clase pública, en nuestro caso Europeo y extensión .java.
Dentro de un único archivo físico, podemos haber definido varias clases. Pero de todos ellas, solo una puede tener el modificador 'public' y el archivo físico deben tener el mismo nombre que esa clase.
Prog fund poo 3.jpg


  • Si no estuviéramos en el NetBeans tendríamos que 'compilar' esta clase con la orden: javac (ya visto anteriormente).


  • Crearemos en Netbeans una clase de nombre 'Principal' que tenga el método main definido de la forma:
 1 package aprendiendo;
 2 
 3 /**
 4  *
 5  * @author clase
 6  */
 7 public class Principal {
 8 
 9     /**
10      * @param args the command line arguments
11      */
12     public static void main(String[] args) {
13         // TODO code application logic here
14     }
15     
16 }

Nota:> El nombre del paquete será diferente al vuestro.



Objetos

  • Ahora, el objetivo es usar la clase Europeo dentro de la clase Principal.
Para esto crearemos (instanciaremos) 3 objetos de la clase Europeo con los siguientes datos:
Prog fund poo 4.jpg


  • ¿Cómo definimos un objeto? Para esto debemos definir un objeto de la misma manera que uno se define variable: Nombre_clase nombre_objeto;
En nuestro caso:
1 Europeo europeo1; // primer objeto 
2 Europeo europeo2; // segundo objeto 
3 Europeo europeo3  // tercer objeto
En este momento, los atributos de la clase (variables) tienen el valor null. El valor null se aplica a las variables de referencia para indiciar 'que no tienen memoria reservada'.
Toda variable de referencia tiene que asignarse memoria (lo vemos a continuación).
Por lo tanto, si intentamos manipular un atributo de referencia sin haberle reservado memoria recibiremos el mensaje null pointer exception.


  • Por lo tanto debemos asignar memoria a estos objetos.
Para esto, se usa el operador new, que reserva la memoria.
Lo que hace el operador 'new' es asignar memoria al atributo ya que una clase es una estructura compleja que necesita 'crearse' en memoria.
Este operador sería equivalente al malloc de C.
En Java no hay operador opuesto, es decir, la liberación de memoria (free en C).
Lo máximo que podemos hacer es asignar un objeto al valor nulo y es Java que a través del proceso de recolección de basura (proceso especial, garbage collection) es responsable de liberar la memoria que ya no es utilizada por ningún objeto.

En nuestro caso:

1 europeo1 = new Europeo();  // reservamos memoria 
2 europeo2 = new Europeo();  // reservamos memoria 
3 europeo3 = new Europeo();  // reservamos memoria


  • El proceso de reservar memoria se llama instanciación.
Estas operaciones se pueden hacer a la vez (definición e instanciación):
1 Europeo europeo1 = new Europeo();
En esta línea, estamos haciendo 3 operaciones:
  • Una declaración: declaramos europeo1 de tipo Europeo.
  • Una instanciación: reservamos memoria con el operador new.
  • Inicialización: más adelante veremos que cuando realizamos la creación de instancias llamamos a uno método especial (procedimiento) llamado constructor que permite inicializar todas las variables. Por defecto, ya vimos que estar variable se inicializan.
  • En este momento tenemos 3 objetos de la clase europea y, por lo tanto, cada objeto tendrá los mismos atributos que la clase a la que pertenece, en este caso dos atributos (nombre y edad).


  • ¿ Cómo asignamos valores a dichas variables ?
De la forma: objeto.atributo = valor;
En nuestro caso:
1 europeo1.nome = Angel;
2 europeo1.idade = 20;
3 europeo2.nome = Pepe;
4 europeo2.idade = 15;
5 europeo3.nome = Jean;
6 europeo3.idade = 17;


  • El código completo quedaría así:
 1 public class Principal {
 2   public static void main(String arg[]) {
 3 
 4       Europeo europeo1 = new Europeo();
 5       Europeo europeo2 = new Europeo();
 6       Europeo europeo3 = new Europeo();
 7 
 8 
 9       europeo1.nome = Angel;
10       europeo1.idade = 20;
11       europeo2.nome = Pepe;
12       europeo2.idade = 15;
13       europeo3.nome = Jean;
14       europeo3.idade = 17;
15   }
16 }
  • A estas variables (por ejemplo, europeo1.'nombre,...) se denominan variables de instancia. Las variables miembro (definidas en la clase) pueden ser variables de instancia o variables de clase (lo veremos cuando expliquemos las variables estáticas más adelante...).


  • Por lo tanto, el ciclo de vida de un objeto será:
  • Creación: Definimos e instanciamos un objeto.
  • Manipulación: Ejecutamos alguno de los métodos definidos en la clase a la que pertenece el objeto.
  • Destrucción: Cuando ya no necesitemos el objeto, Java eliminará el objeto de memoria haciendo uso del proceso garbage collection o recolector de basura.
Para destruir un objeto lo único que tenemos que hacer es dejar de referenciarlo, es decir, el objeto 'europeo1' tiene una zona de memoria asignada y lo estoy utilizando dentro del programa.
Si quisiera dejar de referenciarlo, sólo tendría que asignar al objeto 'europeo1' el valor 'null' de la forma: europeo1 = null;


  • Siguiendo con nuestro ejemplo, pero antes de poder compilar esta clase, es necesario resolver el siguiente problema. Como discutimos anteriormente, en Java, todas son clases. En nuestro caso, estamos utilizando la clase Europeo en la clase Principal.
  • Por lo tanto, es necesario informar al compilador de Java que utilizaremos una clase.
Por defecto, Java busca las clases que se encuentran en el directorio actual (en la compilación), por lo que si está en ese directorio, no es necesario hacer nada más.
En Netbeans estaréis creando las clases en el mismo paquete, pero recordar que un paquete tiene una traducción 'física' en forma de directorio, por lo que todas las clases que se encuentren dentro del mismo paquete se pueden referenciar.


  • Veremos más adelante que existe una sentencia import que se utiliza para indicar a una clase que pueda hacer uso de otras clases que están en otros paquetes (grupo de clases relacionadas).
  • En el lenguaje Java, todas las clases se encuentran dentro de un paquete. Si no ponemos ningún paquete en su definición, Java las crea en el paquete por defecto.


  • El paquete java.lang y el paquete actual se importan de forma predeterminada o el paquete por defecto (las clases que se encuentran en el directorio actual).
  • Si no estuviéramos en el entorno NetBeans, desde consola tendríamos que indicarle a nuestra clase (cuando la compilemos) a dónde tiene que ir a buscar físicamente las clases que vamos a utilizar.
Para ello hacemos uso de la variable de entorno classpath, la cual puede ser establecida gráficamente si estamos en Windows (MiPC, botón derecho, propiedades) o bien por línea de comandos de la forma (versión Windows):
1 javac classpath c:\java Principal.java
Siendo 'c:\java' el directorio donde nuestra clase (Principal.java) iría a buscar las clases que utiliza.


Ejercicios propuestos

  • Crea las clases necesarias con sus atributos para dar solución a los siguientes problemas, creando al menos dos instancias de cada una de ellas, y dándoles un valor a sus atributos:
  • Se quiere guardar información sobre la flota de coches de una empresa. La información que se quiere guardar es la referida a su matrícula, número de km recorridos, marca y modelo.
  • Una empresa quiere guardar los datos de las nóminas de cada empleado, junto su fecha de nacimiento (antes se utilizaba la clase Date) pero ahora, si os fijáis, casi todos sus métodos están 'depracated'. Esto quiere decir que en próximas versiones de Java pueden desaparecer. Ahora mismo se hace uso de la clase Calendar), su nombre , dirección y teléfono. Tenéis ejemplos de uso de Calendar en este enlace.
  • Queremos guardar información sobre las cuentas bancarias de los clientes de un banco.
Cada cuenta bancaria se compondrá de una persona titular de la cuenta, el saldo en euros, y un código de cuenta (el CCC o Código Cuenta Cliente).
El CCC consta de 20 dígitos: 4 dígitos que representan el código de la entidad, 4 dígitos que representan el código de la oficina, 2 dígitos de control y 10 dígitos con el número de la cuenta.
Los dos dígitos de control se calculan con los valores de la entidad, la oficina y el número de cuenta por lo que no será necesario almacenarlos.
  • Queremos realizar un programa que juegue a las cartas, por lo que necesitamos guardar la información de cada una de las cartas que tengan los jugadores. Esta información es la siguiente:
  • Numero: un entero (1 .. 10) que nos representa el número de la carta, siendo el 8 la sota, el 9 el caballo y el 10 el rey.
  • Palo: un tipo de dato numerado que nos representa el palo de la carta (oros,copas,espadas y bastos).





Solución Ejercicios propuestos
  • Se quiere guardar información sobre la flota de coches de una empresa. La información que se quiere guardar es la referida a su matrícula, número de km recorridos, marca y modelo.
1 public class Coche {
2     String matricula;
3     short numKm;
4     String marca;
5     String modelo;
6     
7 }


  • Una empresa quiere guardar los datos de las nóminas de cada empleado, junto su fecha de nacimiento (antes se utilizaba la clase Date) pero ahora, si os fijáis, casi todos sus métodos están 'depracated'. Esto quiere decir que en próximas versiones de Java pueden desaparecer. Ahora mismo se hace uso de la clase Calendar), su nombre , dirección y teléfono. Tenéis ejemplos de uso de Calendar en este enlace.
1 public class Empleado {
2     String nombre;
3     String direccion;
4     String telefono;    // A pesar que es un número no vamos a realizar operaciones matemáticas
5     double nomina;
6     Calendar fechaNac;
7 }
Nota: Fijarse que fechaNac es una variable de referencia a la que no le hemos asignado memoria (no hemos hecho el new, en el caso de Calendar se hace llamando al método getInstance() de la forma Calendar.getInstance()). Tendréis que hacerlo desde la clase Principal, en la propia definición o en el constructor (que aún no vimos).
Por ejemplo, desde la clase Principal, pondríamos esto:
1    ....
2    Empleado empleado1 = new Empleado();
3    empleado1.fechaNac = Calendar.getInstance();
4    empleado1.fechaNac.set(1992,1,7);
5    ....



  • Queremos guardar información sobre las cuentas bancarias de los clientes de un banco.
Cada cuenta bancaria se compondrá de una persona titular de la cuenta, el saldo en euros, y un código de cuenta (el CCC o Código Cuenta Cliente).
El CCC consta de 20 dígitos: 4 dígitos que representan el código de la entidad, 4 dígitos que representan el código de la oficina, 2 dígitos de control y 10 dígitos con el número de la cuenta.
Los dos dígitos de control se calculan con los valores de la entidad, la oficina y el número de cuenta por lo que no será necesario almacenarlos.
 1 public class Cuenta {
 2     String titural;
 3     double saldo;
 4     String codigoCuenta;    //  Veremos que con los métodos podríamos obtener todos los valores anteriores a partir del código de cuenta por lo que no sería necesario guardarlo separadamente.
 5     String codigoEntidad;
 6     String codigoOficina;
 7     String digitosControl;
 8     String numeroCuenta;
 9     
10 }
  • Queremos realizar un programa que juegue a las cartas, por lo que necesitamos guardar la información de cada una de las cartas que tengan los jugadores. Esta información es la siguiente:
  • Numero: un entero (1 .. 10) que nos representa el número de la carta, siendo el 8 la sota, el 9 el caballo y el 10 el rey.
  • Palo: un tipo de dato numerado que nos representa el palo de la carta (oros,copas,espadas y bastos).
1 public class Carta {
2     enum TiposPalos{OROS, ESPADAS, COPAS, BASTOS};
3     
4     byte numero;    // Con byte llega y sobra
5     TiposPalos palo;
6 }
Cuando desde la clase Principal queráis asignar un valor al atributo 'palo' deberemos hacer referencia a su tipo enum de la siguiente forma:
1    ....
2    Carta carta1 = new Carta();
3    carta1.palo = Carta.TiposPalos.OROS;

Métodos

  • Para poder ver los valores de las variables vamos a definir un método para cada una.
Un método es un conjunto de instrucciones (bloque) identificado por un nombre (nombre del método) el cual puede ser llamado desde cualquier punto del programa, poniendo su nombre.
En ese momento, la secuencia de ejecución del programa cambia, deja de ir secuencialmente y 'salta' al bloque de instrucciones que conforman el método y continúa su ejecución de forma secuencial.
Cuando acaba de ejecutarse el método, 'vuelve' y continúa ejecutando las instrucciones que se encuentran a continuación de la llamada del método.
Prog fund poo 5.jpg
  • La forma de definir un método es el siguiente:
1 [tipo de acceso][static] tipoDato_devuelta nombre_metodo (lista de parámetros)
2 {
3    instrucción 1;
4    instrucción 2;
5    ..............
6    instrucción n;
7 
8 }
  • Veremos más adelante los tipos de acceso así como el modificador 'static'.


  • Los métodos son la forma en cómo un objeto se puede 'comunicar' con otro objeto, es decir, cuando un objeto (objeto1) llama al método de otro objeto (objeto2), estamos enviando un mensaje del objeto1 al objeto2.
El objeto que recibe el mensaje ejecutará el código del método y realizará las acciones programadas, como sacar información por pantalla, devolver un dato, cambiar el estado del objeto,...
Recordar que al inicio de esta parte comentamos que los métodos constituyen la interfaz que permite comunicar a unos objetos con otros.
  • Al final, un programa en la POO consiste en ir creando objetos, llamar a los métodos de los diferentes objetos por parte del usuario o por parte de los mismo objetos y borrar los objetos cuando ya no son necesarios.


  • Cuando declaramos un método tenemos que indicar:
  • Cabecera del método: Contiene el nombre del método, el tipo de dato que va a devolver, una serie de modificadores (como los modificadores de acceso), y opcionalmente una lista de parámetros (representan datos que vamos a enviar al procedimiento cuando lo llamemos).
  • Cuerpo del método: Conjunto de instrucciones que se encuentran entre { }, incluido declaración de variables, las cuales son locales al método (quiere decir que dichas variables sólo son 'visibles' (se puede acceder a ellas) dentro del método. Fuera de él no existen.


En nuestro ejemplo crearemos dos métodos de la forma siguiente:
1 public void impNombre() {    // imprime el nombre
2       System.out.println("O nome é:" + nome);
3 }
  • Ejercicio: Escribir el código del método impEdad.


  • Estos métodos se definirán dentro de la clase Europeo, ya que manejan información que hace referencia a esa clase.
Por lo tanto, el código de clase Europeo completo hasta ahora es el siguiente:
 1 public class Europeo {       // Comienza el código de la clase 
 2   String nombre;
 3   int edad;
 4 
 5    public void impNombre() {   // imprime el nombre 
 6     System.out.println("El nombre es:" + nome);
 7    }
 8 
 9   public void impEdad() {    // imprime la idad
10      System.out.println("La edad es:" + edad);
11   }
12 
13 } // Termina el código de la clase


  • En este punto, tenemos que compilar la clase para obtener los bytescodes actualizado (Europeo.class) (en Netbeans ya lo hace automaticamente al ejecutar la clase Principal).
En la clase Principal deberíamos llamar a estos métodos desde los objetos. Recuerde que un objeto posee todo los atributos y métodos definidos de la clase a la que pertenece.
  • Ejercicio: Cómo llamar a un método desde el objeto para imprimir el nombre y la edad.


  • El código de la clase principal completa es el siguiente:
 1 public class Principal {
 2 
 3     public static void main(String arg[]) {
 4         Europeo europeo1 = new Europeo();
 5         Europeo europeo2 = new Europeo();
 6         Europeo europeo3 = new Europeo();
 7         
 8         
 9         europeo1.nombre = "Angel";
10         europeo1.edad = 20;
11         europeo2.nombre = "Pepe";
12         europeo2.edad = 15;
13         europeo3.nombre = "Jean";
14         europeo3.edad = 17;
15         
16         
17         europeo1.impNombre();
18         europeo1.impEdad();
19         europeo2.impNombre();
20         europeo2.impEdad();
21         europeo3.impNombre();
22         europeo3.impEdad();
23     }
24 }
Si ejecutamos la clase principal una vez compilada, la consola mostrará los datos de los 3 objetos creados.



  • Los métodos en la programación estructurada son conocidos como procedimientos o funciones. Los procedimientos son métodos que no 'devuelven' valores cuando son llamados. Las funciones son métodos que devuelven un valor (pueden devolver un tipo primitivo de dato o una variable de referencia (como un array, un objeto de una clase,...).
Ejemplo de procedimientos:
  • Procedimiento que calcule e imprima la suma de los 100 primeros números.
  • Procedimiento que muestre los 100 primeros números impares.
  • Procedimiento que lea un número entero entre 1 y 100.
Ejemplo de funciones:
  • Una función que devuelva la suma de las 100 primeros números.
  • Una función que devuelva el número mayor entre tres leídos.
  • Una función que indique si un punto está dentro de una circunferencia (devolvería true/false)
  • Una función que devuelva el precio de un producto con el IVA.


  • A partir de la programación orientada a objetos, todos los procedimientos/funciones se denominan métodos y en todos ellos se debe indicar si van a devolver algo.
Si un método no devuelve nada debemos declararlo como: void nombreMétodo(){ }
En este caso, no es necesario indicar la sentencia return, ya que dicha sentencia la vamos a utilizar cuando queramos devolver algo, pero está permitido usarla, de tal forma que al ejecutar dicha sentencia, se saldría del método. Realmente lo que estamos a indicar es que salimos del método y no devolvemos nada a quien nos llamó.
Veamos un ejemplo de un método que no va a devolver nada:
 1 public class Ejemplo{
 2    String nombre;
 3 
 4    /**
 5    *   Lee una cadena de texto hasta que tenga al menos 5 caracteres
 6    */
 7    public void leerNombre(){
 8       do {
 9 
10          nombre = scanner.nextLine();
11       } while(nombre.length()<5);
12 
13    }  // Fin de leerNombre
14 
15 
16    public static void main(String arg[]){
17 
18       leerNombre(); 
19 
20    }
21 
22 }
Nota: La clase Scanner fue explicada en los ejercicios de la UD1.


Si es un método que devuelve algo debemos declararlo como: tipo_dato nombreMétodo(){ }
En este caso, en cualquier parte del código escribiremos la sentencia return valor siendo 'valor' cualquier expresión del tipo de dato que está definido en la definición del método.
Una buena técnica de programación implica ponerlo siempre al final.
 1 public class Ejemplo{
 2    String nombre;
 3    String apellido;
 4 
 5    /**
 6    *   Lee una cadena de texto hasta que tenga al menos 5 caracteres
 7    */
 8    public String leerCadena(){
 9       String cadena="";
10       do {
11 
12          cadena = scanner.nextLine();
13       } while(cadena.length()<5);
14 
15       return cadena;
16    }  // Fin de leerCadema
17 
18 
19    public static void main(String arg[]){
20 
21       nombre = leerCadena(); 
22       apellido = leerCadena();
23 
24    }
25 
26 }
  • Como vemos, cuando llamamos a un método que va a devolver un dato, debemos igualar el resultado de la llamada a una variable del mismo tipo del que está definido en el método.
Lo anterior no siempre es cierto, aunque es lo habitual. Podemos no querer guardar lo que devuelve la función y emplearlo directamente en una sentencia, por ejemplo: System.out.printf("%s",leerCadena());


Ejercicios propuestos

  • A partir de las clases creadas en la sección de 'Clases' => 'Ejercicios propuestos':
  • Se quiere guardar información sobre la flota de coches de una empresa. La información que se quiere guardar es la referida a su matrícula, número de km recorridos, marca y modelo.
Métodos::
  • Se quiere tener un método que saque por pantalla toda la información de un coche.
  • Se quiere tener un método que indique si un determinado coche ha pasado de 1000 km.
  • Se quiere tener un método que en base a una constante (declarada a nivel de clase. que indica cada cuantos kilómetros un coche debe pasar la ITV, informe si un coche debe pasar la ITV o no (devuelve un booleano).
¿ Cómo harías para que una vez pasada la ITV el método que comprueba si debe pasarla no siga informando lo contrario ?


  • Una empresa quiere guardar los datos de los nombre y nóminas de cada empleado, junto con los años de antiguedad en la empresa, su fecha de nacimiento (antes se utilizaba la clase Date) pero ahora, si os fijáis, casi todos sus métodos están 'depracated'. Esto quiere decir que en próximas versiones de Java pueden desaparecer. Ahora miso se hace uso de la clase Calendar), su nombre , dirección y teléfono. Tenéis ejemplos de uso de Calendar en este enlace.
Métodos::
  • Se quiere tener un método que saque por pantalla toda la información personal de un empleado.
  • Se quiere tener un método que saque por pantalla la nómina del empleado. Si la nómina es mayor que el valor de una constante declarada a nivel de clase de valor 1000,25, y su antiguedad es mayor a 5 años, el valor de la nómina que aparece en pantalla se verá incrementada 250,35 euros.
  • Se quiere tener un método que muestre el tiempo de antiguedad del empleado en meses.


  • Queremos guardar información sobre las cuentas bancarias de los clientes de un banco.
Cada cuenta bancaria se compondrá de una persona titular de la cuenta, el saldo en euros, y un código de cuenta (el CCC o Código Cuenta Cliente).
El CCC consta de 20 dígitos: 4 dígitos que representan el código de la entidad, 4 dígitos que representan el código de la oficina, 2 dígitos de control y 10 dígitos con el número de la cuenta.
Los dos dígitos de control se calculan con los valores de la entidad, la oficina y el número de cuenta por lo que no será necesario almacenarlos.
Métodos::
  • Se quiere tener un método que imprima la información del titulo: nombre , número de cuenta y saldo.
  • Se quiere tener un método que descomponga el CCC en sus partes, y las guarde en variables definidas a nivel de clase.
  • Se quiere tener un método que imprima los dígitos del código de entidad.
  • Se quiere tener un método que aplique una comisión de 22,50 euros al saldo de la cuenta. En caso de que el saldo sea inferior a esa cantidad se debe mostrar un mensaje informando del problema y no se sacará ninguna cantidad de la cuenta.


  • Queremos realizar un programa que juegue a las cartas, por lo que necesitamos guardar la información de cada una de las cartas que tengan los jugadores. Esta información es la siguiente:
  • Numero: un entero (1 .. 10) que nos representa el número de la carta, siendo el 8 la sota, el 9 el caballo y el 10 el rey.
  • Palo: un tipo de dato numerado que nos representa el palo de la carta (oros,copas,espadas y bastos).
Métodos::
  • Se quiere tener un método que imprima la información de una carta (número y palo).



Solución Ejercicios propuestos
  • Se quiere guardar información sobre la flota de coches de una empresa. La información que se quiere guardar es la referida a su matrícula, número de km recorridos, marca y modelo.
Métodos::
  • Se quiere tener un método que saque por pantalla toda la información de un coche.
  • Se quiere tener un método que indique si un determinado coche ha pasado de 1000 km.
  • Se quiere tener un método que en base a una constante (declarada a nivel de clase. que indica cada cuantos kilómetros un coche debe pasar la ITV, informe si un coche debe pasar la ITV o no (devuelve un booleano).
Clase Coche:
 1 public class Coche {
 2     final short REVISION_ITV=1000;
 3      
 4     String matricula;
 5     int numKm;
 6     String marca;
 7     String modelo;
 8  
 9     public void imprimirInformacion(){
10         System.out.printf("%nMatricula:%s",matricula);
11         System.out.printf("%nMarca:%s",marca);
12         System.out.printf("%nModelo:%s",modelo);
13         System.out.printf("%nNúmero de kilómetros:%d",numKm);
14     }
15     public void comprobarKm(){
16          if (numKm > 1000) {
17              System.out.println("\nEste coche pasó de 1000 km");
18          }
19     }
20 
21     public boolean comprobarITV(){
22         boolean pasarITV = false;
23         if (numKm > REVISION_ITV){
24             System.out.println("\nEste coche debe pasar la ITV...");
25             pasarITV=true;
26         }
27         return pasarITV;
28     }
29     
30 }
31 ::
Clase Principal:
 1 public class Principal {
 2     
 3     
 4     public static void main(String arg[]){
 5         Coche coche1 = new Coche();
 6         
 7         coche1.marca="OPEL";
 8         coche1.matricula="1234GAZ";
 9         coche1.modelo="ZAFIRA";
10         coche1.numKm=500;
11         
12         coche1.comprobarITV();
13         coche1.numKm+=800;
14         coche1.comprobarITV();   // Aquí muestra el mensaje
15         coche1.comprobarITV();   // Aquí muestra el mensaje
16         
17         
18     }
19 }
20 
21 ::


¿ Cómo harías para que una vez pasada la ITV el método que comprueba si debe pasarla no siga informando lo contrario ?
Hay que crear un método que de alguna forma indique que ya ha pasado la ITV y guarde a que km debe pasar la siguiente.
A ese método le llamamos 'realizarITV'.
Clase Coche:
 1 public class Coche {
 2     final short REVISION_ITV=1000;
 3     int siguienteRevision=REVISION_ITV;
 4     
 5     String matricula;
 6     int numKm;
 7     String marca;
 8     String modelo;
 9  
10     public void imprimirInformacion(){
11         System.out.printf("%nMatricula:%s",matricula);
12         System.out.printf("%nMarca:%s",marca);
13         System.out.printf("%nModelo:%s",modelo);
14         System.out.printf("%nNúmero de kilómetros:%d",numKm);
15     }
16     public void comprobarKm(){
17          if (numKm > 1000) {
18              System.out.println("\nEste coche pasó de 1000 km");
19          }
20     }
21     public boolean comprobarITV(){
22         boolean pasarITV = false;
23         if (numKm > siguienteRevision){
24             System.out.println("\nEste coche debe pasar la ITV...");
25             pasarITV=true;
26         }
27         return pasarITV;
28     }
29     public void realizarITV(){
30         siguienteRevision +=REVISION_ITV;
31         System.out.println("\nITV REALIZADA!!!");
32     }
33     
34 }
35 ::
Clase Principal:
 1 public class Principal {
 2     
 3     
 4     public static void main(String arg[]){
 5         Coche coche1 = new Coche();
 6         
 7         coche1.marca="OPEL";
 8         coche1.matricula="1234GAZ";
 9         coche1.modelo="ZAFIRA";
10         coche1.numKm=500;
11         
12         coche1.comprobarITV();
13         coche1.numKm+=800;
14         if (coche1.comprobarITV()){   // Aquí muestra el mensaje
15             coche1.realizarITV();
16         }
17         coche1.comprobarITV();   // Aquí ya no lo muestra
18         
19         
20     }
21 }
22 ::


  • Una empresa quiere guardar los datos de los nombre y nóminas de cada empleado, junto con los años de antiguedad en la empresa, su fecha de nacimiento (antes se utilizaba la clase Date) pero ahora, si os fijáis, casi todos sus métodos están 'depracated'. Esto quiere decir que en próximas versiones de Java pueden desaparecer. Ahora miso se hace uso de la clase Calendar), su nombre , dirección y teléfono. Tenéis ejemplos de uso de Calendar en este enlace.
Métodos::
  • Se quiere tener un método que saque por pantalla toda la información personal de un empleado.
  • Se quiere tener un método que saque por pantalla la nómina del empleado. Si la nómina es mayor que el valor de una constante declarada a nivel de clase de valor 1000,25, y su antiguedad es mayor a 5 años, el valor de la nómina que aparece en pantalla se verá incrementada 250,35 euros.
  • Se quiere tener un método que muestre el tiempo de antiguedad del empleado en meses.
Clase Empleado
 1 public class Empleado {
 2     final double VALOR_NOMINA=1000.25;
 3     
 4     String nombre;
 5     String direccion;
 6     String telefono;    // A pesar que es un número no vamos a realizar operaciones matemáticas
 7     double nomina;
 8     byte antiguedad;
 9     Calendar fechaNac = Calendar.getInstance();
10     
11     public void imprimirInformacion(){
12         System.out.printf("Nombre:%s%n",nombre);
13         System.out.printf("Direccion:%s%n",direccion);
14         System.out.printf("Telefono:%s%n",telefono);
15         System.out.printf("Nómina:%,.2f%n",nomina);
16         System.out.printf("Data Nacemento:%s%n",fechaNac.getTime());
17     }
18     
19     public void imprimirNomina(){
20         if (nomina > VALOR_NOMINA && antiguedad>5){
21             System.out.printf("%nTu nómina es:%,.2f",nomina+250.35);
22         }
23     }
24     
25     public void imprimirAntiguedadMeses(){
26         System.out.printf("%nAntiguedad en meses:%d",antiguedad*12);
27     }
28     
29 }
30 ::
Clase Principal
 1 public class Principal {
 2     
 3     
 4     public static void main(String arg[]){
 5 
 6         Empleado empleado1 = new Empleado();
 7         
 8         empleado1.nombre = "Pepe";
 9         empleado1.direccion="Avda. Castelao";
10         empleado1.nomina=2000.25;
11         empleado1.antiguedad=7;
12         
13         empleado1.imprimirInformacion();
14         empleado1.imprimirNomina();
15         empleado1.imprimirAntiguedadMeses();
16     }
17 }
18 
19 ::


  • Queremos guardar información sobre las cuentas bancarias de los clientes de un banco.
Cada cuenta bancaria se compondrá de una persona titular de la cuenta, el saldo en euros, y un código de cuenta (el CCC o Código Cuenta Cliente).
El CCC consta de 20 dígitos: 4 dígitos que representan el código de la entidad, 4 dígitos que representan el código de la oficina, 2 dígitos de control y 10 dígitos con el número de la cuenta.
Los dos dígitos de control se calculan con los valores de la entidad, la oficina y el número de cuenta por lo que no será necesario almacenarlos.
Métodos::
  • Se quiere tener un método que imprima la información del titulo: nombre , número de cuenta y saldo.
  • Se quiere tener un método que descomponga el CCC en sus partes, y las guarde en variables definidas a nivel de clase. Debes comprobar que el CCC esté establecido (recuerda que String es un tipo de dato de referencia y por lo tanto puede valer null) y que la longitud sea de 20 caracteres.
  • Se quiere tener un método que imprima los dígitos del código de entidad.
  • Se quiere tener un método que aplique una comisión de 22,50 euros al saldo de la cuenta. En caso de que el saldo sea inferior a esa cantidad se debe mostrar un mensaje informando del problema y no se sacará ninguna cantidad de la cuenta.
Clase Cuenta
 1 public class Cuenta {
 2     final double COMISION=22.50;
 3     
 4     String titular;
 5     double saldo;
 6     String codigoCuenta;    //  Veremos que con los métodos podríamos obtener todos los valores anteriores a partir del código de cuenta por lo que no sería necesario guardarlo separadamente.
 7     String codigoEntidad;
 8     String codigoOficina;
 9     String digitosControl;
10     String numeroCuenta;
11     
12     public void imprimirInformacion(){
13         System.out.printf("%nTitular:%s",titular);
14         System.out.printf("%nCódigo de cuenta:%s",codigoCuenta);
15         System.out.printf("%nSaldo:%,.2f",saldo);
16     }
17     public void descomponerCCC(){
18         if ((codigoCuenta!=null) && (codigoCuenta.length()==20)){
19             codigoEntidad = codigoCuenta.substring(0,4);
20             codigoOficina = codigoCuenta.substring(4,8);
21             digitosControl = codigoCuenta.substring(8,10);
22             numeroCuenta = codigoCuenta.substring(10);
23         }
24     }
25     public void imprimirDigitosControl(){
26         if ((digitosControl!=null) && (digitosControl.length()==4)){
27            System.out.printf("%nDígitos de control:%s",digitosControl);
28         }
29     }
30     public void aplicarComision(){
31         if (saldo<COMISION){
32             System.out.printf("%nNo se puede aplicar la comisión. Tienes un saldo de %.2f",saldo);
33         }
34         else{
35             saldo -= COMISION;
36             System.out.println("\nComisión aplicada");
37         }
38     }
39 }
40 ::
Clase Principal
 1 public class Principal {
 2     
 3     
 4     public static void main(String arg[]){
 5         
 6         Cuenta cuenta1 = new Cuenta();
 7         cuenta1.titular="ANGEL";
 8         cuenta1.saldo = 10.22;
 9         cuenta1.codigoCuenta="11112222331234567890";
10         
11         cuenta1.imprimirInformacion();
12         cuenta1.aplicarComision();
13         cuenta1.descomponerCCC();
14         
15         System.out.printf("\nCodigo entidad:%s",cuenta1.codigoEntidad);
16         System.out.printf("\nCodigo oficina:%s",cuenta1.codigoOficina);
17         cuenta1.imprimirDigitosControl();
18         System.out.printf("\nNúmero de cuenta:%s",cuenta1.numeroCuenta);
19 
20     }
21 }
22 ::


  • Queremos realizar un programa que juegue a las cartas, por lo que necesitamos guardar la información de cada una de las cartas que tengan los jugadores. Esta información es la siguiente:
  • Numero: un entero (1 .. 10) que nos representa el número de la carta, siendo el 8 la sota, el 9 el caballo y el 10 el rey.
  • Palo: un tipo de dato numerado que nos representa el palo de la carta (oros,copas,espadas y bastos).
Métodos::
  • Se quiere tener un método que imprima la información de una carta (número y palo).


Clase Carta
 1 public class Carta {
 2     enum TiposPalos{OROS, ESPADAS, COPAS, BASTOS};
 3     
 4     byte numero;    // Con byte llega y sobra
 5     TiposPalos palo;
 6     
 7     public void imprimirInformacion(){
 8         System.out.printf("%nPalo: %s",palo);
 9         System.out.printf("%nNumero:%d",numero);
10     }
11 }
12 ::
Clase Principal
 1 public class Principal {
 2     
 3     
 4     public static void main(String arg[]){
 5 
 6         Carta carta1 = new Carta();
 7         
 8         carta1.palo=Carta.TiposPalos.BASTOS;
 9         carta1.numero=5;
10         
11         carta1.imprimirInformacion();
12 
13     }
14 }
15 ::



Pasando parámetros a los métodos

  • En algunas ocasiones necesitamos enviar a un método cierta información para que pueda realizar su código.
Por ejemplo, un método que aumente el saldo de una cuenta tendrá que tener como información la cantidad de dinero que queremos sumar.
Esa información la vamos a 'enviar' desde donde realizamos la llamada al método. Dentro de la definición del método indicaremos el nombre y tipo de dato del parámetro.
Para nosotros el parámetro va a ser una variable definida a nivel de método, por lo tanto, sólo será visible 'dentro del bloque de código' que conforma el método (podremos acceder a su valor).


  • Por ejemplo:
1 public class Cuenta {
2    double saldo;
3 
4    public void incrementarSaldo(float cantidad){
5 
6       saldo += cantidad;
7    }
8 
9 }
1 public class Principal {
2 
3    public static void main (String arg[]) {
4 
5       Cuenta cuenta = new Cuenta();
6       cuenta.incrementarSaldo(1000);
7    }
8 
9 }


  • Como vemos podemos enviar como dato cualquier expresión que se corresponda con el tipo de dato que espera recibir el método.
Podríamos enviar el valor de una variable, una constante,....
 1 public class Principal {
 2    final static int INCREMENTO=1000;
 3 
 4    public static void main (String arg[]) {
 5 
 6       Cuenta cuenta = new Cuenta();
 7 
 8 
 9       cuenta.incrementarSaldo(INCREMENTO);
10    }
11 
12 }


  • La lista de parámetros de un método va situada detrás de su nombre, entre los paréntesis de la forma: tipo_dato1 nombre_par1, tipo_dato2 nombre_par2,....
Un método puede no tener parámetros (después de su nombre se pondría (), como vimos anteriormente) o puede llevar múltiples parámetros separados por coma.
 1 public class Circulo {
 2   int posX,posY;
 3 
 4   public void establecerPosicion(int x, int y){
 5 
 6      posX = x;
 7      posY = y;
 8 
 9   }
10  
11 }


  • Los tipos de datos que se pueden enviar pueden ser tipos de datos primitivos o de referencia (como los objetos y arrays).
Aquí hay que profundizar un poco.
Cuando enviamos un dato primitivo a un método, realmente el método está haciendo una 'copia' del valor enviado y lo está guardando en una variable que nosotros identificamos como 'parámetro'.
Este tipo de pase de datos se denomina por valor, de tal forma que la modificación en el valor del dato enviado no modifica el valor original del mismo.
Veamos un ejemplo:
 1 public class Cuenta {
 2    double saldo;
 3 
 4    /**
 5    * Quita una cantidad múltiplo de 20
 6    */
 7    public void retirarSaldo(float cantidad){
 8 
 9       if (cantidad%20!=0){
10           cantidad+=cantidad%20;
11       }
12       saldo -= cantidad;
13    }
14 
15    public void imprimirSaldo(){
16        System.out.printf("%nSaldo:%,.2f",saldo);
17    }
18 
19 }
 1 public class Principal {
 2 
 3    public static void main (String arg[]) {
 4 
 5         Cuenta cuenta = new Cuenta();
 6         int cantidad = 30;
 7 
 8         cuenta.saldo=100;
 9         cuenta.retirarSaldo(cantidad);
10         cuenta.imprimirSaldo();
11 
12         // A la salida de la llamada al método, la variable cantidad sigue valiendo 30, a pesar que fue incrementada en la llamada al método
13         System.out.printf("%nCantidad:%d", cantidad);
14    }
15 
16 }


Sin embargo no pasa lo mismo con los tipos de datos de referencia.
Cuando enviamos uno de ellos en forma de 'variable' cualquier cambio dentro del método provoca que se cambie su valor original. Es lo que se denomina paso de datos por referencia, ya que lo que recibe el parámetro del método es una 'referencia' a la zona de memoria donde está definida la variable.
Por ejemplo:
 1 public class ClaseA{
 2   int a;
 3 
 4   public void copiarClase(ClaseA clase){
 5 
 6     clase.a = a;
 7   }
 8 
 9   public void imprimirValor(){
10      System.out.printf("%nValor de a:%d",a);
11   }
12 }
 1 public class Principal {
 2 
 3    public static void main (String arg[]) {
 4       ClaseA clase1 = new ClaseA();
 5       ClaseA clase2 = new ClaseA();
 6 
 7       clase1.a = 5;
 8       clase2.a = 10;
 9 
10       clase1.copiarClase(clase2);
11   
12       clase2.imprimirValor();   // IMPRIME 5, no 10
13    }
14 
15 
16 }


  • Seguimos con las condiciones que deben cumplir los parámetros:
No pueden coincidir de nombre una variable local definida en el método y el nombre de un parámetro.
No se permite que haya dos parámetros con el mismo nombre.


  • Si un parámetro tiene el mismo nombre que un atributo, podemos hacer referencia al atributo utilizando la palabra clave 'this' de la forma: this.nombre_atributo
Veremos más adelante ejemplos de uso de this, que representa 'la instancia actual'.



Ejercicios propuestos
  • Ejercicios basados en las clases definidas en las secciones anteriores....
  • Se quiere guardar información sobre la flota de coches de una empresa. La información que se quiere guardar es la referida a su matrícula, número de km recorridos, marca y modelo.
Métodos::
  • Crea un método que asigne un valor a cada uno de los atributos.
En el caso de los km debes asegurarte que el valor es positivo mayor que 0. El método devolverá un booleano indicando si la operación fue correcta o no.
  • Crea un método que modifique (sumando al valor que tenga el dato enviado) el valor de los km recorridos. Siempre tiene que ser un valor positivo. Este método debe llamar al método que comprueba si se debe pasar la ITV o no.


  • Una empresa quiere guardar los datos de los nombre y nóminas de cada empleado, junto con los años de antiguedad en la empresa, su fecha de nacimiento (antes se utilizaba la clase Date) pero ahora, si os fijáis, casi todos sus métodos están 'depracated'. Esto quiere decir que en próximas versiones de Java pueden desaparecer. Ahora miso se hace uso de la clase Calendar), su nombre , dirección y teléfono. Tenéis ejemplos de uso de Calendar en este enlace.
Métodos::
  • Crea un método que asigne un valor a cada uno de los atributos.
En la fecha de nacimiento se enviará una cadena con el formato dd/mm/yyyy. Se comprobará que la cadena tenga el formato correcto y que el valor enviado no de como resultado que la edad sea menor que 18.
En la nómina se verificará que sea mayor que 0. En caso contrario se asignará el valor 0.
En la antiguedad se verificará que sea mayor que 0. En caso contrario se asignará el valor 0.
  • Crea un método que aumente la antiguedad del empleado. Cada llamada a este método incrementará en uno el número de años.
  • Crea un método que aumente la nómina en la cantidad enviada. Se asegurará que siempre sea positiva. En caso contrario no se sumará nada.


  • Queremos guardar información sobre las cuentas bancarias de los clientes de un banco.
Cada cuenta bancaria se compondrá de una persona titular de la cuenta, el saldo en euros, y un código de cuenta (el CCC o Código Cuenta Cliente).
El CCC consta de 20 dígitos: 4 dígitos que representan el código de la entidad, 4 dígitos que representan el código de la oficina, 2 dígitos de control y 10 dígitos con el número de la cuenta.
Los dos dígitos de control se calculan con los valores de la entidad, la oficina y el número de cuenta por lo que no será necesario almacenarlos.
Métodos::
  • Crea un método que asigne un valor a cada uno de los atributos.
El método que asigna el número de cuenta verificará que el formato es correcto. Dicho método llamará al método que descompone el número de cuenta en sus partes. Devolverá true/false dependiendo de si el formato fue correcto o no.
  • Modifica el método de la comisión, de tal forma que si el saldo de la cuenta es inferior a 0, debe de aplicarse una comisión de 22,50 euros definida a nivel de clase en forma de constante.
  • Crea un método para operar sobre la cuenta, enviando un cantidad positiva o negativa (si queremos ingresar o disponer de dinero). En el caso de disponer de dinero cuando el saldo es positivo pero es inferior a la cantidad a disponer, se quitará el dinero del saldo (pasando a ser negativo) y se llamará al método de la comisión modificado en el paso anterior.


  • Queremos guardar información sobre las bombillas de una casa.
Cada bombilla estará identificada por su lugar y posición en la casa.
Los lugares pueden ser: SALON, COMEDOR, COCINA y BANHO y su posición será un número.
Las bombillas van a poder estar encendidas o apagadas. Inicialmente estarán apagadas.
Crea un método que imprima la información de la bombilla.
Crea un método para asignar un valor a cada uno de los atributos.
En el caso de la posición esta debe ser mayor que 0. En caso de que sea 0, se asignará el valor 1.
Crea un método para apagar o encender una bombilla.
Desde la clase Principal crea 3 bombillas y modifica/imprime sus valores.




Solución Ejercicios propuestos
  • Se quiere guardar información sobre la flota de coches de una empresa. La información que se quiere guardar es la referida a su matrícula, número de km recorridos, marca y modelo.
Métodos::
  • Crea un método que asigne un valor a cada uno de los atributos.
En el caso de los km debes asegurarte que el valor es positivo mayor que 0. El método devolverá un booleano indicando si la operación fue correcta o no.
  • Crea un método que modifique (sumando al valor que tenga el dato enviado) el valor de los km recorridos. Siempre tiene que ser un valor positivo. Este método debe llamar al método que comprueba si se debe pasar la ITV o no.
Clase Coche
 1 public class Coche {
 2     final short REVISION_ITV=1000;
 3     int siguienteRevision=REVISION_ITV;
 4     
 5     String matricula;
 6     int numKm;
 7     String marca;
 8     String modelo;
 9  
10     public void setValores(String matricula, int numKm, String marca, String modelo){
11         setMatricula(matricula);
12         setNumKm(numKm);
13         setMarca(marca);
14         setModelo(modelo);
15     }
16     public void setMatricula(String matricula){
17         this.matricula = matricula;
18     }
19     public boolean setNumKm(int numKm){
20         boolean devuelto = false;
21         if (numKm > 0){
22             this.numKm = numKm;
23             devuelto = true;
24         }
25         return devuelto;
26     }
27     public void setMarca(String marca){
28         this.marca = marca;
29     }
30     public void setModelo(String modelo){
31         this.modelo = modelo;
32     }
33     
34     public void aumentarKm(int numKm){
35         if (numKm >0){
36             this.numKm += numKm;
37             comprobarITV();
38         }
39     }
40     public void imprimirInformacion(){
41         System.out.printf("%nMatricula:%s",matricula);
42         System.out.printf("%nMarca:%s",marca);
43         System.out.printf("%nModelo:%s",modelo);
44         System.out.printf("%nNúmero de kilómetros:%d",numKm);
45     }
46     public void comprobarKm(){
47          if (numKm > 1000) {
48              System.out.println("\nEste coche pasó de 1000 km");
49          }
50     }
51     public boolean comprobarITV(){
52         boolean pasarITV = false;
53         if (numKm > siguienteRevision){
54             System.out.printf("\nEste coche \"%s\" debe pasar la ITV...",matricula);
55             pasarITV=true;
56         }
57         return pasarITV;
58     }
59     public void realizarITV(){
60         siguienteRevision +=REVISION_ITV;
61         System.out.println("\nITV REALIZADA!!!");
62     }
63     
64 }
Clase Principal
 1 public class Principal {
 2     
 3     
 4     public static void main(String arg[]){
 5         Coche coche1 = new Coche();
 6         Coche coche2 = new Coche();
 7         
 8         coche1.setMarca("OPEL");
 9         coche1.setMatricula("1234GAZ");
10         coche1.setModelo("ZAFIRA");
11         coche1.setNumKm(500);
12         
13         coche2.setValores("6765AQS", 1210, "FORD", "MONDEO");
14         
15         coche1.imprimirInformacion();
16         coche2.imprimirInformacion();
17         
18         coche1.aumentarKm(1200);
19         
20     }
21 }



  • Una empresa quiere guardar los datos de los nombre y nóminas de cada empleado, junto con los años de antiguedad en la empresa, su fecha de nacimiento (antes se utilizaba la clase Date) pero ahora, si os fijáis, casi todos sus métodos están 'depracated'. Esto quiere decir que en próximas versiones de Java pueden desaparecer. Ahora miso se hace uso de la clase Calendar), su nombre , dirección y teléfono. Tenéis ejemplos de uso de Calendar en este enlace.
Métodos::
  • Crea un método que asigne un valor a cada uno de los atributos.
En la fecha de nacimiento se enviará una cadena con el formato dd/mm/yyyy. Se comprobará que la cadena tenga el formato correcto y que el valor enviado no de como resultado que la edad sea menor que 18.
En la nómina se verificará que sea mayor que 0. En caso contrario se asignará el valor 0.
En la antiguedad se verificará que sea mayor que 0. En caso contrario se asignará el valor 0.
  • Crea un método que aumente la antiguedad del empleado. Cada llamada a este método incrementará en uno el número de años.
  • Crea un método que aumente la nómina en la cantidad enviada. Se asegurará que siempre sea positiva. En caso contrario no se sumará nada.
Clase Empleado
  1 /*
  2  * To change this license header, choose License Headers in Project Properties.
  3  * To change this template file, choose Tools | Templates
  4  * and open the template in the editor.
  5  */
  6 package EjerciciosPOO.Ejercicio2;
  7 
  8 import java.util.Calendar;
  9 
 10 /**
 11  *
 12  * @author clase
 13  */
 14 public class Empleado {
 15     final double VALOR_NOMINA=1000.25;
 16     
 17     String nombre;
 18     String direccion;
 19     String telefono;    // A pesar que es un número no vamos a realizar operaciones matemáticas
 20     /** Nomina del empleado. 
 21      * Siempre positiva 
 22      */
 23     double nomina;  
 24     byte antiguedad;
 25     Calendar fechaNac = Calendar.getInstance();
 26     
 27 
 28     public void setValores(String nombre, String direccion, String telefono,double nomina,byte antiguedad,String fechaNac){
 29         setNombre(nombre);
 30         setDireccion(direccion);
 31         setTelefono(telefono);
 32         setNomina(nomina);
 33         setAntiguedad(antiguedad);
 34         setFechaNac(fechaNac);
 35     }
 36     
 37     public boolean setFechaNac(String fechaNac){
 38         boolean saida = true;
 39         if ((fechaNac.length()==10) && (fechaNac.charAt(2)=='/') && (fechaNac.charAt(5)=='/')){
 40             try {
 41                 this.fechaNac.set(Calendar.DAY_OF_MONTH,Integer.parseInt(fechaNac.substring(0,2)));
 42                 this.fechaNac.set(Calendar.MONTH,Integer.parseInt(fechaNac.substring(3,5))-1);  // Recuerda que los meses empiezan en 0
 43                 this.fechaNac.set(Calendar.YEAR,Integer.parseInt(fechaNac.substring(6)));
 44             }
 45             catch(Exception e){
 46                 System.out.println("\nFormato Fecha Incorrecta");   // En un programa non se pon a saída de erro nesta parte- O faríamos dende Principal
 47                 saida = false;
 48             }
 49         }
 50         else{
 51             System.out.println("\nFormato Fecha Incorrecta");   // En un programa non se pon a saída de erro nesta parte- O faríamos dende Principal
 52             saida = false;
 53         }
 54         return saida;
 55     }
 56     
 57     public void setNombre(String nombre){
 58         this.nombre = nombre;
 59     }
 60     public void setDireccion(String direccion){
 61         this.direccion = direccion;
 62     }
 63     public void setTelefono(String telefono){
 64         this.telefono = telefono;
 65     }
 66     /**
 67      * @param nomina Nomina del empleado. Debe ser positiva mayor que 0.
 68      * {@link Empleado#nomina}
 69      */
 70     public void setNomina(double nomina){
 71         if (nomina<0){
 72             nomina = 0;     // Recordar que los parámetros de tipo primitivo se envían 'por valor'
 73         }
 74         this.nomina = nomina;
 75     }
 76     public void setAntiguedad(byte antiguedad){  
 77         if ((antiguedad<0) || (antiguedad>127)){  // Máximo valor que puede guardar un byte
 78             antiguedad = 0; // Recordar que los parámetros de tipo primitivo se envían 'por valor'
 79         }
 80         this.antiguedad = antiguedad;
 81     }
 82     
 83     public void incrementarAntiguedad(){
 84         if (antiguedad < 127)   // No dejamos que aumente de 127 años que es lo que soporta un byte
 85             antiguedad++; 
 86     }
 87     public void aumentarNomina(double cantidad){
 88         if (cantidad<0) {
 89             cantidad = 0;   // Recordar que los parámetros de tipo primitivo se envían 'por valor'
 90         }
 91         nomina += cantidad;
 92     }
 93     public void imprimirInformacion(){
 94         System.out.printf("Nombre:%s%n",nombre);
 95         System.out.printf("Direccion:%s%n",direccion);
 96         System.out.printf("Telefono:%s%n",telefono);
 97         System.out.printf("Nómina:%,.2f%n",nomina);
 98         System.out.printf("Data Nacemento:%s%n",fechaNac.getTime());
 99     }
100     
101     public void imprimirNomina(){
102         if (nomina > VALOR_NOMINA && antiguedad>5){
103             System.out.printf("%nTu nómina es:%,.2f",nomina+250.35);
104         }
105     }
106     
107     public void imprimirAntiguedadMeses(){
108         System.out.printf("%nAntiguedad en meses:%d",antiguedad*12);
109     }
110     
111 }
Clase Principal
 1 public class Principal {
 2     
 3     
 4     public static void main(String arg[]){
 5 
 6         Empleado empleado1 = new Empleado();
 7         
 8         empleado1.setValores("Pepe","Avda. Castelao","981111111",1000.25,(byte)2,"01/02/2000");
 9         empleado1.imprimirInformacion();
10         empleado1.incrementarAntiguedad();
11         empleado1.aumentarNomina(100.25);
12         empleado1.imprimirInformacion();
13 
14     }
15 }


  • Queremos guardar información sobre las cuentas bancarias de los clientes de un banco.
Cada cuenta bancaria se compondrá de una persona titular de la cuenta, el saldo en euros, y un código de cuenta (el CCC o Código Cuenta Cliente).
El CCC consta de 20 dígitos: 4 dígitos que representan el código de la entidad, 4 dígitos que representan el código de la oficina, 2 dígitos de control y 10 dígitos con el número de la cuenta.
Los dos dígitos de control se calculan con los valores de la entidad, la oficina y el número de cuenta por lo que no será necesario almacenarlos.
Métodos::
  • Crea un método que asigne un valor a cada uno de los atributos.
El método que asigna el número de cuenta verificará que el formato es correcto. Dicho método llamará al método que descompone el número de cuenta en sus partes. Devolverá true/false dependiendo de si el formato fue correcto o no.
  • Modifica el método de la comisión, de tal forma que si el saldo de la cuenta es inferior a 0, debe de aplicarse una comisión de 22,50 euros definida a nivel de clase en forma de constante.
  • Crea un método para operar sobre la cuenta, enviando un cantidad positiva o negativa (si queremos ingresar o disponer de dinero). En el caso de disponer de dinero cuando el saldo es positivo pero es inferior a la cantidad a disponer, se quitará el dinero del saldo (pasando a ser negativo) y se llamará al método de la comisión modificado en el paso anterior.
Clase Cuenta
 1 public class Cuenta {
 2     final double COMISION=22.50;
 3     
 4     String titular;
 5     double saldo;
 6     String codigoCuenta;    //  Veremos que con los métodos podríamos obtener todos los valores anteriores a partir del código de cuenta por lo que no sería necesario guardarlo separadamente.
 7     String codigoEntidad;
 8     String codigoOficina;
 9     String digitosControl;
10     String numeroCuenta;
11     
12     public void asignarValores(String titular,double saldo,String codigoCuenta){
13         setTitular(titular);
14         setSaldo(saldo);
15         setCodigoCuenta(codigoCuenta);
16     }
17     public void setTitular(String titular){
18         this.titular = titular;
19     }
20     public void setSaldo(double saldo){
21         this.saldo = saldo;
22     }
23     public boolean setCodigoCuenta(String codigoCuenta){
24         boolean devolver = false;
25         if ((codigoCuenta!=null) && (codigoCuenta.length()==20)){
26             this.codigoCuenta = codigoCuenta;
27             descomponerCCC();
28             devolver = true;
29         }
30         
31         return devolver;
32     }
33     
34     /* Como este método es publico se puede llamar directamente desde principal
35     por lo que dejamos el if. 
36     Cuando veamos los modificadores de acceso, haremos que no se pueda llamar desde Principal y como solo
37     se va a poder llamar desde setCodigoCuenta se podría quitar
38     */
39     public void descomponerCCC(){
40         if ((codigoCuenta!=null) && (codigoCuenta.length()==20)){
41             codigoEntidad = codigoCuenta.substring(0,4);
42             codigoOficina = codigoCuenta.substring(4,8);
43             digitosControl = codigoCuenta.substring(8,10);
44             numeroCuenta = codigoCuenta.substring(10);
45         }
46     }
47 
48     public void aplicarComision(){
49         if (saldo<0){
50             saldo -= COMISION;
51             System.out.println("\nComisión aplicada");
52         }
53     }
54 
55     public void operarCuenta(double cantidad){
56         saldo += cantidad;
57         aplicarComision();
58     }
59     
60     public void imprimirInformacion(){
61         System.out.printf("%nTitular:%s",titular);
62         System.out.printf("%nCódigo de cuenta:%s",codigoCuenta);
63         System.out.printf("%nSaldo:%,.2f",saldo);
64     }
65     public void imprimirDigitosControl(){
66         System.out.printf("%nDígitos de control:%s",digitosControl);
67     }
68 }
Clase Principal
 1 public class Principal {
 2     
 3     
 4     public static void main(String arg[]){
 5         
 6         Cuenta cuenta1 = new Cuenta();
 7         cuenta1.asignarValores("ANGEL",100, "11112222331234567890");
 8         cuenta1.imprimirInformacion();
 9         
10         cuenta1.operarCuenta(500.50);
11         cuenta1.imprimirInformacion();
12         cuenta1.operarCuenta(-700.50);
13         cuenta1.imprimirInformacion();
14         
15         
16 
17     }
18 }


  • Queremos guardar información sobre las bombillas de una casa.
Cada bombilla estará identificada por su lugar y posición en la casa.
Los lugares pueden ser: SALON, COMEDOR, COCINA y BANHO y su posición será un número.
Las bombillas van a poder estar encendidas o apagadas. Inicialmente estarán apagadas.
Crea un método que imprima la información de la bombilla.
Crea un método para asignar un valor a cada uno de los atributos.
En el caso de la posición esta debe ser mayor que 0. En caso de que sea menor o igual a cero, se asignará el valor 1.
Crea un método para apagar o encender una bombilla.
Desde la clase Principal crea 3 bombillas y modifica/imprime sus valores.
Clase Bombilla
 1 public class Bombilla {
 2     enum Lugar{SALON,COMEDOR,COCINA,BANHO}
 3     
 4     Lugar lugar;
 5     byte posicion;
 6     
 7     boolean estado; // Al ser variable miembro se inicaliza a false. No hace falta ponerlo
 8     
 9     public void asignarValores(Lugar lugar,byte posicion,boolean estado){
10         setEstado(estado);
11         setLugar(lugar);
12         setPosicion(posicion);
13     }
14     public void setLugar(Lugar lugar){
15         this.lugar = lugar;
16     }
17     public void setPosicion(byte posicion){  
18         if ((posicion <= 0) || (posicion>127)){
19             posicion = 1;
20         }
21         this.posicion = posicion;
22     }
23     public void setEstado(boolean estado){
24         this.estado = estado;
25     }
26     public void imprimirInformacion(){
27         System.out.printf("%nBombilla en %s %d",lugar,posicion);
28         System.out.printf("%nEstado %b",estado);
29     }
30     public void cambiarEstado(boolean estado){
31         this.estado = estado;
32     }
33 
34 }
Clase Principal
 1 public class Principal {
 2     
 3     
 4     public static void main(String arg[]){
 5 
 6         Bombilla bombilla1 = new Bombilla();
 7         Bombilla bombilla2 = new Bombilla();
 8         Bombilla bombilla3 = new Bombilla();
 9         
10         bombilla1.asignarValores(Bombilla.Lugar.SALON, (byte)1, true);
11         bombilla2.asignarValores(Bombilla.Lugar.COCINA, (byte)1, false);
12         bombilla3.asignarValores(Bombilla.Lugar.SALON, (byte)2, false);
13         bombilla1.imprimirInformacion();
14         bombilla2.imprimirInformacion();
15         bombilla3.imprimirInformacion();
16         
17         bombilla1.setEstado(false);
18         bombilla2.setEstado(true);
19         bombilla3.setEstado(true);
20 
21         bombilla1.imprimirInformacion();
22         bombilla2.imprimirInformacion();
23         bombilla3.imprimirInformacion();
24 
25         
26     }
27 }



Características POO

  • Todo lenguaje de POO, como Java, tiene 3 características principales:
  • Herencia
  • Polimorfismo
  • Encapsulación
  • Definiremos cada uno de ellos y continuaremos con el ejemplo anterior.



Herencia

  • La primera característica es HERENCIA:
Podemos establecer una relación entre 2 clases de tal manera que una de ellas "herede" todo lo que tiene la otra.
La herencia significa que no tenemos que redefinir todo el código, por lo que es reutilizable.
Pueden heredar tanto métodos como variables.
Gráficamente, podría ser algo como esto:
Prog fund poo 6.jpg


  • Como podemos ver, creamos dos subclases de la clase Europeo.
Esto conduce tanto la clase Espanol como la Francés.
  • La clase Frances hereda los métodos y variables de la clase Europeo, es decir, es como si ya dentro de su código se escribieran las definiciones de los atributos y métodos de clase Europeo.
  • Más adelante veremos que no todo es heredado y que existen restricciones.
También se debe tener en cuenta que en Java no se permite la herencia múltiple, es decir, que una clase hereda al mismo tiempo de dos o más clases.
Gráficamente sería lo siguiente:
Prog fund poo 7.jpg
  • Desde el punto de vista de la clase Europeo, la clase Espanol es una subclase de la clase Europeo.
  • Desde el punto de vista de la clase Espanol, la clase Europeo es una superclase de la clase de Espanol.


  • ¿ Cómo Java puede indicar que una clase es una subclase de otra ?
A través de la palabra clave extends.
  • En nuestro caso, supongamos que queremos almacenar la información referida al NIF (en el caso español) y al LIT (en el

caso francés).

También queremos tener los métodos que imprimen esta información, de nombres impNif e ImpLit.
  • Esta información pertenece a la clase Espanol y a la clase Frances, no pertenece a la clase Europeo.
Es por eso que deberíamos definir una clase con todo lo que la clase Europeo tenía más sus propios atributos.
En lugar de eso, crearemos dos subclases: Espanol y Frances que son subclases de la clase Europeo.
Por lo tanto, crearemos dos archivos más, uno para la clase de Espanol y el otro para la clase de Frances.
  • El nombre de archivo Espanol.java tendría lo siguiente:
1 public class Espanol extends Europeo {
2 
3     String nif;
4 
5     public void impNif() {
6         System.out.println("Nif:" + nif);
7     }
8 }


  • Ejercicio: Hacer el código de la clase Francés.




Solución:
1 public class Frances extends Europeo{
2 
3     String lit;
4 
5     public void impLit() {
6         System.out.println("Lit:" + lit);
7     }
8     
9 }


  • Ejercicio: Compilar las dos clases en el mismo fichero físico. ¿ Qué nombre debe de tener ? ¿ Cuántas clases públicas hay dentro del fichero ?


  • Ahora debemos modificar la clase Principal, ya que tenemos más información que se debe ajustar a la situacion:
Prog fund poo 8.jpg


  • Resulta que europeo1 y europeo2 son españoles, por lo tanto, deben ser objetos de la clase de Espanol, mientras que europeo3 es francés, por lo que debe ser un objeto de la clase de Frances.
Los cambios en la clase Principal son los siguientes:
  • Dos de los objetos se convierten en objetos de la clase de Espanol y uno en un objeto de la clase de Frances:
Espanol europeo1 = new Espanol();
Espanol europeo2 = new Espanol();
Frances europeo3 = new Frances();
  • El resto del código se mantiene.
  • Ejercicio: ¿ Por qué el objeto europeo1 puede utilizar los métodos y variables de la clase Europeo ?





  • Solución: Porque la clase a la que pertenece (Espanol) se define como una subclase de la clase Europeo y por lo tanto hereda todos los métodos y atributos de la clase Europeo.
  • Ejercicio: Los nuevos objetos tienen atributos propios (Nif y Lit)
Asigna un valor e mándalos imprimir.




  • Solución:
 1 public class Principal {
 2 
 3     public static void main(String arg[]) {
 4         Espanol europeo1 = new Espanol();
 5         Espanol europeo2 = new Espanol();
 6         Frances europeo3 = new Frances();
 7         
 8         
 9         europeo1.nombre = "Angel";
10         europeo1.edad = 20;
11         europeo2.nombre = "Pepe";
12         europeo2.edad = 15;
13         europeo3.nombre = "Jean";
14         europeo3.edad = 17;
15         
16         
17         europeo1.impNombre();
18         europeo1.impEdad();
19         europeo2.impNombre();
20         europeo2.impEdad();
21         europeo3.impNombre();
22         europeo3.impEdad();
23         
24         europeo1.nif="1111111A";
25         europeo2.nif="2222222A";
26         europeo3.lit="XA1111";
27         
28         europeo1.impNif();
29         europeo2.impNif();
30         europeo3.impLit();
31 
32         
33     }
34 }


  • Como podemos ver, el objeto europeo3 no puede llamar al método impNif, ya que este es un método de clase Espanol.
europeo3 es un objeto de la clase de Frances, por lo que tiene los atributos y métodos de la clase de Frances, además de los atributos y métodos de la clase Europeo ya que la clase Frances es una subclase de la clase Europeo.
  • La herencia no tiene que limitarse a un solo nivel, y así podemos tener otra subclase que sea Gallego y que tenga el atributo nifGalego y un método para imprimirlo impNifGalego().
Prog fund poo 9.jpg


Para implementar esta solución, debemos crear otra clase del nombre Gallego dentro de un archivo físico del nombre Gallego.java y que 'extienda' de la clase Espanol.

1 public class Gallego extends Espanol {
2 
3     String nifGallego;
4 
5     public void impNifGallego() {
6         System.out.println("Nif galego:" + nifGallego);
7     }
8 
9 }

Nota: Dependiendo de donde tengáis las clases, puede ser necesario el uso de la instrucción import. Si están en el mismo paquete no es necesario.


  • Partimos del hecho de que europeo2 es gallego y, por lo tanto, tiene un valor nif_galego "GA111111", por lo que debemos modificar la clase principal:
1 .....
2 .....
3 Gallego europeo2 = new Gallego();
4 ....
5 europeo2.nifGallego = "GA11111";
6 europeo2.impNifGallego ();
  • En este caso, europeo2 es un objeto de la clase Gallego, por lo que tiene las variables y métodos de la clase a la que pertenece, pero además esta clase es una subclase de la clase de Espanol, por lo que hereda todas las variables y métodos de esta clase, que a su vez es una subclase de la clase Europeo por lo que también hereda las variables e métodos de esta clase.





Polimorfismo

  • La segunda características de la POO es el POLIMORFISMO.
Esta característica hace referencia a la posibilidad de utilizar métodos definidos tanto en la superclase como en las subclases con el mismo nombre, tipo y número de argumentos.


De esta forma, cuando llamemos a un método de un objeto de una clase sabrá a cual llamar dependiendo de la clase a la que pertenezca.


  • Un ejemplo:
Supongamos que tenemos un método de nombre para impMaximosIngresos, que imprime los ingresos máximos que un europeo debe tener para hacer una declaración de hacienda.
Estos ingresos se almacenarán en una variable do nombre maximosIngresos, con un valor de 12048 euros.
  • Ejercicio: implementar la solución.






  • Solución: Debemos de declarar la variable maximosIngresos en la clase Europeo.
1 short maximosIngresos = 12048;
Observa cómo se inicializa esta variable, de modo que al crear un objeto esa clase tendrá dicho valor de forma predeterminada.
Si no lo hacemos así, debemos de dar un valor en la clase principal de la forma: europeo1.maximosIngresos = 12048;


Debemos de definir el método que imprime los ingresos máximos en la clase Europeo:
1 public void impMaximosIngresos() { // Imprime ingresos máximos
2    System.out.println ("Ingresos máximos son" + maximosIngresos + "euros");
3 }
y debemos modificar la clase Principal para imprimir esta variable:
Por ejemplo: europeo1.impMaximosIngresos ();


  • Sin embargo, queremos que imprima los ingresos máximos en pesetas para aquellos objetos que pertenecen a la clase Espanol o la clase Gallego.
¿Cómo podemos resolverlo? De dos maneras:
  • Declarando un nuevo método en la clase de Espanol que imprima el ingreso máximo en pesetas dando otro nombre que no sea el de la superclase, por ejemplo, impMaximosIngresosPesetas.
  • Declarar el método en la clase de Espanol, con el mismo nombre, pero imprimiendo la cantidad en pesetas.


  • La opción b) es lo que se conoce como polimorfismo.
En nuestro ejemplo, declararemos el siguiente método en clase Espanol:
1 @Override
2 public void impMaximosIngresos() {  // imprime el ingreso máximo en pesetas 
3     System.out.println ("Los ingresos máximos son" + maximosIngresos*166 + " pesetas");
4 }
Nota: El valor 166 tendría que estar definido en una constante.


Si ahora cambiamos la clase Principal para imprimir el ingreso máximo de los 3 objetos (personas):
1 europeo1.impMaximosIngresos();
2 europeo2.impMaximosIngresos();
3 europeo3.impMaximosIngresos();
Veremos que resulta que europeo1 y europeo2 imprimen el ingreso máximo en pesetas.
El objeto europeo3 imprime en euros, ya que el método lo ha heredado de la clase Europeo.
  • Su aplicación práctica la podríamos ver en el siguiente ejemplo.
Supongamos que necesitamos un método que imprima los ingresos máximos de los objetos que le vamos a pasar en forma de parámetros.
Dichos objetos podrán ser de la clase Frances, Espanol o Gallego.
La definición del método sería la siguiente:
1 public static void tratarObjetos(Europeo europeo) {
2 
3    europeo.impMaximosIngresos();
4 }
Como vemos, espera recibir un objeto de la clase Europeo. De esta forma podemos enviarle objetos de la clase Frances, Espanol o Gallego, ya que pertenecen a la Europeo.
Cuando el objeto 'europeo' llame al método 'impMaximosIngresos()' lo hará en función de la clase a la que pertenezca el objeto, de tal forma que si el objeto que enviamos pertenece a la clase Espanol. llamará al método que imprime el importe máximo en pesetas, y si enviamos un objeto que pertenezca a la clase Frances, llamará al método que imprima en Euros.
Si no lo hiciéramos así, tendríamos que definir un método para imprimir objetos de la clase Espanol, otro para la clase Francés,...
  • Por lo tanto en el polimorfismo, cuando se llama a un método se ejecuta el método que pertenece a la clase del objeto referenciado y no al de la clase de la variable que lo referencia (que lío :))
En el ejemplo anterior, el método 'tratarObjetos' recibe un parámetro de la clase Europeo. Por lo tanto la 'variable' europeo es del tipo 'Europeo' => public static void tratarObjetos(Europeo europeo)
Sin embargo, el objeto que enviamos a dicha variable está instanciado como un objeto de la clase Espanol (por ejemplo). Cuando invocamos al método de la forma: europoe.impMaximosIngresos() estamos invocando el método referenciado por la clase a la que pertenece el objeto (Espanol) y no por el método referenciado por la variable que guarda el objeto (Europeo).



  • Otros ejemplos:
  • Una clase Animal con un método 'comunicarse()'. Creamos subclases de la clase Animal, como Perro, Gato, Pajaro...Cada una de esas clases tendrá el método 'comunicarse()' definido ya que cada animal se comunica de una forma diferente.
Si dispongo de un método que espera recibir un objeto de alguna de las 3 clases, lo tendré que definir de esta forma: public void comunicar(Animal animal) { animal.cominicarse(); }
Si creo un array (conjunto de datos del mismo tipo) de la forma: Animal animales[]= { new Perro("simon"),new Perro("paco"),new Gato("mimi")}; Podré recorrer este array y cuando llame al método 'comunicarse()' lo hará en función de la clase a la que pertenezca cada objeto.
Ejemplo obtenido de este enlace.
  • Una clase Parking en la que disponemos de un método 'aparcar(Vehiculo vehiculo)' que espera recibir como parámetro un objeto de la clase Vehiculo. Disponemos de diferentes clases de vehículos, como Automovil, Moto y Camion. Son subclases de la clase Vehiculo. Cuando quiera aparcar un vehículo podré llamar al método aparcar(objeto) enviando cualquiera de los tres tipos de objetos que tengo.
Ejemplo obtenido de este enlace.





  • Por lo tanto, para que haya polimorfismo, es necesario definir métodos en clases diferentes relacionadas con la herencia y deben de tener:
  • El mismo nombre
  • Los mismos parámetros (tipo y número)
  • El mismo tipo de dato de retorno
  • Los modificadores de acceso solo pueden ser iguales o menos restrictivos (indicado en la imagen siguiente).
Prog fund poo 10.jpg


  • El polimofismo implica que un objeto va a ejecutar un método en función de la clase a la que pertenece.




Encapsulación

  • La última característica es la ENCAPSULACIÓN: los bloques compuestos en Java están delimitados por dos llaves ({}).
Las variables de Java solo son válidas desde el punto donde se declaran hasta el final del bloque donde están declaradas.
Es posible tener múltiples niveles de bloques anidados, y cada uno puede contener sus propias variables locales.
Sin embargo, no se puede declarar una variable con el mismo nombre que una de ámbito más exterior.
Por ejemplo:
Prog fund poo 11.jpg


  • Relacionados con la encapsulación están los modificadores de acceso, que modifican la visibilidad de las variables, clases y métodos (es decir, si se puede acceder al valor de una variable).
  • Se puede acceder a un método, clase o variable definida como public desde cualquier lugar.
  • Los métodos y variables:
  • private: solo se acceden desde los métodos de la propia clase, no son heredados por las subclases y no son accesible desde ellos.
  • protected: son visibles dentro de la clase, las subclases y las otras clases definidas dentro del mismo paquete (package).
Un paquete (package) es un grupo de clases relacionadas que se encuentran en el mismo archivo o en archivos diferentes, pero dentro del mismo directorio físico.
Todas las variables y métodos no privados se pueden ver desde todas las clases que pertenezcan al mismo paquete.
Más adelante ampliaremos la información sobre paquetes, cómo hacer uno y qué sirven ...
  • El siguiente cuadro es un resumen del anterior:
Prog fund poo 12.jpg
Nota: Una x indica que puede usar el método / variable declarado con ese modificador.


  • Profundizando un poco más en los conceptos diremos que un miembro (objeto, método, atributo...) privado es accesible solo para la clase en que se define
Este acceso se usa para declarar miembros que solo deberían ser utilizados por la clase.
Esto incluye a los objetos de estas clases que puedan ser utilizados por otras pero que si no fueran privados, podrían modificar los valores de sus atributos y dejar al objeto en un estado 'inconsistente'.
Por ejemplo:
Prog fund poo 13.jpg


  • Si tratamos de compilar la clase Beta, diremos que el atributo (variable) soyPrivado no es accesible.
Para poder modificar o leer el valor de la variable soyPrivado, necesitaríamos implementar dos métodos, uno que nos devuelva el valor y otro que le asignase un valor enviado como parámetro.
¿Cuál es la ventaja de esto? Que controlamos los valores que queremos asignar a la variable y, por lo tanto, hacemos que nuestra clase sea más robusta, es decir, que sea más difícil que falle.


  • Por ejemplo:
Prog fund poo 14.jpg
  • Ahora la temperatura (variable temp) tendrá el valor de 10 que asignaremos previamente.
Como podemos ver, el método asignarValor controla que nunca pasamos un número negativo y si lo pasamos por error asignará un valor predeterminado (valor 0)
Aunque la variable se declara como privada, los métodos 'asignarValor' y 'devolverValor' son públicos y van poder se llamados desde la otra clase a través del objeto 'a'.



  • Continuemos con nuestro ejemplo:
Supongamos que los españoles necesitan un método que informe cuánto debe pagar a hacienda por la cantidad de dinero ganado.
Por lo tanto, declararemos un método de nombre, pagarHacienda con un parámetro de entrada que nos informará de la cantidad ganada.
Como es un método que solo afecta a los españoles, lo declararemos en la clase de español y no en otro. Este método devolverá el dinero a pagar.
  • El proceso de este método es el siguiente:
  • Si la cantidad es > 1000, el importe a pagar se calcula de la siguiente manera:
Cantidad / GC_COEFICIENTE_1
  • Si la cantidad es <= 1000, entonces el importe a pagar se calcula de la siguiente manera:
Cantidad / GC_COEFICIENTE_2
GC_COEFICIENTE_1 valor constante: 10.12
GC_COEFICIENTE_1 valor constante: 11.33
  • Recuerda: Para crear una constante en Java, el modificador final debe preceder a la declaración y por convenio el nombre debe ir con letras mayúsculas y si tiene varias palabras, separadas por guión bajo.
Ejemplo: final int CONSTANTE = 10;



  • Ejercicio: implementar este método.
 1 public class Espanol extends Europeo {
 2     String nif;
 3     private final float GC_COEFICIENTE_1 = 10.12f;   // No queremos que el valor de la constante sea accesible desde fuera de la clase.
 4     private final float GC_COEFICIENTE_2 = 11.33f;   // No queremos que el valor de la constante sea accesible desde fuera de la clase.
 5     
 6     public double cantidadPagar(int cantidade) {
 7         if (cantidade < 1000) {
 8             return (cantidade / GC_COEFICIENTE_1);
 9         } else {
10             return (cantidade / GC_COEFICIENTE_2);
11         }
12     }
13     
14     public void impNif() {
15         System.out.println("Nif:" + nif);
16     }
17 
18     @Override
19     public void impMaximosIngresos() {  // imprime el ingreso máximo en pesetas 
20         System.out.println("Los ingresos máximos son" + maximosIngresos * 166 + " pesetas");
21     }
22 }


Parando la herencia

  • Quizás nos estemos preguntando como podemos hacer para que un determinado método no pueda ser heredado por una clase.
Veamos un ejemplo:
  • Pregunta: ¿ Qué problema tiene el método anterior si creamos una instancia (objeto) de la clase de Gallego ?





  • Solución: Ese objeto heredará el método por lo cual podría llamarlo de la forma:
Gallego gallego1 = new Gallego();
System.out.println("Cantidad a pagar por 1000:" + gallego1.cantidadPagar(1000));


Y esto no queremos que suceda, ya que la clase Gallego no debería 'heredar' este método, solo los españoles que no son gallegos.
¿ Cómo resolver el problema ?
Declarar este método como privado => Entonces no será heredado por las subclases y por lo tanto no podría llamarse desde la clase Gallego.
Pero al implementar esta solución encontramos el problema de que ni el método es visible para el objeto de la clase de Espanol.


  • Necesitamos encontrar una forma de que dicho método no sea heredado por la clase Gallego.
Disponemos de dos opciones:
  • Opción 1: Con esta opción, realmente no evitamos que herede el método, lo que hacemos es sobreescribir el método en la clase Gallego para que no haga nada.
Es decir, en la clase de Espanol:
1 public double cantidadPagar(int cantidad) {
2    // CÓDIGO
3 }
y en la clase Gallego:
1 @Override
2 public double cantidadPagar(int cantidad) {}


Esta opción tiene como principal desventaja que cuando creamos un objeto de la clase Gallego, podremos llamar al método 'gallego1.cantidadPagar(1000)'


  • Opción 2: Si vemos el cuadro de la encapsulación, podemos observar como un método definido sin atributo de modificador de acceso, si definimos una subclase en otro paquete no podrá acceder al mismo.
Por lo tanto podríamos hacer:
En la clase de Espanol:
1 double cantidadPagar(int cantidad) {
2    // CÓDIGO
3 }
Definimos el método sin modificador de acceso.
Definimos la clase Gallego en otro paquete:
1 package otropaquete;
2 
3 import miclase.Espanol;    // Suponiendo que la clase Espanol esté en el paquete 'miclase'
4 public class Gallego extends Expanol{
5 
6   // Código de la clase Gallego
7 
8 }
De esta forma, cuando creemos un objeto de la clase Gallego, no podremos acceder al método 'calcularIngreso()'
Esta solución tiene el problema que puede que tengamos problema de acceso a otros atributos por estar en un paquete diferente. Dependerá de los modificadores de acceso que tengan.


Nota: Todos estos conceptos se usan nuevamente en el ejercicio 'Un programador novato'.

Organización de las clases

  • Jeráquica: Todas las clases provienen de la clase Object. La clase Object se encuentra en parte más alta del árbol de herencia en el entorno de desarrollo de Java.
Todas las clases del sistema Java son descendientes (directos o indirectos) de la clase Object. Esta clase define los estados y comportamientos básicos que deben tener todos los objetos, como la capacidad de comparar un objeto con otro, de convertirse en cadenas, de devolver la clase a la que pertenece un objeto,...
Podéis consultar la lista completa de clases en Java junto con su jerarquía en este enlace.


  • Física: Cada paquete en Java hace referencia a las clases que se encuentran en un mismo directorio. Los paquetes son grupos relacionados de clases e interfaces y proporcionan un mecanismo para manejar un gran número de clases e interfaces y evitar conflictos de nombres. Además de los paquetes que proporciona Java, puedes crear tus propios paquetes y guardar en ellos tus propias clases e interfaces, utilizando la sentencia 'package'.


  • Supongamos que se está implementando un grupo de clases que representan una colección de objetos gráficos como círculos, rectángulos, líneas y puntos.
Podrás empaquetarlas en un paquete, por ejemplo, 'gráficos' y entregar el paquete a otros programadores (junto con la documentación de referencia).
  • De esta manera, otros programadores pueden determinar fácilmente cuales son tu grupo de clases, como utilizarlas, y cómo relacionarlos entre sí y con otras clases y paquetes. Los nombres de las clases no tienen conflictos con nombres de clases en otros paquetes porque las clases y las interfaces dentro de un paquete son referenciadas dentro de su paquete (técnicamente un paquete crea un nuevo espacio de nombres).

Un paquete se declara usando la sentencia 'package':

Prog fund poo 15.jpg
  • La primera línea del código anterior crea un paquete llamado graphics. Todas las clases e interfaces definidas en el archivo que contiene esta sentencia son miembros del paquete.
Por lo tanto, Draggable, Circle y Rectangle son miembros del paquete graphics.


  • Los archivos .class generados por el compilador al compilar el archivo anterior que contiene la definición de las clases Draggable, Circle y Rectangle deben ubicarse en un directorio llamado 'graphics' en algún lugar de la ruta CLASSPATH.
  • CLASSPATH es una lista de directorios que indica a Java donde se encuentran las clases e interfaces compilados. Cuando busca una clase, el intérprete de Java busca el directorio actual y en su CLASSPATH un directorio cuyo nombre coincida con el nombre del paquete del que la clase es miembro.
Los archivos .class para todas las clases y las interfaces definidas en un paquete deben estar en ese directorio del paquete.
Los nombres de los paquetes pueden contener subpaquetes (paquetes dentro de paquetes) (separados por punto). De hecho, los nombres de los paquetes Java tienen varios componentes: java.util, java.lang, etc ...
Cada componente del nombre del paquete representa un directorio en el sistema de archivos. Por lo tanto, los archivos .class de java.util están en un directorio llamado 'util' dentro de otro directorio llamado 'java' en algún lugar en CLASSPATH.
  • Para usar las clases en un paquete, utilizamos la importación antes de la definición de la clase.
Veámoslo en el siguiente ejemplo:
"Hmm", dice el programador novato, "tengo un problema. El coordinador del equipo de diseño me dijo que usaría la clase Calendar para ver la fecha y hora actual en mi aplicación, pero Java no parece entender la clase Calendar,
Me informa de un error cada vez que trato de usarla...".
Prog fund poo 16.jpg


  • "Esto se debe a que la clase 'Calendar' es parte del paquete de utilidades de Java y tienes que importar ese paquete antes de que pueda usarlo ".
"¿Importarlo?", Pregunte al programador.
  • Las clases que creó Sun se almacenan en bibliotecas de clase llamadas "paquetes". Para que una clase de un paquete esté disponible y pueda ser utilizada en otra clase, se debe importar el paquete, lo que significa que el compilador localizará ese paquete.
Por defecto, solo las clases básicas de Java están disponibles, aquellas que se encuentran en el paquete java.lang.
El compilador importa automáticamente el paquete java.lang, pero para usar el resto de las clases que vienen con Java tendrás que importarlas con la sentencia import.
1 import [paquete [.packet2 ...].]nombre de clase;
Para separar los diferentes subpaquetes (ya que podemos crear clases en un paquete que se encuentren dentro de de otro paquete) hay que colocar un punto ('.') entre el paquete y los subpaquetes.

Los paquetes de Java estándares se almacenan en un paquete grande llamado java, por lo que el paquete de utilidad se llama realmente java.util (hay otros paquetes grandes disponibles, por ejemplo, el paquete swing se almacena en el paquete java.swing).


  • En el ejemplo anterio de la clase Calendar, tendríamos que escribir el siguiente código:
 1 package POO;
 2 
 3 import java.util.Calendar;
 4 
 5 
 6 public class Principal {
 7 
 8     
 9     public static void main(String arg[]) {
10 
11         System.out.printf("Fecha actual %s",java.util.Calendar.getInstance().getTime());
12     }
13 
14 
15 }


  • Si quisiéramos 'importar' todas las clases de un determinado paquete, pondríamos '*' de la forma: import java.util.*;
Normalmente no deberíamos hacer uso de esta opción, ya que, aunque a nivel de rendimiento no supone ninguna diferencia, si que da problemas de claridad y problemas con clases que tengan el mismo nombre y que se encuentren en diferentes paquetes.
  • De claridad ya que no sabemos de que paquete proviene una clase que estemos utilizando en nuestro programa (podríamos tener varios import con *).
  • De 'choque' entre clases que se puedan llamar igual pero que estén en diferentes paquetes.
Por ejemplo:
java.lang.reflect.Array
java.sql.Array


  • En caso de no utilizar la sentencia 'import' podríamos utilizar la clase, pero tendríamos que referenciarla con su nombre completo.
En el ejemplo anterior: System.out.printf("Fecha actual %s",java.util.Calendar.getInstance().getTime());


  • La lista completa de paquetes que incorpora Java cuando realizamos la instalación del JDK o del JRE son los siguientes:
  • java.io: Clases que gestionan la entrada/salida de información sobre diferentes dispositivos, como la pantalla, el teclado, ficheros,...
  • java.lang: Contiene las clases básicas del lenguaje. Ya comentado anteriormente. En este paquete se encuentra la clase Object de la que derivan todas las demás clases, la clase System,...
  • java.util: Diferentes clases que sirven para ayudar al programador a realizar diferentes tareas, como la clase Scanner, Calendar,...
  • java.math: Contiene clases para operaciones matemáticas.
  • java.awt: Clases relacionadas con el diseño de aplicaciones gráficas utilizando la interface gráfica 'awt'. Disponemos de la clase Button (botones), clase TextField (cajas de texto),...
  • java.swing: Clases para implementar interface gráficas más completas que las de awt. Tienen mejor rendimiento y poseen más elementos gráficos que el awt.
  • java.net: Conjunto de clases que permiten realizar programación en red, permitiendo enviar y recibir información entre diferentes ordenadores conectados en red.
  • java.sql: Conjunto de clases que nos van a permitir realizar operaciones contra bases de datos (conectar, recuperar información, borrar, actualizar y modificar).
  • java.security: Conjunto de clases para implementar mecanismos de seguridad como claves, certificados, firmas digitales,...





-- Ángel D. Fernández González -- (2017).