5 Escritura y Lectura de Objeto¶
Introducción¶
Si se quiere guardar en archivo un objeto con varios atributos, se podría guardar en un fichero, guardando cada atributo por separado como un dato primitivo. Pero esto se puede volver engorroso si hay una gran cantidad de objetos.
Para ello, Java permite guardar objetos en ficheros binarios, por lo que la clase tiene que implementar la interfaz Serializable
.
Interfaz Serializable¶
La interfaz serializable es un interfaz que permite guardar objetos en fichero binarios. Para que un objeto pueda ser escrito en un fichero binario, la clase de dicho objeto debe implementar dicha interfaz.
La interfaz dispone de una serie de métodos con los que podremos guardar y leer objetos en ficheros binarios. Lo más importantes son:
Object readObject()
: Se utiliza para leer un objeto del ObjectInputStream. Puede lanzar las excepciones IOException y ClassNotFoundException.void writeObject(Object obj)
: Se utiliza para escribir el objeto especificado en el objeto ObjectOutputStream. Puede lanzar la excepción IOException.
La serialización de objetos de Java permite tomar cualquier objeto que implemente la interfaz Serializable y convertido en una secuencia de bits que puede ser posteriormente restaurada para regenerar el objeto original.
Para leer y escribir objetos serializables a un stream se utilizan las clases de Java ObjectInputStream y ObjectOutputStream respectivamente.
import java.io.Serializable;
public class Person implements Serializable{
private String name;
private int age;
public Person(String name, int age){
this.name = name;
this.age = age;
}
public Person(){
this.name = null;
}
public void setName(String name){this.name = name;}
public void setAge(int age){this.age = age;}
public String getName(){return this.name;}
public int getAge(){return this.age;}
}
Escritura con objetos¶
Para escribir un objeto en un fichero, será necesario crear un flujo de salida a disco con FileOutputStream y crear el flujo de salida ObjectOutputStream que es el que procesa los datos y se ha de vincular al fichero de FileOutputStream
El método writeObject() escribe los objetos al flujo de salida y los guarda en el fichero en disco.
public class WriteObjects {
public void show() throws IOException {
Person p; // Define la variable persona
File f = new File("person.dat"); // Declara el fichero
FileOutputStream file = new FileOutputStream(f); // Crea el flujo de salida
ObjectOutputStream writer = new ObjectOutputStream(file);
String[] names = {"Ana", "Luis Miguel", "Alicia", "Pedro", "Manuel", "Andrés",
"Julio", "Antonio", "María Jesús"};
int[] ages = {14,15,13,15,16,12,16,14,13};
for(int i = 0; i < ages.length; i++){
p = new Person(names[i], ages[i]);
writer.writeObject(p); // Escribe la persona en el fichero
}
writer.close(); // Cierra el stream de salida
}
public static void main(String[] args) throws IOException {
new WriteObjects().show();
}
}
Lectura con objetos¶
Para leer los objetos de un fichero se necesita el flujo de entrada a disco FileInputStream y a continuación crear el flujo de entrada ObjectInputStream que es el que procesa los datos y se ha de vincular al fichero de FileInputStream
El método readObject() lee los objetos del flujo de entrada, puede lanzar la excepción ClassNotFoundException e IOException, por lo que habrá que controlarlas. El proceso de lectura se hace en un bucle while(true), este se encierra en un bloque try-catch ya que la lectura finalizará cuando llegue al final del fichero, por lo que lanzará la excepción EOFException.
public class ReadObjects {
public void show() throws IOException, ClassNotFoundException {
Person p; // Define la variable persona
File f = new File("files/PersonFile.dat");
FileInputStream file = new FileInputStream(f); // Crea el flujo de entrada
ObjectInputStream object = new ObjectInputStream(file); // Conecta el flujo de bytes al flujo de datos
try{
while(true){
p = (Person) object.readObject();
System.out.printf("Nombre: %s, edad: %d \n", p.getName(), p.getAge());
}
} catch (EOFException eo){
System.out.println("Fin de Lectura");
}
object.close(); // Cerrar stream de entrada
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
new ReadObjects().show();
}
}
Problema con los ficheros de objetos¶
Existe un problema con los ficheros de objetos. Al crear un fichero de objetos se crea una cabecera inicial con información y a continuación se añaden los objetos. Si el fichero se utiliza de nuevo para añadir más registros, se crea una nueva cabecera y se añaden los objetos a partir de esa cabecera. El problema surge al leer el fichero cuando en la lectura se encuentra con la segunda cabecera, y aparece la excepción StreamCorruptedException y no se puede leer más objetos.
La cabecera se crea cada vez que se pone crea un objeto de la clase ObjectOutputStream(f).
Para que no se añadan dichas cabeceras se suele redefinir la clase ObjectOutputStream creando una nueva clase que la herede, y dentro de dicha clase se redefine el método writeStreamHeader(), que se encarga de escribir las cabeceras.
De esta forma, si el fichero ya se ha creado se llamará a ese método de la clase redefinida.
public class MyObjectOutputStream extends ObjectOutputStream{
public MyObjectOutputStream(OutputStream out) throws IOException{
super(out);
}
protected MyObjectOutputStream() throws IOException, SecurityException{
super();
}
@Override
protected void writeStreamHeader(){}
}
A la hora de usarlo dentro de nuestro programa, a la hora de abrir el fichero para añadir nuevos objetos se pregunta si ya existe, en caso afirmativo crea el objeto, y si no existe se crea con la clase ObjectOutputStream
File f = new File("files/PersonFile.dat");
public class WriteObject {
public void show() throws IOException {
File f = new File("files/PersonFile.dat");
ObjectOutputStream writer;
Person p;
if(!f.exists()){
FileOutputStream file = new FileOutputStream(f);
writer = new ObjectOutputStream(file);
} else {
writer = new MyObjectOutputStream(new FileOutputStream(f, true));
}
String[] names = {"Ana", "Luis Miguel", "Alicia", "Pedro", "Manuel", "Andrés",
"Julio", "Antonio", "María Jesús"};
int[] ages = {14,15,13,15,16,12,16,14,13};
for(int i = 0; i < ages.length; i++){
p = new Person(names[i], ages[i]);
writer.writeObject(p); // Escribe la persona en el fichero
}
writer.close(); // Cierra el stream de salida
}
public static void main(String[] args) {
new WriteObject().show();
}
}