Prog. Ampliación POO

De MediaWiki
Saltar a: navegación, buscar



Métodos get y set



Librerías

  • Vimos durante el curso que las clases que tienen algún tipo de relación entre ellas, deben de agruparse bajo un mismo 'paquete'.
  • Recordar también que las clases que se encuentran dentro de un mismo paquete tienen ciertos privilegios de acceso a los métodos/atributos del resto de las clases (modificar de acceso protected).
Por lo tanto podemos considerar los paquetes como una forma de 'limitar' el acceso a métodos y atributos definidos en las clases que se encuentran en el paquete (forma de encapsulación).


  • También vimos que para hacer uso de una clase definida en un paquete es necesario utilizar la orden import.


  • Una librería en un conjunto de clases relacionadas las cuales pueden estar definidas en diferentes paquetes, pero todas dentro del mismo 'archivo físico' el cual no es más que un archivo comprimido donde se encuentras las clases compiladas en sus correspondientes paquetes (directorios).
Por ejemplo, los paquetes que proporciona Java en el JDK y que ya vimos en un punto anterior de este manual se encuentran en el archivo /direc_instalación_jdk_o_jre/lib/rt.jar como podemos ver en la siguiente imagen:
Prog librerias 1.jpg





  • Ejercicio propuesto:
  • Modifica el ejercicio del programador novato y crea una librería en la que se encuentren las clases Vehiculo, Coche y Transporte (que están dentro del paquete 'transportes').
Crea el archivo jar y después haz uso del mismo dentro del proyecto donde se encuentra la clase Principal.
  • Añade a la misma librería las clases creadas en el ejercicio final y que se encuentran dentro del paquete 'material.deportivo'.
Modifica el proyecto donde se encuentra la clase Principal para que haga uso de la librería.



Excepciones

  • Podéis consultar la jerarquía de excepciones en este gráfico.
  • Nosotros trabajamos sobre las que derivan de la clase Exception, ya que las que derivan de la clase Error son errores de la máquina virtual de Java (por ejemplo que no disponga de memoria) y esos errores no son gestionados por las aplicaciones desarrolladas por nosotros.



Delegación de excepciones

  • Vimos en teoría que las excepciones son errores no controlados por el usuario y que normalmente se producen en el envío de datos no adecuados a los métodos de una clase.
Por ejemplo, cuando intentamos acceder a una posición de una cadena que no existe: String nombre = "Angel"; char letra = nombre.charAt(55);
Sintácticamente el código es correcto, pero la ejecución dará un error en tiempo de ejecución que hará que se pare el programa.


  • Si un método no gestiona las excepciones que puedan producirse, el error irá desplazándose hasta el método que realizó la llamada y así sucesivamente hasta llegar a un método que gestione el error y en el caso de que no exista, llegará hasta la máquina virtual de Java, que detendrá la ejecución del programa.


  • En este manual realizamos ejercicios en el manejo de las excepciones.
En esos ejercicios, la 'captura' de la excepción se realizaba en el propio método donde se podía producir el error.


Supongamos que tenemos una clase Lectura que se encarga de leer por teclado diferentes tipos de datos.
  1. package EntradaSalida;
  2.  
  3. import java.util.Scanner;
  4.  
  5. /**
  6.  *
  7.  * @author clase
  8.  */
  9. public class Lectura {
  10.     private static Scanner sc = new Scanner(System.in);
  11.    
  12.     public static int leerNumeroEntero() {
  13.  
  14.         int numero = sc.nextInt();
  15.         return numero;
  16.     }
  17. }


  • Si hacemos uso de esta clase, puede darse el caso de que el usuario no introduzca un número entero, produciéndose una excepción del tipo: java.util.InputMismatchException
En los ejercicios anteriores vimos que la excepción la podemos controlar nosotros dentro del método de la forma:
  1.     public static int leerNumeroEntero() {
  2.         int numero=0;
  3.         try{
  4.          numero = sc.nextInt();
  5.         }
  6.         catch(java.util.InputMismatchException ime){
  7.             numero = 0;
  8.         }
  9.         return numero;
  10.     }
