Sei sulla pagina 1di 6

Serialización de objetos en Java 2

Introducción
La serialización de un objeto consiste en obtener una secuencia de bytes que represente el estado de
dicho objeto. Esta secuencia puede utilizarse de varias maneras (puede enviarse a través de la red,
guardarse en un fichero para su uso posterior, utilizarse para recomponer el objeto original, etc.).

Estado de un objeto
El estado de un objeto viene dado, básicamente, por el estado de sus campos. Así, serializar un
objeto consiste, básicamente, en guardar el estado de sus campos. Si el objeto a serializar tiene
campos que a su vez son objetos, habrá que serializarlos primero. Éste es un proceso recursivo que
implica la serialización de todo un grafo (en realidad, un árbol) de objetos.
Además, también se almacena información relativa a dicho árbol, para poder llevar a cabo la
reconstrucción del objeto serializado.
En ocasiones puede interesar que un atributo concreto de un objeto no sea serializado. Esto se puede
conseguir utilizando el modificador transient, que informa a la JVM de que no nos interesa
mantener el valor de ese atributo para serializarlo o hacerlo persistente.
Ejemplo:
public class MiFecha
{
protected int n;
protected Date fecha;
protected transient long s;
. . .
}
En este ejemplo, los atributos n y fecha serán incluídos en la secuencia de bytes resultante de
serializar un objeto de clase MiFecha. El atributo s no será incluído, por tener el modificador
transient.
Objetos serializables. Interfaz Serializable
Un objeto serializable es un objeto que se puede convertir en una secuencia de bytes. Para que un
objeto sea serializable, debe implementar la interfaz java.io.Serializable. Esta interfaz no define
ningún método. Simplemente se usa para 'marcar' aquellas clases cuyas instancias pueden ser
convertidas a secuencias de bytes (y posteriormente reconstruidas). Objetos tan comunes como
String, Vector o ArrayList implementan Serializable, de modo que pueden ser serializados y
reconstruidos más tarde.
Para serializar un objeto no hay más que declarar el objeto como serializable:
public class MiClase implements java.io.Serializable
El sistema de ejecución de Java se encarga de hacer la serialización de forma automática.

Ejemplos
Almacenamiento de objetos
Es posible utilizar los mecanismos de serialización disponibles para serializar un objeto
guardándolo en un fichero y para realizar el proceso inverso, recuperándolo desde el fichero.
FileOutputStream fos = new FileOutputStream("fichero.bin");
FileInputStream fis = new FileInputStream("fichero.bin");
ObjectOutputStream out = new ObjectOutputStream(fos);
ObjectInputStream in = new ObjectInputStream(fis);
ClaseSerializable o1 = new ClaseSerializable();
ClaseSerializable o2 = new ClaseSerializable();
// Escribir el objeto en el fichero
out.writeObject(o1);
out.writeObject(o2);
. . .
// Leer el objeto del fichero (en el mismo orden !!)
o1 = (ClaseSerializable)in.readObject();
o2 = (ClaseSerializable)in.readObject();

Envío de objetos por la red


También es posible enviar un objeto serializado a través de la red. La diferencia consiste en que
ahora se utilizan streams de distinto tipo.
Socket socket = new Socket(maquina, puerto);
OutputStream os = socket.getOutputStream();
InputStream is = socket.getInputStream();
ObjectOutputStream out = new ObjectOutputStream(os);
ObjectInputStream in = new ObjectInputStream(is);
PeticionSerializable ps = new PeticionSerializable();
RespuestaSerializable rs;
// Escribir una petición en el socket
out.writeObject(ps);
// Recibir del socket la respuesta
rs = (RespuestaSerializable)in.readObject();

Serialización en RMI
En RMI, la serialización se utiliza de forma casi transparente al usuario. Concretamente, se utiliza
en el paso de parámetros y retorno de valores de las invocaciones a métodos de objetos remotos.
Por ejemplo, cuando hacemos una invocación remota del tipo retorno obj.metodo(param);

ocurre el siguiente proceso, de forma transparente al usuario:


1. (Local) El objeto param se serializa y se envía al objeto remoto como una secuencia de
bytes
2. (Remoto) Se obtiene el objeto original a partir de la secuencia de bytes
3. (Remoto) Se ejecuta el método y se obtiene un valor de retorno
4. (Remoto) El valor de retorno se serializa y se envía como una secuencia de bytes
5. (Local) Se obtiene el retorno a partir de la secuencia de bytes
Para que esta invocación se lleve a cabo, es necesario que tanto los parámetros de las invocaciones
remotas como los valores devueltos pertenezcan a clases serializables.
´
Serialización personalizada
En ocasiones puede interesar tomar el control sobre el proceso de serialización de una clase en
concreto. Esto se puede hacer 'sobrecargando' los métodos writeObject y readObject de la clase
cuya serialización se quiere controlar. En realidad, no se puede hablar de sobrecarga, puesto que
estos métodos no están definidos en java.lang.Object. Este punto es un poco oscuro. Puede
consultarse el API al respecto (método writeObject(Object) de ObjectOutputStream
java.io.ObjectOutputStream y método readObject() de ObjectInputStream) y el JavaTutorial
(Essential Java Classes -> Reading and Writing (but no 'rithmetic) -> Object Serialization ->
Providing Object Serialization for Your Classes).
Para 'personalizar' la serialización de un objeto, basta añadir un método tal que:
private void writeObject (ObjectOutputStream stream)
throws IOException
{
stream.defaultWriteObject();
. . .
}
Es necesario respetar exactamente tanto la signatura del método como la primera acción a realizar.
A continuación pueden añadirse otras acciones que escriban en el stream dado.
También será necesario añadir un método para hacer el paso inverso:
private void readObject (ObjectInputStream stream)
throws IOException
{
stream.defaultReadObject();
. . .
}
Aquí hay un ejemplo muy sencillo de uso de estos dos métodos. Con los println es posible
comprobar que realmente se ejecutan cuando se produce la serialización y la reconstrucción de un
objeto Prueba.

Interfaz Externalizable
Existe una interfaz en java.io llamada Externalizable que permite obtener un mayor control sobre el
proceso de serialización y reconstrucción de nuestros objetos. Esta interfaz define dos métodos,
writeExternal y readExternal, que se encargan de serializar y reconstruir un objeto, respectivamente.
La serialización mediante Externalizable requiere de un mayor cuidado. De forma automática, sólo
se guarda información relativa a la identidad de la clase del objeto que se está serializando. No se
guarda automáticamente ni su estado ni información relativa a sus superclases. Por ello, en la
implementación de writeExternal hay que guardar explícitamente el estado de aquellos atributos
que nos interesen, incluidos los heredados. A la hora de implementar writeExternal y readExternal,
se ha de tener muy en cuenta la serialización de las clases superiores en el grafo de herencia y
coordinar la implementación de estos métodos con la de los mismos métodos en clases superiores.
RPC

El RPC (del inglés Remote Procedure Call, Llamada a Procedimiento Remoto) es un protocolo
que permite a un programa de ordenador ejecutar código en otra máquina remota sin tener que
preocuparse por las comunicaciones entre ambos. El protocolo es un gran avance sobre los sockets
usados hasta el momento. De esta manera el programador no tenía que estar pendiente de las
comunicaciones, estando éstas encapsuladas dentro de las RPC.

Las RPC son muy utilizadas dentro del paradigma cliente-servidor. Siendo el cliente el que inicia el
proceso solicitando al servidor que ejecute cierto procedimiento o función y enviando éste de vuelta
el resultado de dicha operación al cliente.

Hay distintos tipos de RPC, muchos de ellos estandarizados como pueden ser el RPC de Sun
denominado ONC RPC (RFC 1057), el RPC de OSF denominado DCE/RPC y el Modelo de
Objetos de Componentes Distribuidos de Microsoft DCOM. Ninguno de estos es compatible entre
sí. La mayoría de ellos utilizan un lenguaje de descripción de interfaz (IDL) que define los métodos
exportados por el servidor.

Hoy en día se está utilizando el XML como lenguaje para definir el IDL y el HTTP como protocolo
de red, dando lugar a lo que se conoce como servicios web. Ejemplos de éstos pueden ser SOAP o
XML-RPC.

package ejemplosRMI . hebras ;

import java . rmi. Remote ;


import java . rmi. RemoteException ;

public interface Prueba1 extends Remote {


public int mete ( String s) throws RemoteException , InterruptedException ;
public int saca () throws RemoteException ;
}
package ejemplosRMI . hebras ;

import java . rmi. server . UnicastRemoteObject ;


import java . rmi. RemoteException ;
import java . rmi. RMISecurityManager ;
import java . rmi. Naming ;

public class Prueba1Impl extends UnicastRemoteObject implements Prueba1 {


private int hanEntrado ;
public synchronized int mete ( String s)
throws RemoteException , InterruptedException {
hanEntrado ++;
System . out. println (" Entrando :"+s+":"+"han entrado :"+ hanEntrado );
wait ();
System . out. println (" Saliendo :"+s+":"+"hay dentro :"+ hanEntrado );
return hanEntrado ;
}
public synchronized int saca () throws RemoteException {
System . out. println (" sacando ...");
notify ();
return hanEntrado ;
}
public Prueba1Impl () throws RemoteException {
hanEntrado = 0;
}
}

LENGUAJES DE PROGRAMACIÓN PARALELA

No encontraremos ningún lenguaje de programación para máquinas paralelas tan potente como el
versátil C compilado con GCC. Aún así, existen lenguajes de más alto nivel adecuados para estos
menesteres, entre ellos, cabe citar:

 Fortran:

Fortran, tal y como es hoy es un lenguaje de alto nivel, con numerosas mejoras con respecto al
ANSI del 1966, (objetos como los de C++, instrucciones para procesamiento en paralelo,...)

De echo podemos encontrar compiladores capaces de generar código para arquitecturas paralelas
tipo SMP y Clusters entre otras. Como ejemplo de éstos cito: HPFC (HPF Compiler) que se
encuentra en la siguiente web: http://www.cri.ensmp.fr/~coelho/hpfc.html.

 Mentat:

Mentat es un lenguaje orientado a objetos para procesamiento en paralelo, funciona en máquinas


clusters y se encuentra disponible para Linux. Su sintaxis es similar a la del popular C++.

 Parallaxis-III:

Es un lenguaje de programación estructurada basado en Modula-2 pero con extensiones para


trabajar con máquinas SIMD (Simple Instruction Multiple Data). La distribución básica de este
compilador suele incluir además: un depurador de código y ejemplos de algoritmos.

 PVM:

La PVM (Parallel Virtual Machine) no es más que un API GNU de programación C para máquinas
paralelas, capaz de crear una máquina virtual paralela, es decir, emplea los recursos libres de cada
uno de los nodos sin tener que preocuparnos del cómo, haciéndonos creer que estamos ante una
única super-máquina.

El que para mí es el mejor punto de la PVM es que si disponemos de una red UNIX no tenemos
más que instalar el software que, además, es libre y ya dispondremos de un supercomputador
paralelo con un coste nulo, y podremos ponernos a trabajar con él sin dejar de emplear cada
máquina de la red para las mismas funciones que antes.

Además, PVM está portado para otras máquinas además de Linux, entre las que se encuentran
distintos tipos de UNIX e incluso existe una versión para NT aunque cabe decir que alcanza unas
velocidades bastante inferiores a las que alcanza con Linux.

Como conclusión creo interesante comentar que la PVM ofrece una abstracción inigualable, pero
que, aún así, en muchos casos, sobre todo para tareas con alto grado de acoplamiento, la mejor
solución al menos en lo que a rendimiento se refiere, es un diseño específico, siendo a veces la
única manera posible.

MODELOS DE PROGRAMACIÓN PARALELA

Modelos de Programación para Multiprocesadores


El Modelo de Variables Compartidas
El Modelo de Paso de Mensajes
Paralelización Automática

Modelos de Programación para Multiprocesadores

Modelos de Programación Secuencial


FORTRAN 77
FORTRAN 77 + Librerías Paralelas

Modelos de Programación Paralela


Variables Compartidas
Paso de Mensajes

GLUSTER

Gluster es una distribución GNU para clusters de computadoras orientada a poner a disposicion de
cualquiera que lo desee las herramientas de Supercomputación y Superalmacenamiento. El núcleo
de Gluster provee una plataforma para el desarrollo de aplicaciones de para clusters adaptas a una
tarea específica como lo son Clusterización HPC, Clusters de almacenamiento, Previsión
empresarial, Clusters de Bases de Datos, etc. Gluster está diseñado para una masiva escalabilidad y
desempeño y hace que el difícil uso de operaciones multi-casting y RDMA (Acceso Remoto
Directo a Memoria) sea posible.