Persistencia
Es la capacidad de los objetos para perdurar más allá de la
duración del programa que los creó.
Se trata de que un objeto pueda mantener su estado entre
diferentes ejecuciones del programa.
Una de las formas que existen de implementar la persistencia
es almacenar los objetos en memoria secundaria antes de que el programa
termine, y cargarlos de nuevo en memoria primaria en la siguiente ejecución.
Serialización
Al implementar la persistencia, es frecuente que el estado
de los objetos se almacene en un archivo.
El archivo es una sucesión de bytes. Por tanto, es necesario
“traducir” el estado del objeto a una sucesión lineal de bytes. Ese proceso se
conoce como serialización.
La serialización de objetos
permite convertir cualquier objeto que implemente a la interfaz Serializable o a la interfaz Externalizable en una secuencia de
bites que puede ser utilizada posteriormente para reconstruir el objeto
original.
Esta secuencia de bits puede
guardarse en un fichero o puede enviarse a otra máquina virtual (que puede
estar ejecutándose en otro sistema operativo) para reconstruir el objeto
(deserialización) en otro instante o en otra máquina virtual. No tenemos que
preocuparnos en absoluto de las diferentes representaciones de datos en
distintos ordenadores.
Los objetos mantienen referencias
a otros objetos. Estos otros objetos deben ser también almacenados y
recuperados con el fin de mantener las relaciones originales. Por supuesto,
todos estos objetos deben ser serializables ya que de lo contrario se lanzará
una excepción del tipo NotSerializableException.
Para reconstruir un objeto (o
conjunto de objetos) Java serializado es necesario que la clase (o clases) esté
en el classpath con el fin de identificarla y verificarla antes de restaurar el
contenido en una nueva instancia.
Ejemplo
import
java.util.*;
import
java.io.*;
public
class Serial {
public static void main(String[] args) {
try{
FileOutputStream archivo = new
FileOutputStream("D:\\prueba.dat");
ObjectOutputStream salida = new
ObjectOutputStream(archivo);
salida.writeObject("Hoy es:
");
salida.writeObject(new Date());
salida.close();
}
catch(IOException e){
System.out.println("Problemas
con el archivo");
}
try{
FileInputStream archivo = new
FileInputStream("D:\\prueba.dat");
ObjectInputStream entrada = new
ObjectInputStream(archivo);
String hoy = (String)entrada.readObject();
Date fecha =
(Date)entrada.readObject();
entrada.close();
System.out.println(hoy + fecha);
}
catch(FileNotFoundException e){
System.out.println("No se
pudo abrir el archivo");
}
catch(IOException e){
System.out.println("Problemas
con el archivo");
}
catch(Exception e){
System.out.println("Error
al leer un objeto");
}
}
}
En la salida del programa
veríamos algo como
Hoy
es: Sun Oct 11 18:09:10 CEST 2015
Un objeto se puede serializar si
implementa el interface Serializable.
Este interface no declara ningún método, se trata de un interface vacío.
import
java.io.*;
public
interface Serializable{}
Para que un objeto sea
serializable, todas sus variables de instancia han de ser serializables. Todos
los tipos primitivos en Java son serializables por defecto (igual que los
arrays y otros muchos tipos estándar).
La serialización se introdujo en
Java para implementar la persistencia y soportar la Invocación Remota de Métodos (RMI) que permite a una aplicación
enviar mensajes a un objeto remoto (que se está ejecutando en otra máquina
virtual). También es necesaria en el caso de los JavaBeans.
Flujos para entrada y salida de objetos
Los flujos para leer y escribir
objetos tienen métodos que reciben o devuelven instancias de la clase Object.
void
ObjectOutputStream.writeObject(Object o)
Serializa un objeto y lo graba en
el flujo al que se haya conectado el ObjectOutputStream
Object
ObjectInputStream.readObject()
Carga el siguiente objecto del
flujo al que esté conectado. Para usarlo hay que hacer un cast. Ejemplo:
Perro
p = (Perro) miStream.readObject();
El modificador Transient
Cuando un dato de una clase
contiene información sensible, hay disponibles varias técnicas para protegerla.
Incluso cuando dicha información es privada (el miembro dato tiene el
modificador private) una vez que se
ha enviado al flujo de salida alguien puede leerla en el archivo en disco o
interceptarla en la red. El modo más simple de proteger la información
sensible, como una contraseña (password) es la de poner el modificador transient delante del miembro dato que
la guarda. En el siguiente ejemplo se redefine la función toString() método de la clase base Object. Esta función devolverá el nombre del cliente y la
contraseña. En el caso de que la variable password guarde el valor null se
imprimirá el texto (no disponible).
Nota: Probar a eliminar el modificador transient, comprobar que se
pierde la privacidad al almacenarse la clave tal cual, sin null.
ARCHIVO CLIENTE.JAVA
public
class Cliente implements java.io.Serializable{
// Propiedades
private String nombre;
private transient String passWord;
// Constructor
public Cliente(String nombre, String pw){
this.nombre = nombre;
passWord = pw;
}
// Métodos
public String toString(){
String texto = (passWord==null) ?
"(No disponible)" : passWord;
texto += nombre;
return texto;
}
}
ARCHIVO SERIALIZABLE.JAVA
import
java.io.*;
public
class Serializacion {
public static void main(String[] args) {
// Propiedades
Cliente cliente = new
Cliente("Inazio", "DAM");
// Código
try{
ObjectOutputStream salida = new
ObjectOutputStream(new FileOutputStream("D:\\cliente.obj"));
salida.writeObject("Datos del
cliente\n");
salida.writeObject(cliente);
salida.close();
ObjectInputStream entrada = new
ObjectInputStream(new FileInputStream("D:\\cliente.obj"));
String str =
(String)entrada.readObject();
Cliente obj1 =
(Cliente)entrada.readObject();
System.out.println("------------");
System.out.println(str + "
" + obj1);
System.out.println("------------");
entrada.close();
}
catch(IOException e){
e.printStackTrace();
}
catch(ClassNotFoundException e){
e.printStackTrace();
}
try{
// Espera la pulsación de una
tecla y luego RETORNO
System.in.read();
}
catch(Exception e){}
}
}
Así, la salida del programa será
------------
Datos
del cliente
(No disponible)Inazio
------------
La herencia en objetos serializables
Para serializar objetos de una
jerarquía solamente la clase base tiene que implementar el interface
Serializable. En el siguiente ejemplo se muestra una herencia de la clase
Figura que implementa la clase Serializable. De esta clase se heredan la clase
Circulo y Rectangulo, ninguna de ellas implementa la clase Serializable.
ARCHIVO FIGURA.JAVA
public
abstract class Figura implements java.io.Serializable{
// Propiedades
protected int x;
protected int y;
// Constructor
public Figura(int x, int y){
this.x = x;
this.y = y;
}
// Métodos
public abstract double area();
}
ARCHIVO CIRCULO.JAVA
public
class Circulo extends Figura{
// Propiedades
protected double radio;
private static final double PI = 3.1416;
// Constructor
public Circulo(int x, int y, double radio){
super(x, y);
this.radio = radio;
}
// Métodos
public double area(){
return PI*radio*radio;
}
}
ARCHIVO RECTANGULO.JAVA
public
class Rectangulo extends Figura{
// Propiedades
protected double ancho, alto;
// Constructor
public Rectangulo(int x, int y, double
ancho, double alto){
super(x, y);
this.ancho = ancho;
this.alto = alto;
}
// Métodos
public double area(){
return ancho*alto;
}
}
ARCHIVO HERENCIASERIALIZABLE.JAVA
import
java.io.*;
public
class HerenciaSerializable {
public static void main(String[] args) {
// Propiedades
Figura fig1 = new Rectangulo(10, 15,
30, 60);
Figura fig2 = new Circulo(12, 19, 60);
// Código
try{
ObjectOutputStream salida = new
ObjectOutputStream(new FileOutputStream("D:\\figura.obj"));
salida.writeObject("Guardar
un objeto de una clase derivada\n");
salida.writeObject(fig1);
salida.writeObject(fig2);
salida.close();
ObjectInputStream entrada = new
ObjectInputStream(new FileInputStream("D:\\figura.obj"));
String str =
(String)entrada.readObject();
Figura obj1 =
(Figura)entrada.readObject();
Figura obj2 =
(Figura)entrada.readObject();
System.out.println("-.-.-.-.-.-.-.-.-.-.-");
System.out.println(obj1.getClass().getName()
+ " origen (" + obj1.x + ", " + obj1.y + ")" +
" area= " + obj1.area());
System.out.println(obj2.getClass().getName()
+ " origen (" + obj2.x + ", " + obj2.y + ")" +
" area=" + obj2.area());
System.out.println("-.-.-.-.-.-.-.-.-.-.-");
entrada.close();
// Se puede fundir en un catch
Exception
}
catch(IOException e){
e.printStackTrace();
}
catch(ClassNotFoundException e){
e.printStackTrace();
}
try{
// Espera la pulsación de una
tecla y luego RETORNO
System.in.read();
}
catch(Exception e){}
}
}
Y el resultado mostrado por
pantalla es
-.-.-.-.-.-.-.-.-.-.-
Rectangulo
origen (10, 15) area= 1800.0
Circulo
origen (12, 19) area=11309.76
-.-.-.-.-.-.-.-.-.-.-
La interfaz Externalizable
Para un control explícito del
proceso de serialización la clase ha de implementar el interface Externalizable. La clase es responsable
de escribir y de leer su contenido, y ha de estar coordinada con sus clases
base para hacer esto. La definición del interior Externalizable es la siguiente:
package
java.io;
public
interface Externalizable extends Serializabe{
public void writeExternal(ObjectOutput out)
throws IOException;
public void readExternal(ObjectOutput in)
throws IOException, java.lang.ClassNotFoundException;
}
La interfaz Externalizable extiende a Serializable añadiendo dos métodos, writeExternal() y readExternal() que son llamados automáticamente durante la
serialización y la recuperación.
Si un objeto implementa a Externalizable no se serializa
automáticamente nada y se debe especificar
lo que se debe serializar mediante llamadas a writeExternal().
SI un objeto implementa a Externalizable no se serializa
automáticamente nada y se debe especificar lo que se debe serializar mediante
llamadas a writeExternal().
Al contrario que en la
serialización, al recuperar un objeto que ha sido externalizado se llama al
constructor por defecto así que éste debe ser accesible.
Ejemplo
ARCHIVO USUARIO.JAVA
import
java.io.*;
import
java.util.*;
public
class Usuario implements Externalizable{
// Propiedades
private String usuario;
private String password;
// Constructor
public Usuario(){
System.out.println("Creando usuario
vacío");
}
Usuario(String u, String p){
System.out.println("Creanado
usuario (" + u + ", " + p + ")");
usuario = u;
password = p;
}
// Métodos
public void writeExternal(ObjectOutput out)
throws IOException{
System.out.println("Usuario.WriteExternal");
// Indicación explícita de los
atributos a almacenar
out.writeObject(usuario);
}
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException{
System.out.println("Usuario.readExternal");
// Indicación explícita de los
atributos a recuperar
usuario = (String)in.readObject();
}
public void muestraUsuario(){
String cad = "Usuario: " +
usuario + " Password: ";
if (password == null)
cad = cad + "No
disponible";
else
cad = cad + password;
System.out.println(cad);
}
}
ARCHIVO LISTAUSUARIOS.JAVA
import
java.io.*;
import
java.util.LinkedList;
import
java.util.ListIterator;
public
class ListaUsuarios implements Serializable{
// Propiedades
private LinkedList lista = new
LinkedList();
int valor;
// Constructor
ListaUsuarios(String[] usuarios, String[]
passwords){
for (int i = 0; i <
usuarios.length; i++)
lista.add(new
Usuario(usuarios[i], passwords[i]));
}
// Métodos
public void muestraUsuarios(){
ListIterator li = lista.listIterator(0);
Usuario u;
while(li.hasNext()){
u = (Usuario) li.next();
u.muestraUsuario();
}
}
}
ARCHIVO DEMOEXTERNALIZABLE.JAVA
import
java.io.*;
public
class DemoExternalizable {
public static void main(String[] args)
throws IOException, ClassNotFoundException{
System.out.println("Creando el
objeto");
String[] usuarios =
{"Inazio", "Chuse", "Claver"};
String[] passwords =
{"1111", "2222", "3333"};
ListaUsuarios lp = new ListaUsuarios(usuarios,
passwords);
System.out.println("Almacenando
objeto");
ObjectOutputStream o = new
ObjectOutputStream(new FileOutputStream("D:\\objetos.out"));
o.writeObject(lp);
o.close();
System.out.println("Recuperando
objeto");
ObjectInputStream in = new
ObjectInputStream(new FileInputStream("D:\\objectos.out"));
lp = (ListaUsuarios)in.readObject();
lp.muestraUsuarios();
}
}
Y la salida nos mostrará este
resultado
Creando
el objeto
Creanado
usuario (Inazio, 1111)
Creanado
usuario (Chuse, 2222)
Creanado
usuario (Claver, 3333)
Almacenando
objeto
Usuario.WriteExternal
Usuario.WriteExternal
Usuario.WriteExternal
Recuperando
objeto
Creando
usuario vacío
Usuario.readExternal
Creando
usuario vacío
Usuario.readExternal
Creando
usuario vacío
Usuario.readExternal
Usuario:
Inazio Password: No disponible
Usuario:
Chuse Password: No disponible
Usuario:
Claver Password: No disponible
0 comentarios:
Publicar un comentario