En este tipo de gestión de las excepciones, el método es el que se encarga de corregir el error del usuario, pero puede darse el caso de que no queramos que sea el método quien se encargue de eso, y que sea el que haga uso del método el que se preocupe de controlar el error y realizar las acciones adecuadas para subsanarlo.
  • Para ello, en la definición del método podemos indicar que tipos de excepciones va a lanzar, de la forma:
  1. public void nombreMetodo() throws TipoExcepcion1[, TipoExcepcion2,....] {
  2.  
  3. }
Como podemos ver podemos indicar que el método va a 'lanzar' diferentes tipos de excepciones, ya que puede ser que dentro del código se ejecuten sentencias que puedan producir diferentes tipos.
A esta forma de gestionar las excepciones se denomina Delegación de excepciones.


  • Aplicado a nuestro ejemplo:
  1.     public static int leerNumeroEntero() throws java.util.InputMismatchException{
  2.  
  3.         int numero = sc.nextInt();
  4.         return numero;
  5.     }


Ahora quien tiene que gestionar la excepción es la clase que haga uso del método leerNumeroEntero().
Lo podemos comprobar:
Prog excepciones 1.jpg
Ahora, cuando una clase hace uso de este método puede comprobar como va a lanzar una excepción y el tipo de excepción que puede lanzar.
Por lo tanto, el try-catch lo tendrá que hacer la clase que hace uso de este método:
  1. package proyectojava;
  2.  
  3. import EntradaSalida.Lectura;
  4. /**
  5.  *
  6.  * @author clase
  7.  */
  8. public class Principal2 {
  9.    
  10.     public static void main (String arg[]){
  11.        
  12.       try {
  13.           System.out.printf("Numero entero:%d",Lectura.leerNumeroEntero());
  14.       }  
  15.       catch(java.util.InputMismatchException ime){
  16.           System.out.println("El dato introducido no es un número entero.");
  17.       }
  18.        
  19.     }
  20.    
  21.    
  22. }


Ejercicios propuestos

  • Podéis responder a algunas de las preguntas planteadas en este enlace.




Lanzar excepciones

  • Puede darse el caso de que en ciertas circunstancias queramos generar nosotros una excepción para informar a quien llama a un determinado método, de que los datos enviados no son los correctos (por ejemplo).
Esto lo conseguimos 'lanzando' una excepción. Lanzar una excepción es equivalente a que se produzca una excepción en nuestro código y que provocará que nuestro programa 'pare' si no está implementado el try-catch correspondiente.
  • Imaginemos que disponemos de una clase OperacionesIntervalos en la que disponemos de un método que calcula el número de números pares en un intervalo, siempre que este esté entre los números 1 y 1000.
El usuario podría enviar dos números fuera de los límites de dicho intervalo.
El usuario podría enviar los números del intervalo en orden contrario, enviando primero el número mayor, por ejemplo...
En ese caso, el método podría devolver un valor 'por defecto', como por ejemplo 0, pero este funcionamiento no es muy adecuado, ya que podría pensarse que el número de números pares es ese valor.
Para evitar este funcionamiento, podemos hacer que si los números del intervalo están fuera de los límites o si el orden no es el adecuado, provocaremos una excepción.
Para eso haremos uso de la instrucción throw.


  • Throw lleva asociado la instanciación de un tipo de excepción (recordar que las excepciones son clases). Dependiendo del tipo de error podremos lanzar un tipo de excepción.
Para lanzar una excepción podemos poner: throw new TipoExcepcion(mensaje);
Mensaje es una cadena en la que podemos indicar cual fue el motivo de la excepción.
  • Aplicado a nuestro ejemplo:
  1. package varios;
  2.  
  3. /**
  4.  *
  5.  * @author clase
  6.  */
  7. public class OperacionesIntervalos {
  8.     public static final int LIMITE_INFERIOR = 1;
  9.     public static final int LIMITE_SUPERIOR = 1000;
  10.  
  11.     public static int obtenerNumerosParesIntervalo(int inferior, int superior) throws IndexOutOfBoundsException{
  12.         int numPares = 0;
  13.        
  14.         if (inferior > superior){
  15.             throw new IndexOutOfBoundsException("Limite inferior no puede ser menor que el superior");
  16.         }
  17.         if (inferior < LIMITE_INFERIOR || superior > LIMITE_SUPERIOR){
  18.             throw new IndexOutOfBoundsException("Los límites del intervalo se pasan de los valores permitidos");
  19.         }
  20.         for(int cont=inferior;cont<=superior;cont++){
  21.             if (cont%2==0){
  22.                 numPares++;
  23.             }
  24.         }
  25.         return numPares;
  26.     }
  27.    
  28. }
  • Línea 11: Indicamos que este método va a poder lanzar una excepción del tipo 'IndexOutOfBoundsException'
  • Línea 15: En caso de que el número que indica el límite inferior del intervalo, tenga un valor mayor que el segundo número, lanzaremos una excepción del tipo indicado, enviando un texto explicativo del motivo de la excepción.
  • Línea 18: Igual que en la línea 11 pero esta vez debido a que nos pasamos de los límites que puede tener el intervalo.


  1. package proyectojava;
  2.  
  3. import varios.OperacionesIntervalos;
  4. /**
  5.  *
  6.  * @author clase
  7.  */
  8. public class Principal2 {
  9.    
  10.     public static void main (String arg[]){
  11.        
  12.       try {
  13.           System.out.printf("Numero de pares entre 2 y 50:%d",OperacionesIntervalos.obtenerNumerosParesIntervalo(2,50));
  14.           System.out.printf("Numero de pares entre 30 y 5:%d",OperacionesIntervalos.obtenerNumerosParesIntervalo(30,5));
  15.       }  
  16.       catch(IndexOutOfBoundsException iobe){
  17.           System.out.println("\nEl intervalo no es correcto");
  18.           System.out.println("Mensaje de la excepción:" + iobe.getMessage());
  19.       }
  20.       try {
  21.           System.out.printf("Numero de pares entre -10 y 50:%d",OperacionesIntervalos.obtenerNumerosParesIntervalo(-10,50));
  22.       }  
  23.       catch(IndexOutOfBoundsException iobe){
  24.           System.out.println("\nEl intervalo no es correcto");
  25.           System.out.println("Mensaje de la excepción:" + iobe.getMessage());
  26.       }
  27.        
  28.     }
  29. }
  • Línea 14: Esta llamada va a provocar que se lance una excepción, ya que el primer número es mayor que el segundo.
  • Línea 18: Podemos recoger el texto que viene con la excepción.
  • Línea 21: Esta llamada va a provocar que se lance una excepción, ya que el intervalo no está entre 1 y 1000.
  • Línea 25: Podemos recoger el texto explicativo de la excepción.


  • Recordar que un método puede lanzar múltiples excepciones, teniendo que indicar los tipos de excepciones que lanza en la definición del método, después de la palabra clave throws separadas por comas.


Lanzar excepciones propias

  • Puede suceder que el error que estamos controlando no tenga una correspondencia con alguno de los tipos de excepción de Java.
Por ejemplo, en el caso anterior, lanzamos una excepción de tipo IndexOutOfBoundsException, que normalmente se emplea cuando intentamos acceder a una posición de un array que no existe.
En el caso anterior, podríamos darla por 'buena' en el caso de que intentemos calcular los números pares fuera del intervalo 1 - 1000, pero en el caso de que enviemos los números del intervalo en orden diferente, no se ajustaría al significado del tipo de excepción.
  • Es en estos casos en los que podemos 'crear' nuestras propias clases de excepciones.
Recordar que una excepción es una clase, por lo tanto, lo que tenemos que hacer es definir una clase que derive de la clase base 'Exception' y que tenga un constructor con un parámetro de tipo String, que será el mensaje que guardará la excepción (esto si queremos que tenga dicha funcionalidad).
  1. public class MiExcepcion{
  2.    
  3.    public MiExcepcion(String msg){
  4.        super(msg);
  5.    }
  6.  
  7. }
Como vemos, en el constructor llamaremos al constructor de la clase Exception enviando ese mimos mensaje.


  • Apliquemos esto al ejemplo anterior:

Clase IncorrectoIntervaloException (representa el tipo de excepción personalizado)

  1. package varios;
  2.  
  3. public class IncorrectoIntervaloException extends Exception{
  4.    
  5.     public IncorrectoIntervaloException(String msg){
  6.         super(msg);
  7.     }
  8.    
  9. }


Clase OperacionesIntervalos

  1. package varios;
  2.  
  3. /**
  4.  *
  5.  * @author clase
  6.  */
  7. public class OperacionesIntervalos {
  8.     public static final int LIMITE_INFERIOR = 1;
  9.     public static final int LIMITE_SUPERIOR = 1000;
  10.  
  11.     public static int obtenerNumerosParesIntervalo(int inferior, int superior) throws IndexOutOfBoundsException,IncorrectoIntervaloException{
  12.         int numPares = 0;
  13.        
  14.         if (inferior > superior){
  15.             throw new IncorrectoIntervaloException("Limite inferior no puede ser menor que el superior");
  16.         }
  17.         if (inferior < LIMITE_INFERIOR || superior > LIMITE_SUPERIOR){
  18.             throw new IndexOutOfBoundsException("Los límites del intervalo se pasan de los valores permitidos");
  19.         }
  20.         for(int cont=inferior;cont<=superior;cont++){
  21.             if (cont%2==0){
  22.                 numPares++;
  23.             }
  24.         }
  25.         return numPares;
  26.     }
  27.    
  28. }
  • Línea 15: Aquí lanzamos la excepción personalizada. Fijarse que no hace falta importar la clase de la excepción ya que en el ejemplo se encuentra en el mismo paquete.


Clase Principal2

  1. package proyectojava;
  2.  
  3. import varios.OperacionesIntervalos;
  4. import varios.IncorrectoIntervaloException;
  5. /**
  6.  *
  7.  * @author clase
  8.  */
  9. public class Principal2 {
  10.    
  11.     public static void main (String arg[]){
  12.        
  13.       try {
  14.           System.out.printf("Numero de pares entre 2 y 50:%d",OperacionesIntervalos.obtenerNumerosParesIntervalo(2,50));
  15.           System.out.printf("Numero de pares entre 30 y 5:%d",OperacionesIntervalos.obtenerNumerosParesIntervalo(30,5));
  16.       }  
  17.       catch(IndexOutOfBoundsException iobe){
  18.           System.out.println("\nMensaje de la excepción:" + iobe.getMessage());
  19.       }
  20.       catch(IncorrectoIntervaloException iie){
  21.           System.out.println("\nMensaje de mi excepción:" + iie.getMessage());
  22.       }
  23.       try {
  24.           System.out.printf("%nNumero de pares entre -10 y 50:%d",OperacionesIntervalos.obtenerNumerosParesIntervalo(-10,50));
  25.       }  
  26.       catch(IndexOutOfBoundsException iobe){
  27.           System.out.println("\nMensaje de la excepción:" + iobe.getMessage());
  28.       }
  29.       catch(IncorrectoIntervaloException iie){
  30.           System.out.println("\nMensaje de mi excepción:" + iie.getMessage());
  31.       }
  32.        
  33.     }
  34. }
  • Línea 4: Importamos la clase del nuevo tipo de excepción.
  • Línea 20: Capturamos el tipo de excepción personalizada.



printStackTrace()

  • Este método lo encontramos dentro de cualquier objeto que pertenezca a una clase de excepción.
Cuando realizamos el catch, indicamos el nombre del objeto que va a capturar el tipo de excepción indicado.
Ese objeto tiene la información del error, que la obtenemos con la llamada al método 'getMessage()' y también podemos obtener el origen del error con la llamada a 'printStackTrace()'.


  • Como comentamos anteriormente, cuando se produce una excepción el error se va propagando entre los métodos hasta llegar a uno que gestione el error (tenga el catch).
Con la llamada a printStackTrace, podemos saber en qué método se originó la excepción.


  • Veamos un ejemplo:

