G.Bordel >Docencia >TAP Técnicas Actuales de Programación (curso 2010-2011)
desprotegido Intro. desprotegido Temario desprotegido Calendario desprotegido RPF desprotegido Recursos protegido Práctica protegido Gest. Alum.
tema_anterior Tema 9: Entrada y salida de datos tema_siguiente
  1. Introducción.
  2. Estructura de clases para E/S.[ejercicios]
  3. E/S de objetos. Serialización.
  4. Creación de nuevas clases de E/S.[ejercicios]

9.3- E/S de objetos. Serialización

Palabras reservadas en Java
abstractbooleanbreakbytecasecatch
charclassconst*continuedefaultdo
doubleelseextendsfinalfinallyfloat
forgoto*ifimplementsimportinstanceof
intinterfacelongnativenewpackage
privateprotectedpublicreturnshortstatic
strictfp**superswitchsynchronizedthisthrow
throwstransienttryvoidvolatilewhile

Comenzaremos viendo cómo la entrada y salida de objetos puede realizrse en principio con la máxima sencillez utilizando las clases ObjectInputStream y ObjectOutputStream. Las siguientes líneas de código instancian un stream de salida conectado a un fichero concreto denominado "Tiempos" y lo envuelven dentro de otro objeto de clase ObjectOutputStream. Mediante el método writeObject de este objeto es posible enviar al fiichero "Tiempos" objetos como es el caso de una String ("Today") o una fecha (instanciada directamente como parámetro del método de escritura). El último método (flush) asegura que todos estos datos lleguen al fichero vaciando cualquier posible buffer intermedio que haya establecido el sistema.

1-  FileOutputStream out = new FileOutputStream("Tiempos");
2-  ObjectOutputStream s = new ObjectOutputStream(out);
3-  s.writeObject("Today");
4-  s.writeObject(new Date());
5-  s.flush();

El segmento de código correspondiente a la lectura de los datos almacenados con el anterior ejemplo puede verse a continuación. Se instancia en esta ocasión un stream de entrada conectado al mismo fichero "Tiempos" para envolverlo con un ObjectInputStream. De este modo, mediante el método readObject pueden recuperarse los objetos guardados. Estos estan caracterizados como Object y podremos aplicarles el "cast" correspondiente si queremos asignarlos a una referencia de su propia clase.

1-  FileInputStream in = new FileInputStream("Tiempos");
2-  ObjectInputStream s = new ObjectInputStream(in);
3-  String today = (String)s.readObject();
4-  Date date = (Date)s.readObject();

No todos los objetos pueden serializarse, sino que para que sea asi debe especificarse explicitamente en su definición (los usados en el ejemplo -String y Date- lo son por que su definición lo explicita). Para especificar que los objetos de una clase que definamos podrán serializarse basta con implementar el interfaz Serializable. Este interfaz en realidad no es tal ya que su definición esta vacia y en consecuencia no nos obliga a definir ningún método.

1-  package java.io;
2-  public interface Serializable {
3-  };

Por tanto, insistimos, nuestros objetos podrán serializarse simplemente por el hecho de incluir en la declaración de la clase este interfaz.

1-  public class MySerializableClass implements Serializable {
2-      \Definición de la clase sin ningún requerimiento especial.
3-  }

El modificador "transient".

En el proceso de serialización todos los elementos que componen un objeto son situados en serie siguiendo un mecanismo inversible para poder recuperarlos posteriormente. Normalmente en caso de querer transmitir o almacenar un objeto no toda su composición puede ser de interés (p.ej. habrá elementos dependientes de una ejecución concreta que no se precisa recuperar tal cual en otra) y en tal caso haremos uso del modificador "transient" en la declaración de dichos elementos. Los elementos calificados como "transient" no son incluidos en la serialización para la escritura e ignorados igualmente en la lectura.

1-  public class MySerializableClass implements Serializable {
2-      public Vector v=new Vector();
3-      private transient Date d=new Date();
4-      \...
5-  }

Personalización de los métodos de lectura y escritura.

En la mayoria de los casos lo visto hasta el momento es suficiente para transmitir y almacenar objetos, pero puede darse la circunstancia de que necesitemos alterar de algún modo la función que nos proporcionan los métodos readObject y writeObject de ObjectInputStream y ObjectOutputStream. Esto es posible re-escribiendo los dos métodos dentro de nuestra clase ya que estos métodos son introducidos por el sistema en toda clase declarada como serializable y los métodos de los streams realmente llaman a los propios del objeto en cuestión. Más exactamente los métodos introducidos son los dos indicados y otros dos llamados defaultReadObject y defaultWriteObject de modo que los dos primeros se limitan a llamar a estos dos segundos. Esto nos permite re-escribir los primeros y poder hacer uso aun de los métodos de lectura y escritura habituales. El el siguiente ejemplo se hace esto.

1-  private void writeObject(ObjectOutputStream s) throws IOException {
2-      // código especifico anterior a la escritura estándar
3-      s.defaultWriteObject();
4-      // código especifico posterior a la escritura estándar
5-  }
1-  private void readObject(ObjectInputStream s) throws IOException {  
2-      // código especifico anterior a la escritura estándar
3-      s.defaultReadObject();
4-      // código especifico posterior a la escritura estándar
5-  }

Aún cuando personalicemos estos métodos el sistema pone de su parte ciertos elementos (aún cuando no usemos las llamadas a los métodos por defecto), de modo que en caso de que quisieramos eliminar por completo el comportamiento del sistema y escribir nuestras propias rutinas de serialización el modo de proceder sería otro. Se trata de extender el interfaz Externalizable (que a su vez extiende el Serializable) e implementar los dos métodos que nos exige.

1-  package java.io;
2-  public interface Externalizable extends Serializable
3-  {
4-      public void writeExternal(ObjectOutput out)
5-                                throws IOException;
6-      public void readExternal(ObjectInput in)
7-                               throws IOException, 
8-                               java.lang.ClassNotFoundException;
9-  }

En este caso sólo se guarda "automáticamente" la identidad del objeto, siendo responsabilidad de estos dos métodos todo lo demás: salvar y recuperar los contenidos y coordinarse con su supertipo con este mismo objeto.

Siguiente punto: 9.4- Creación de nuevas clases de E/S


Plataforma de soporte a curso y contenidos (c) German Bordel 2005.