domingo, 28 de junio de 2015

¿Cómo crear nuestras propias excepciones en Java?

Podemos crear nuestras propias extensiones debido a que es posible heredar de la clase Exception, que a su vez contiene todas las excepciones.

Primero, revisaremos la jerarquía de esta clase y luego iremos con un ejemplo.

Es común haber notado excepciones del tipo RuntimeException en nuestros programas. Además hay que recordar que es posible arrojar la excepción o tratarla allí mismo. Si la vamos a arrojar quiere decir que algún otro método va a tratar la excepción. Por ejemplo:
Esto quiere decir que las excepciones no serán tratadas en la clase principal, si no que serán arrojadas al método donde se dio la excepción.

A veces resulta más conveniente tratar las excepciones, dentro de la clase donde se originan. Para esto se utiliza los operadores try & catch.

Para el ejemplo haremos uso del try-catch, con una excepción propia.
Se tiene un conjunto de figuras y se quiere imprimir la lista de ellas pero solo aquellas cuyo perímetro esté dentro de un rango establecido por el usuario. Entonces se solicita el rango es decir el perímetro mínimo y el perímetro máximo. Nuestro programa puede dejar de funcionar si el perímetro mínimo es mayor que el perímetro máximo, y es un tipo de excepción particular. Por lo cual crearemos nuestra propia excepción llamama ExcepcionFiltroPerimetro.

Interfaces en Java

Las interfaces en Java
Supongamos que tenemos un conjunto de clases y necesitamos que hereden de JFrame, pero además necesitamos que herede de otra clase de la cual solo necesitamos sus métodos, es decir de una clase abstracta. Java no permite realizar esta acción debido a que no soporta herencia múltiple. Existe un artificio para la herencia múltiple que se denomina: implementar interfaces.

No necesariamente se aplica una interface como artificio para la herencia múltiple, cuando solo se tiene métodos comunes, constantes comunes entonces se habla de una interface.

Una interface es como un protocolo: todas las clases deben implementar todos los métodos abstractos de la interface.

Generalmente, nombres como: comparable, medible, ordenable, etc se asocian directamente a una interface.

Para hacer uso de una interface luego del nombre de la clase se escribe la palabra implements seguido del nombre de la interface. En herencia se maneja extends en interfaces implements.

Cabe mencionar que existen interfaces predeterminadas por java como por ejemplo: clonable, comparable, seriable, etc.

Ejemplo:

Se tiene 2 clases: Estudiante y Curso. La idea es gestionar a los Estudiantes mediante Curso. Luego ordenar los objetos Estudiante contenidos en un ArrayList creado en Curso. Al final implementar una interface (Comparable) predeterminado por Java, para ordenar los elementos del ArrayList de acuerdo a algún parámetro deseado.

La clase estudiante implementa la interface Comparable. Al momento de implementar esta clase, es obligación implementar el único método abstracto de esta clase: toCompare.
Entonces, se implementa el método toCompare y se lo sobreescribe:
Para la sobrescritura, dependiendo del tipo de parámetro se retorna un entero que devuelve un entero -1 en caso de que el Objeto comparado sea "menor" que alguno de los demás objetos.

La sobrescritura de este método nos sirve para la utilización dentro de la misma clase. Pero si nos fijamos todos los Estudiantes están en un ArrayList dentro de la clase curso.

Debe ser desde esta clase que se de el ordenamiento debido a que contiene la información de todos los estudiantes ingresados. Entonces como ordenar desde la clase Curso?

La clase Collections que hereda directamente de Object (es decir no necesitamos importarla para usarla) tiene un método llamado sort, que hereda de directamente de la interface comparable, tal como lo muestra el API de java:

Entonces, creamos un método llamado ordenarPorEdad en la clase Curso que contiene lo siguiente:

Esto hará que los Estudiantes dentro de listaEstudiantes se ordenen por edad, en orden ascendente de acuerdo al ordenamiento natural.

Por último se muestra la clase principal en la cual se crea los archivos y se hace el ordenamiento respectivo. Se comprueba por pantalla.


La salida por consola:

LA INTERFAZ CLONEABLE
Esta interfaz permite realizar una copia exacta de un objeto, es decir cada campo tendrá el mismo contenido para el objeto original y el objeto clonado. Cabe recalcar que algunas referencias indican que no es muy recomendado utilizar esta interfaz ya que pueden existir subclases que necesiten implementar su propia lógica clonable, y al momento de heredar de una clase que implemente Clonable y tenga sobrescrito el método clone de cierta forma, puede ser que para la clase hija no sea tan conveniente. Más información de porqué muchos programadores utilizan el constructor de copia en vez de la interface clonable aquí: http://stackoverflow.com/questions/4081858/about-java-cloneable