Clase MiClase

  1. package proyectojava;
  2.  
  3. /**
  4.  *
  5.  * @author clase
  6.  */
  7. public class MiClase {
  8.    
  9.     public void metodo1()  {
  10.        
  11.         System.out.println("Método 1 se ejecuta");
  12.         metodo2();
  13.        
  14.     }
  15.     public void metodo2()  {
  16.        
  17.         System.out.println("Método 2 se ejecuta");
  18.         metodo3();
  19.        
  20.     }
  21.     public void metodo3()  {
  22.        
  23.         System.out.println("Método 3 se ejecuta");
  24.         System.out.println("Método 3 lanza excepción");
  25.  
  26.         String cad="";
  27.         char car = cad.charAt(10);
  28.     }
  29.    
  30. }
  • Línea 27: Como vemos esta clase llama a tres métodos y el último de ellos provoca una excepción.


Clase Principal3

  1. package proyectojava;
  2.  
  3. /**
  4.  *
  5.  * @author clase
  6.  */
  7. public class Principal3 {
  8.    
  9.     public static void main (String arg[]){
  10.    
  11.         MiClase miclase = new MiClase();
  12.         try{
  13.             miclase.metodo1();
  14.         }
  15.         catch(Exception e){
  16.             System.out.println("CAPTURANDO EXCEPCION:" + e.getMessage());
  17.             e.printStackTrace(System.err);
  18.         }
  19.     }
  20.  
  21. }
  • En la línea 17 mostramos la traza de errores, es decir, desde donde se produce el error y como se va 'propagando' hasta llegar a un método que gestione el error.
En este ejemplo estamos utilizando la salida de errores (System.err) y por eso la información sale en color rojo. Podríamos ponerla en la salida estándar con 'System.out'.


Prog excepciones 2.jpg


  • Normalmente lo emplearemos cuando necesitemos depurar un programa para ver el origen de un error.




Ejercicios propuestos

  • Crea una clase de nombre MiCadena.
Cuando se instancie dicha clase, se podrá mandar como dato una cadena que se guardará en un atributo de la clase.
Dispondrá de los siguientes métodos:
  • concatenar(String dato): concatenerá a la cadena guardada la cadena del parámetro.
  • devolverEntero(): Intentará convertir la cadena guardada a un long y devolverlo. Podrá lanzar una excepción 'ConversionNumeroException'.
  • convertirCadenaNumero(): analizará la cadena buscando dígitos numéricos. En caso de encontrarlo, guardará en la cadena los dígitos encontrados descartando el resto. Por ejemplo: "A12ddddd2423" dará como resultado: "122423". En caso de no encontrar números lanzará la excepción 'NoNumeroException'.
  • devolverPosicionCadena(int pos): Devolverá el caracter encontrado en la posición indicada. Podrá lanzar un IndexOutOfBoundsException.
  • compararNumero(int valor): Comparará el número enviado con el número guardado en la cadena. Si es mayor valor, devolverá false, en caso contrario devolverá true. Debes hacer uso del método devolverEntero() y la excepción no la debes gestionar tú.
  • asignarValor(String cadena): Guardará la cadena enviada como parámetro sustituyendo a la cadena guardada previamente. En caso de que la cadena a enviar sea vacía o tenga el valor null, lanzará una excepción de tipo ValorCadenaException.


Crea una clase Principal y haz una llamada a cada uno de los métodos anteriores.


  • Crea una clase de nombre Lectura.
Todos los métodos podrán ser llamados sin necesidad de instanciar la clase.
Define un único objeto Scanner.
La entrada de datos se realizará en cada uno de los métodos.
Crea los siguientes métodos:
  • leerEntero(): debe devolver un número entero entre 1 y 100. En caso de que no introduzca un número se lanzará la excepción 'InputMismatchException' y en el caso de que no esté entre los límites, lanzará la excepción 'LimitesException'. En cualquier otro caso lanzará la excepción Exception.
  • leerCadena(): debe devolver una cadena de texto introducida por teclado. Dicha cadena de texto no puede ser vacía. Las excepciones deben ser controladas por el que realiza la llamada.
  • leerBooleano(): debe devolver un booleano introducido por teclado. Las excepciones deben ser controladas por el que realiza la llamada.
Crea una clase Principal y haz una llamada a cada uno de los métodos anteriores.

Recursividad


Tipos de relaciones en POO


Clases abstractas

Ejercicios


