En esta sesión vamos a hacer un ejercicio práctico que englobe varios de los conceptos vistos hasta ahora.
1. Vamos a construir una aplicación que permita definir distintos tipos de figuras geométricas, y poderlas leer y guardar en ficheros. Antes de explicar los pasos a seguir, daremos un rápido vistazo a la estructura de la aplicación, para entender qué ficheros y directorios tenéis en la plantilla.
En la plantilla tenemos dos paquetes de clases:
Finalmente, tenemos una clase principal en el directorio raíz, llamada AplicGeom, que leerá las figuras que haya en un fichero que se le pase como parámetro, y mostrará un menú de opciones para que el usuario pueda:
Veremos ahora qué pasos seguir para construir todo esto.
package geom; public class Circulo extends Figura implements java.io.Serializable { int x; int y; int radio; public Circulo(int x, int y, int radio) { ... } public int getX() { ... } public int getY() { ... } public int getRadio() { ... } public void setX(int x) { ... } public void setY(int y) { ... } public void setRadio(int radio) { ... } public String imprime() { return "CIRCULO: (" + x + ", " + y + "), r = " + radio; } }
... public class Rectangulo extends Figura implements java.io.Serializable { int x1; int y1; int x2; int y2; ...
... public class Linea extends Figura implements java.io.Serializable { int x1; int y1; int x2; int y2; ...
Recordad definir también el método imprime para que saque
una cadena de texto con información relativa a cada figura.
En el caso del Rectangulo, sería algo como:
"RECTANGULO: (" + x1 + ", " + y1 + ") - (" + x2 + ", " + y2 + ")"
Y para la Linea:
"LINEA: (" + x1 + ", " + y1 + ") - (" + x2 + ", " + y2 + ")"
NOTA IMPORTANTE: Observad que tanto la clase padre Figura como
todas las subclases definidas son Serializables (implementan la
interfaz Serializable). Esto nos permitirá después guardarlas y
leerlas de ficheros como objetos completos. Recordad también poner la
directiva package al principio del fichero, indicando el paquete
al que pertenecen las clases.
package io; import java.io.*; public class EntradaTeclado { BufferedReader in = null; ...
Después, en el constructor hacemos que dicho buffer lea de la entrada estándar (System.in):
... public EntradaTeclado() { in = new BufferedReader(new InputStreamReader(System.in)); }
Finalmente, definimos un método leeTeclado que devuelva una cadena con cada línea que el usuario ha escrito hasta pulsar Intro. Esta cadena la utilizaremos después desde el programa principal, para recoger todas las órdenes del usuario (capturamos las excepciones necesarias, y devolvemos null si se produce algún error):
... public String leeTeclado() { try { return in.readLine(); } catch (Exception e) { return null; } } }
package io; import geom.*; import java.io.*; import java.util.*; public class IOFiguras { public static Figura[] leeFiguras(String fichero) { ... } public static void guardaFiguras(Figura[] figuras, String fichero) { ... } }
public static Figura[] leeFiguras(String fichero) { ArrayList alAux = new ArrayList(); Figura f; try { ObjectInputStream oin = new ObjectInputStream (new FileInputStream(fichero)); ... // leer figuras del fichero
oin.close(); } catch (Exception e) { // Se pueden producir 2 excepciones: // FileNotFoundException si no encuentra el fichero // IOException cuando llegue a fin de fichero // DA IGUAL QUE SE PRODUZCA UNA U OTRA, // LA CAPTURAMOS Y NO HACEMOS NADA MAS } // Si no había fichero, o no tenía figuras, la lista estará // vacía (se inicializa al principio del método, pero no llega // a entrar en el "while"). Entonces devolvemos null if (alAux.size() == 0) return null; // Si había figuras, devolvemos un array con ellas return((Figura[])(alAux.toArray(new Figura[0]))); }
Observa que leemos objetos de tipo Figura (genéricos), sin discriminar si son líneas, círculos o rectángulos. El objeto se recupera tal cual, y del tipo que sea, aunque se trate como una figura genérica. Observa también que no hay forma de detectar el final del fichero. Se lanzará una IOException cuando lleguemos. Así que hay que meter la lectura de objetos en un bucle (infinito), y el bucle en un try, de forma que cuando llegue al final del fichero lanzará la excepción y saldremos del bucle, con todo el fichero ya leído. Por último, observa que no pasa nada si el fichero no existe, o si se lanza cualquier excepción. El método irá añadiendo figuras conforme las vaya leyendo: si no hay fichero, o no hay figuras en él, la lista quedará vacía (nunca entrará en el "while"), y se devolverá null. Si hay figuras, se irán colocando en la lista y se devolverá la lista al final. Después se trata de tomar lo que devuelva la llamada al método y hacer lo que corresponda:
Figura[] fig = IOFiguras.leeFiguras(nombre_fichero); if (fig != null) { // ... Procesar el array de figuras como se necesite // Por ejemplo, para meterlas en una lista: ArrayList figuras = new ArrayList(); for (int i = 0; i < fig.length; i++) figuras.add(fig[i]); } else { // ... No hay figuras que procesar }
Captura las excepciones adecuadas para poderlo compilar. No es
necesario que hagas nada en el bloque catch de la captura.
public static void guardaFiguras(Figura[] figuras, String fichero) { try { ObjectOutputStream oout = new ObjectOutputStream (new FileOutputStream(fichero)); ... // guardar figuras
oout.close(); } catch (Exception e) {} }
Captura las excepciones adecuadas, para poderlo compilar. No es
necesario que hagas nada en el bloque catch de la captura.
public AplicGeom(String fichero) { this.fichero = fichero; et = new EntradaTeclado(); figuras = new ArrayList(); Figura[] fig = IOFiguras.leeFiguras(fichero); // ... Aquí iría el resto del código: un bucle para // recorrer las figuras del array "fig" (si no es null) // y meterlas en la lista "figuras" }
Después vemos que hay un método main que recoge el nombre del
fichero de los parámetros de entrada, y crea un objeto de tipo AplicGeom
con él. Finalmente, llama al método iniciar de la clase, que está
vacío, pero deberá contener toda la lógica del programa. Deberéis
completarlo para que haga una iteración continua en la que:
PARA ENTREGAR
ENTREGA FINAL DEL BLOQUE 2