Como sea, voy a mostrar un ejemplo de como implementar está clase. Qué a diferencia de la interfaz Comparable es más simple.


En la clase Estudiante se debe asegurarse 2 cosas:
1) Que se implemente la interfaz Comparable
2) Sobrescribir el método clone

La excepción CloneNotSupportedException solo se da cuando la clase no implementa la interfaz Clonable.

Algo ha tomar en cuenta es que si se aplica herencia, este método nos devuelve un objeto de tipo estudiante, es decir es necesario hacer un casting al tipo de la clase hija.

En el main, tendríamos algo así:

Y la salida por pantalla:


Por si fuera necesario, también dejo un ejemplo de un constructor de copia, que es más recomendado.

Serialización de Objetos en Java

SERIALIZACIÓN DE OBJETOS EN JAVA
De manera muy sencilla, es posible escribir información de un objeto Java en un archivo para evitar perder la información luego de que se cierre el programa Java, es decir tener persistencia en Java. Es muy básicamente una forma de evitar solo trabajar en tiempo de ejecución y ampliar el alcance de nuestro programa cada vez que se vuelva a iniciar el programa. Existen maneras de escribir sobre un archivo de tal manera que la información se escriba en forma de caracteres. Es decir los atributos de cada objeto pueden estar almacenados en un archivo en forma de caracteres. Supongamos que tenemos una clase Paciente con los atributos: nombre(String), no. documento(String) y género(char). Entonces es posible enviar como caracteres, la información de cada campo hacia un archivo pudiendo obtener un archivo así:


Este método funciona como un registro de datos, pero al momento de querer obtener este registro y almacenarlo como un objeto necesitamos obtener cada campo de cada línea, hacer casting para cada valor (porque hay que recordar que los vamos a obtener como caracteres) y luego setearlos de nuevo en el objeto. Este ejemplo puede no presentar muchos problemas, pero que sucede si tenemos muchos atributos, eso implicaría líneas de código y redundancia. Más bien este método mostrado, sirve para enviar datos listos para su impresión. 

La serialización de Objetos, permite enviar datos en forma de bytes, de tal manera que cada archivo contendrá toda la información del objeto, es decir incluye que librerias ha importado, paquetes, herencia, etc. Pero principalmente contiene el tipo de datos de cada atributo lo que simplificaría mucho las cosas. Es decir, el mecanismo de serialización realiza copias exactas de los objetos.

Para el envío de objetos nos apoyamos en las clases ObjectInputStream y ObjectOutputStream, permirten leer y enviar objetos respectivamente. El constructor de estas dos clases espera recibir como argumento de entrada un objeto flujo(Output/InputStream) que pueden escribir o enviar información.
Este mecanismo sirve como envoltura, por ejemplo para archivos: para envolver un objeto FileInputStream en un objeto ObjectInputStream, se pasa el objeto FileInputStream al constructor de ObjectInputStream, así:
ObjectInputStream entrada = ObjectInputStream(new (FileInputStream archivo)) ;  
donde archivo es de tipo File.

O para el constructor de ObjectOutputStream:
ObjectOutputStream entrada = ObjectOutputStream(new (FileOutputStream archivo)) ;   donde archivo es de tipo File.

A continuación se muestra un ejemplo:
Se tiene la clase paciente con 4 atributos:


Como es de notarse, el envío de datos mediante flujo de caracteres se complica aquí por la presencia del atributo Calendar fechaNacimiento, además de lo nombrado al inicio de este post. 

Para poder serializar primeramente la clase debe implementar la interface Serializable, que no requiere implementar ningún método. Además si queremos que uno de los atributos no se serialize, entonces añadimos la palabra transient.

En la clase principal:



El archivo de texto no mostrará nada debido a que lo que se envía es bytes y para ver esa información se necesitará un editor diferente. Pero una buena forma de comprobar de que si se está enviando información, es chequeando el tamaño del archivo, que se modificará luego de enviar información.

Una vez que la información está almacenada en el archivo se la leerá y luego se la almacenará en un nuevo ArrayList de Pacientes. Para realizar esta opción necesitaremos de un objeto del tipo ObjectInputStream: