## Persistencia de un Bean

Un Bean persiste cuando tiene sus propiedades, campos e información de estado almacenadas y restauradas desde un fichero. El mecanismo que hace posible la persistencia se llama serialización. Cuando un ejemplar de bean es serializado se convierte en una canal de datos para ser escritos. Cualquier Applet, aplicación o herramienta que utilice el Bean puede "recontistuirlo" mediante la deserialización. Los JavaBeans utilizan el API Object Serialization del JDK para sus necesidades de serialización.

Siempre que una clase en el árbol de herencia implemente los interfaces Serializable o Externalizable, esa clase será serializable.

Todos los Bean deben persisitir. Para persistir, nuestros Beans deben soportar la serialización implementando los interfaces java.io.Serializable o java.io.Externalizable. Estos interfaces te ofrecen la elección entre serialización automática y "hazlo tu mismo".

### Controlar la Serialización

Se puede controlar el nivel de serialización de nuestro Bean:

- Automático: implementando Serializable. Todo es serializado.
- Excluyendo los campos que no se quieran serializar marcándolos con el modificador transient (o static).
- Escribir los Beans a un fichero de formato específico: implementando Externalizable, y sus dos métodos.

### Serialización por Defecto: El Interface Serializable

El interface Serializable proporciona serialización automática mediante la utilización de las herramientas de Java Object Serialization. Serializable no declara métodos; actúa como un marcador, diciéndole a las herramientas de Serialización de Objetos que nuestra clase Bean es serializable. Marcar las clases con Serializable significa que le estamos diciendo a la Máquina Virtual Java (JVM) que estamos seguros de que nuestra clase funcionará con la serialización por defecto. Aquí tenemos algunos puntos importantes para el trabajo con el interface Serializable:

- Las clases que implementan Serializable deben tener un constructor sin argumentos. Este constructor será llamado cuando un objeto sea "reconstituido" desde un fichero .ser.
- No es necesario implementar Serializable en nuestra subclase si ya está implementado en una superclase.
- Todos los campos excepto static y transient son serializados. Utilizaremos el modificador transient para especificar los campos que no queremos serializar, y para especificar las clases que no son serializables.

El BeanBox escribe los Beans serializables a un fichero con la extensión .ser.

El Bean OurButton utiliza la serialización por defecto para conseguir la persistencia de sus propiedades. OurButton sólo añade Serializable a su definición de clase para hacer uso de la serialización por defecto:

       public class OurButton extends Component implements Serializable,...

Si arrastramos un ejemplar de OurButton al BeanBox, la hoja de propiedades muestra las propiedades de OurButton. Lo que nos asegura que la serialización está funcionando.

1. Cambia algunas de las propiedades de OurButton. Por ejemplo cambia el tamaño de la fuente y los colores.
2. Serializa el ejemplar de OurButton modificado seleccionando el menú File|SerializeComponent... del BeanBox. Apareceza un dialogo para guardar el fichero.
3. Pon el fichero .ser en un fichero JAR con un manifiesto adecuado.
4. Limpia el BeanBox seleccionando el menú File|Clear.
5. Recarga el ejemplar serializado el menú File|LoadJar.

El ejemplar OurButton aparecerá en el BeanBox con las propiedades modificadas intactas. Implementando Serializable en nuestra clase, las propiedades primitivas y los campos pueden ser serializados. Para miembros de la clase más complejos se necesita utilizar técnicas diferentes.

### Serialización Selectiva Utilizando el Modificador transient

Para excluir campos de la serialización de un objeto Serializable, marcaremos los campos con el modificador transient:

      transient int Status;

La serialización por defecto no serializa los campos transient y static.

### Serialización Selectiva: writeObject y readObject()

Si nuestra clase serializable contiene alguno de los siguientes métodos (las firmas deben ser exactas), el serialización por defecto no tendrá lugar:

    private void writeObject(java.io.ObjectOutputStream out)
        throws IOException;
    private void readObject(java.io.ObjectInputStream in)
        throws IOException, ClassNotFoundException;

Se puede controlar cómo se serializarán los objetos más complejos, escribiendo nuestras propias implementación de los métodos writeObject() y readObject(). Implementaremos writeObject cuando necesites ejercer mayor control sobre la serialización, cuando necesitemos serializar objetos que la serialización por defecto no puede manejar, o cuando necesitamos añadir datos a un canal de serialización que no son miembros del objeto. Implementaremos readObject() para recronstruir el canal de datos para escribir con writeObject().

#### Ejemplo: el Bean Molecule

El Bean Molecule mantiene un número de versión en un campo estático. Como los campos estáticos no son serializables por defecto, writeObject() y readObject() son implementados para serializar este campo. Aquí están las implementaciones de writeObject() y readObject() de Molecule.java:

    private void writeObject(java.io.ObjectOutputStream s)
                            throws java.io.IOException {
            s.writeInt(ourVersion);
            s.writeObject(moleculeName);
        }

    private void readObject(java.io.ObjectInputStream s)
                            throws java.lang.ClassNotFoundException,
                                   java.io.IOException {
            // Compensate for missing constructor.
            reset();
            if (s.readInt() != ourVersion) {
                throw new IOException("Molecule.readObject: version mismatch");
            }
            moleculeName = (String) s.readObject();
        }

Estas implementaciones límitan los campos serializados a ourVersion y moleculeName. Ningún otro dato de la clase será serializado.

Es mejor utilizar los métodos defaultWriteObject() y defaultReadObject de ObjectInputStream antes de hacer nuestras propias especificaciones en el canal de escritura. Por ejemplo:

    private void writeObject(java.io.ObjectOutputStream s)
                            throws java.io.IOException {
            //First write out defaults
            s.defaultWriteObject();
            //...
        }

    private void readObject(java.io.ObjectInputStream s)
                            throws java.lang.ClassNotFoundException,
                                   java.io.IOException {
            //First read in defaults
            s.defaultReadObject();
            //...
        }

### El Interface Externalizable

Este interface se utiliza cuando necesitamos un completo control sobre la serialización de nuestro Bean (por ejemplo, cuando lo escribimos y leemos en un formato de fichero específico). Necesitamos implementar dos métodos: readExternal() y writeExternal(). Las clases Externalizable también deben tener un constructor sin argumentos.

#### Ejemplo: Los Beans BlueButton y OrangeButton

Cuando ejecutamos BeanBox, podemos ver dos Beans llamados BlueButton y OrangeButton en el ToolBox. Estos dos Beans son realmente dos ejemplares serializados de la clase ExternalizableButton.

ExternalizableButton implementa el interface Externalizable. Esto significa que hace toda su propia serialización, mediante la implementación de Externalizable.readExternal() y Externalizable.writeExternal().

El programa BlueButtonWriter es utilizado por los makefile de los botones para crear un ejemplar de ExternalizableButton, cambiar su propiedad background a azul, escribir el Bean en un fichero BlueButton.ser. OrangeButton se crea de la misma manera, utilizando OrangeButtonWriter. El makefile pone estos ficheros .ser en buttons.jar, donde el ToolBox puede encontrarlos y recontistuirlos.

Ozito