Nota: Todos estos ejercicios están obtenidos de Internet. El sitio original está referenciado en su correspondiente enlace de la solución.



  • Declara una clase Persona que pueda ser instanciada con los atributos: nombre, apellidos, edad y estado civil (soltero/casado).
Declara una clase abstracta Legislador que herede de la clase Persona, con un atributo provinciaQueRepresenta (tipo String), partido político (tipo String) y despacho que ocupa (byte). Declara un método abstracto getCamaraEnQueTrabaja que devuelve una cadena String. Crea dos clases concretas que hereden de Legislador: la clase Diputado y la clase Senador que sobreescriban los métodos abstractos necesarios. Crea una lista de legisladores y muestra por pantalla la cámara en que trabajan haciendo uso del polimorfismo.
En todas las clases sobreescribe el método toString para que muestre la información.
Posible solución => Enlace 1


  • Declara una clase Persona que no pueda ser instanciada con los atributos: nombre, apellidos y edad.
Crea una clase Profesor que sea una subclase de la clase Persona, que tenga como atributos un idProfesor (numérico) y un método abstracto, importeNomina() que devolverá un número con decimales.
Crea una subclase de Profesor de nombre ProfesorTitular, en el que la nómina es igual a 30 horas lectivas multiplicadas por 43.20 euros cada hora.
Crea una subclase de Profesor de nombre ProfesorInterino, que tendrá como dato la fecha de inicio de la interinidad. Dicha fecha será enviada en el formato (dd/mm/yyyy) y tendrá un método devolverAños() que devolverá un número de tipo byte, que representará el número de años que lleva el Interino trabajando.
La nómina en el caso de los interinos es igual a 30 horas lectivas multiplicadas por 35.60 euros cada hora.
En todas las clases sobreescribe el método toString para que muestre la información.


Posible solución => Enlace 2.
Con respecto a la solución dada se tienen que considerar las siguientes diferencias:
  • Persona se pide que se defina como abstracta.
  • Las horas y precio por hora deben ser declaradas como constantes.
  • La fecha es enviada en forma de cadena y cuando se quiera calcular el número de meses, deberá de hacerse uso de la clase Calendar.


  • La empresa XYZ requiere una aplicación informática para administrar los datos de su personal.
Del personal se conoce: número de DNI, nombre, apellidos y año de ingreso.
Existen dos categorías de personal: el personal con salario fijo y el personal a comisión. Los empleados con salario fijo tienen un sueldo básico y un porcentaje adicional en función del número de años que llevan: menos de dos años salario base, de 2 a 3 años: 5% más; de 4 a 7 años: 10% más; de 8 a 15 años: 15% más y más de 15 años: 20% más. Los empleados a comisión tienen un salario mínimo que será constante para todos los empleados de este tipo e igual a 750.00€, un número de clientes captados y un monto por cada cliente captado. El salario se obtiene multiplicando los clientes captados por el monto por cliente, si el salario por los clientes captados no llega al salario mínimo, cobrará esta cantidad.
Se contará con una clase padre Empleado de la cual no se podrán crear objetos y de la que heredan las clases EAsalariado y EComision. En todas las clases debe haber un constructor con parámetros para todos los atributos y otro vacío. En todos deben crearse los getters and setters correspondientes. Empleado contará con un método imprimir() y un método obtenerSalario().
Se creará una clase gestora y en el método main se creará un vector con los siguientes objetos:
Javier Gómez, DNI: 569587A, desde 2008, salario fijo base = 1225.00€.
Eva Nieto, DNI: 695235B, desde 2010, 179 clientes captados a 8.10€ cada uno.
José Ruiz, DNI: 741258C, desde 2012, 81 clientes captados a 7.90€ cada uno.
María Núñez, DNI: 896325D, desde 2013, salario fijo base = 1155.00€.
Los dos primeros se crearán utilizando el constructor con todos los parámetros y los dos últimos con el constructor vacío y utilizando los setters adecuados.
Desde el método main se llamará a estos otros dos métodos:
sueldoMayor(): Dado un array de objetos Empleado muestra el nombre, apellido y salario del que más cobra.
mostrarTodos(): Dado un array de objetos Empleado lo recorre imprimiendo los datos de todos ellos.
Posible solución => Enlace 3

Interfaces


UML













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