New 2 Java: Construir una Aplicación: 5.- Escribir Objetos a Ficheros, Usar Arrays y más...

¿La siguiente sentencia es verdadera o falsa?
Las clase anónimas internas se declaran y ejemplarizan al mismo tiempo, usando la palabra clave new con el nombre de la clase o el interface existentes.

Verdadero. Las clase anónimas internas se declaran y ejemplarizan al mismo tiempo, usando la palabra clave new con el nombre de la clase o el interface existentes.

. Crear Clases Anónimas

Las clases anónimas internas nos permiten trabajar con clases locales sin tener que ponerles nombre. Se utilizan muy frecuentemente para el manejo de eventos de los componentes gráficos. Un beneficio de utilizar clases anónimas internas es que mantienen la acción del código que manejan en el mismo lugar donde se ejemplariza el controlador GUI. En la clase controladora DiveHandler, se ejemplariza el UIWestPanel, proporcionando las referencias necesarias para el manejo de eventos.

Creamos una clase anónima interna de la siguiente manera:

ActionListener listener = new ActionListener() {
  public void actionPerformed(ActionEvent ae) {
    System.out.println("Anonymous class example");
 }
};

Luego registramos el componente con el oyente:

button.addActionListener(listener);

Podemos combinar la definición de una clase interna con su utilización y registro de un oyente en un bloque de código. Usando la siguiente sintaxis para crear la clase anónima interna y registrar un componente, no necesitamos referenciar una variable local:

button.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
      System.out.println("Button clicked.");
      }
});

Observa el }); de la última línea del ejemplo. El } termina la definición de la clase interna. El ) finaliza la lista de argumentos de addActionListener, y el único argumento dentro de los corchetes es un argumento del tipo ActionListener, referenciando un ejemplar creado desde nuestra clase anónima.

Aunque parezca que este objeto ActionListener no tiene un nombre de clase, el compiladorr le asigna nombres a las clases anónimas internas. Después de compilar las clases con clases anónimas internas, veremos que se han creado nuevos ficheros .class que tienen un signo $ y un número, como DiveHandler$1.class.

Las clase anónimas suelen ser más concisas que las clases miembro, y hacen posible que para nuestra conveniencia definamos objetos oyentes donde los estámos utilizando.Pero se pueden hacer díficiles de leer si el código del método del interface es largo y complejo. Una buena forma de mantener el código limpio es encerrar los grandes trozos de código en sus propios métodos, y luego llamar al método desde dentro del método del interface.

Ahora podemos registrar el enterButton con un objeto ActionListener, y llamar a otros métodos dentro de la clase anónima interna:

Sigue estos pasos...
  1. Abre el fichero DiveHandler.java en tu editor de texto.
  2. Añade la siguiente clase interna que se muestra en negrita:
    enterButton = ui.getEnter();
    enterButton.setText("Enter");
    enterButton.addActionListener (
     new ActionListener () {
       public void actionPerformed (ActionEvent ae)
        { //Open method
         ui.getAirIntake();
         ui.getCount();
         addDive();
        }//Close method  
     }//Close inner class
     );//Close argument and block
    
  3. Graba el fichero.

Observa estos tres métodos llamados dentro del método actionPerformed:

  • ui.getAirIntake; Esta llamada obtiene la información necesaria sobre el aire que hay en el tanque desde la clase UIWestPanel.
  • ui.getCount; Esta llamada obtiene el número de inmersiones desde el GUI.
  • addDive; Crearemos una definición para addDive más adelante, donde se recolectará la información del GUI y se grabará en un fichero.

Ahora escribiremos una clase interna para el botón viewButton como hicimos para enterButton:

Sigue estos pasos...
  1. Abre el fichero DiveHandler.java en tu editor de texto.
  2. Añade la siguiente clase interna que se muestra en negrita:
    viewButton = ui.getView();
    viewButton.setText("View");
    viewButton.addActionListener (
      new ActionListener () {
       public void actionPerformed (ActionEvent ae)
        { //Open method
         viewDive();
         }//Close method  
     }//Close inner class
     );//Close argument
    }//Closes constructor 
    
  3. Graba el fichero.

El constructor ahora abre los streams de entrada y salida, crea dos botones, y registra cada botón con su oyente, usando clases anónimas.

. Crear la Funcionalidad del Botón

El GUI que hemos creado tiene campos de texto, menús y botones de radio para que el usuario introduzca la información de la inmersión. Una vez que el usuario introduce los datos y pulsa el botón Enter, o mejor dicho el botón enterButton, se dispara un evento. Debemos proporcionar las instrucciones para lo que tenga que suceder. En este caso necesitamos escribir los datos a un fichero.

. Escribir Objetos a un Fichero

Para escribir los datos a un fichero, seguimos estos pasos:

  1. Llamamos al método que colecta los datos desde el GUI y los devuelve como un objeto Vector, y lo asignamos a un identificador del tipo Vector.
  2. Sacamos todos los ítems del Vector y los forzamos para que corresponda su tipo a los del modelo DiveRecord.
  3. Creamos un nuevo objeto DiveRecord.
  4. Llamamos al método setDive y le pasamos los ítems Vector.
  5. Escribimos el objeto DiveRecord en un fichero.

Ya hemos preparado nuestro código para hacer el primer paso. En UIWestPanel, creamos un método llamado getUIFields que obtenía los datos introducidos y los devolvía como un objeto Vector:

public Vector getUIFields() { 
Vector fieldValues = new Vector();
  fieldValues.add(rb.getText()); 
  fieldValues.add(date.getText());
  fieldValues.add(s); 
  fieldValues.add(psiStart.getText()); 
  fieldValues.add(psiEnd.getText()); 
  fieldValues.add(psiUsed.getText());
  fieldValues.add(bottomTime.getText()); 
  fieldValues.add(diveNumberL.getText()); 
  fieldValues.add(vis.getText()); 
  fieldValues.add(comments.getText());
  return fieldValues;
}// closes getUIFields

Observa que los datos se devuelven como objetos String dentro del Vector. El método setDive de DiveRecord necesita que le pasemos objetos de distintos tipos:

public void setDive (String t, String d, int dth, 
       int s, int e, int u, int bt, int c, int v, String cts){
       . . . }

Por eso después de obtener los datos del Vector, necesitaremos forzarlos a los tipos adecuados. Esto se hace dentro de un método addDive que vamos a crear en la clase DiveHandler.

Sigue estos pasos...
  1. Abre el fichero DiveHandler.java en tu editor de texto.
  2. Define el siguiente método addDive:
    public void addDive()
    { //Open addDive 
     Vector values = ui.getUIFields();
      //Gets each item and assigns it to a
      //variable of the proper type.
       System.out.println(t);
       String d = (String)values.get(1);
       System.out.println(d);
       //Converting strings to ints
       int dth = Integer.parseInt((String)values.get(2));
       System.out.println(dth);
       int s = Integer.parseInt((String)values.get(3));
       System.out.println(s);
       int e = Integer.parseInt((String)values.get(4));
       System.out.println(e);
       int u = Integer.parseInt((String)values.get(5));
       System.out.println(u);
       int bt = Integer.parseInt((String)values.get(6));
       System.out.println(bt);
       int c = Integer.parseInt((String)values.get(7));
       System.out.println(c);
       int v = Integer.parseInt((String)values.get(8));
       System.out.println(v);
       String cts = (String)values.get(9);
       System.out.println(cts);            
       //Calls the setDive method of the Dive Record
       //to create the dive object, passing the
       //assigned items from above.
    
          try
          { //open try
            Dr = new DiveRecord();
            Dr.setDive(t, d, dth, s, e, u, bt, c, v, cts);
            output.writeObject (Dr);
            output.flush();
            objectCount ++;
    	      
          if(input != null) {
    	  input.close();
    	  input = null;
             }
          } //close try
                  
           catch (NumberFormatException nef) {
                   JOptionPane.showMessageDialog(this,
                     "You need to fill out all fields",
                     "Invalid fields",
                     JOptionPane.ERROR_MESSAGE);
                   }//close catch
          catch (FileNotFoundException fnf) {
                   JOptionPane.showMessageDialog(this,
                     "File not found",
                     null,
                     JOptionPane.ERROR_MESSAGE);
                   }//close catch
          catch (IOException io)
                     {//open catch
                   JOptionPane.showMessageDialog(this,
                     "Error closing file",
                     null,
                     JOptionPane.ERROR_MESSAGE);
                     closeFile();
                   }//close catch
                   
                ui.clearFields();
    }//Close method
    
  3. Graba el fichero.

En el método de arriba, primero se llama al método getUIFields para recuperar la entrada del usuario y asignarla a un objeto Vector. Los valores que el usuario a introducido se fuerzan al tipo apropiado para poder pasarlos al método setDive del objeto DiveRecord:

String t = (String)values.get(0);

En esta línea, el valor 0, en este caso el tipo de inmersión, se recupera desde el objeto Vector y se fuerza al tipo String. Se llama al método System.out.prinln para que podamos chequearlo en la salida estándard. Puedes borrar las líneas de las llamadas a System.out.prinln si prefieres que no se imprima nada en la consola mientras se ejecuta la aplicación.

Después de sacar los datos del Vector y de forzarlos a los tipos apropiados, se pasan dentro del método setDive para crear un objeto DiveRecord:

Dr = new DiveRecord();
Dr.setDive(t, d, dth, s, e, u, bt, c, v, cts);

Entonces se escribe el objeto en un fichero y se incrementa la variable objectCount con cada objeto escrito:

output.flush();
objectCount ++;

Como explicamos anteriormente en la sección sobre las clases ObjectOutputStream y ObjectInputStream, debemos incluir el código para manejar excepciones y condiciones. Para la mayoría de estas ocurrencias, no necesitaremos parar el programa. Sólo deberemos informar al usuario de que algo ha sucedido, como que se ha alcanzado el final del fichero, que no se ha encontrado un fichero o que ha habido un problema al escribir el fichero. Podemos conseguir esto muy fácilmente utilizando la clase JOptionPane:

catch (NumberFormatException nef) {
   JOptionPane.showMessageDialog(this,
     "You need to fill out all fields",
     "Invalid fields",
     JOptionPane.ERROR_MESSAGE);
 }//close catch
catch (FileNotFoundException fnf) {
   JOptionPane.showMessageDialog(this,
     "File not found",
     null,
     JOptionPane.ERROR_MESSAGE);
 }//close catch
catch (IOException io) {
   JOptionPane.showMessageDialog(this,
    "Error closing file",
    null,
    JOptionPane.ERROR_MESSAGE);
    closeFile();
 }//close catch

Por último, después de haber escrito el fichero, es importante cerrarlo y limpiar los campos del GUI para que el usuario pueda introducir otra inmersión:

    closeFile();
 }//close catch
 ui.clearFields();
 

. Ver en el GUI Objetos de un Fichero

Los pasos para ver un objeto son similares a los de escribir un objeto, con algunas pequeñas diferencias:

  1. Asignar un identificador a un objeto DiveRecord.
  2. Si un objeto no es null, abrir el fichero de texto con un ObjectInputStream.
  3. En el caso de que haya algún problema al abrir el fichero, manejar la excepción.
  4. Leer los objetos.
  5. Forzar los tipos a String.
  6. Configurar los strings del GUI.

En el método viewDive, utilizamos una sentencia if para comprobar si el stream ya estaba abierto:

       
public void viewDive()       {
try {
  DiveRecord Dr;
	        
if(input == null)
   try{       
	input = new ObjectInputStream(new FileInputStream(
	                                 "diveLogs.dat"));
       }catch (IOException ioex){
		       JOptionPane.showMessageDialog(null,
			 "Error during reading file",
			 null, JOptionPane.ERROR_MESSAGE);
		   }
	       }

La variable input es null si el fichero no se había abierto apropiadamente para su lectura. Si no es así, se abre un nuevo objeto ObjectInputStream y se proporciona el manejo de excepciones.

Para leer el objeto, debemos forzarlo como un objeto DiveRecord, y luego lo leemos:

Dr = (DiveRecord) input.readObject();

Como el método setFields de la clase UIWestPanel acepta un array de strings como argumento, obtenemos todos los ítems del objeto DiveRecord, los forzamos al tipo String, y los almacenamos en un array. Por último, pasamos el array al método setFields:

String svalues[] = { 
 Dr.getType(),
 Dr.getDte(),
 String.valueOf(Dr.getDepth()),
 String.valueOf(Dr.getStart()),
 String.valueOf(Dr.getEnd()),
 String.valueOf(Dr.getUsed()),
 String.valueOf(Dr.getTime()),
 String.valueOf(Dr.getCount()),
 String.valueOf(Dr.getVis()),
 Dr.getComments()};
	       
ui.setFields(svalues);
return;

De nuevo, necesitamos manejar las excepciones que se podrían lanzar:

   catch (EOFException eofex){
       JOptionPane.showMessageDialog (null,
               "No more records in file",null, 
	       JOptionPane.ERROR_MESSAGE);
   }//close catch
   catch (ClassNotFoundException cnfex){
       JOptionPane.showMessageDialog(null,
       "Unable to open object",
       null, 
       JOptionPane.ERROR_MESSAGE);
   }//close catch
   catch (IOException ioex) {
       JOptionPane.showMessageDialog(null,
       "Error during reading file",
       null, 
       JOptionPane.ERROR_MESSAGE);
   }//close catch
Sigue estos pasos...
  1. Abre el fichero DiveHandler.java en tu editor de texto.
  2. Añade el siguiente método viewDive:
    public void viewDive()       {
      try {
    	       
        DiveRecord Dr;
    	        
        if(input == null) {
          try{       
    	input = new ObjectInputStream(new FileInputStream(
    	                                 "diveLogs.dat"));
    		   }catch (IOException ioex){
    		       JOptionPane.showMessageDialog(null,
    		       "Error during reading file",
    		       null, JOptionPane.ERROR_MESSAGE);
    		   }
    	  }
    	        
    	 Dr = (DiveRecord) input.readObject();
    	 String svalues[] = { 
    	   Dr.getType(),
    	   Dr.getDte(),
    	   String.valueOf(Dr.getDepth()),
    	   String.valueOf(Dr.getStart()),
    	   String.valueOf(Dr.getEnd()),
    	   String.valueOf(Dr.getUsed()),
    	   String.valueOf(Dr.getTime()),
    	   String.valueOf(Dr.getCount()),
    	   String.valueOf(Dr.getVis()),
    	   Dr.getComments()};
    	       
               ui.setFields(svalues);
               return;
    	   }//close try
    	   catch (EOFException eofex){
    	       JOptionPane.showMessageDialog (
    	           null,"No more records in file",null, 
    		   JOptionPane.ERROR_MESSAGE);
    	   }//close catch
    	   catch (ClassNotFoundException cnfex){
    	       JOptionPane.showMessageDialog(
    	        null,"Unable to open object",
    		null, JOptionPane.ERROR_MESSAGE);
    	   }//close catch
    	   catch (IOException ioex) {
    	       JOptionPane.showMessageDialog(null,
    	       "Error during reading file",
    	       null, JOptionPane.ERROR_MESSAGE);
    	   }//close catch
    	    
           }//close method
    
  3. Graba el fichero.

Tu clase DiveHandler se debería parecer a esta DiveHandler.java.

. Sumario

En este quinta parte del tutorial New 2 Java: Crear una Aplicación hemos aprendido a:

. Separar el Interface de la Implementación

Hemos aprendido cómo utilizar la arquitectura MVC, manteniendo los componentes gráficos separados de los datos o modelo.

. Diseñar un Objeto

Hemos decidido qué atributos debería tener un objeto, y hemos proporcionado los métodos get necesarios para trabajar con los datos.

. Serializar Objetos

Hemos descubierto lo fácil que es serializar un objeto, y hemos conocido el interface Serializable.

. Escribir Objetos en Ficheros

Por último, hemos aprendido cómo escribir objetos en un fichero y cómo leerlos luego y poner sus datos en el interface gráfico de usuario.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP
ARTÍCULO ANTERIOR

SIGUIENTE ARTÍCULO

¡SÉ EL PRIMERO EN COMENTAR!
Conéctate o Regístrate para dejar tu comentario.