Ficheros en
Java
Manejar ficheros en Java es muy sencillo. En el paquete java.io encontramos
las herramientas que necesitamos.
1.
La clase File representa un fichero o directorio (que
no tiene por qué existir todavía). File nos permite averiguar mucha información
útil (ruta del fichero, si se puede escribir, etc).
2.
Por otro lado, si quieremos tratar con ficheros de
texto usaremos streams basados en caracteres, para el resto de casos
utilizaremos streams basados en bytes.
Para el uso de la clase File ver antes sección anterior.
El uso de los streams de ficheros dependerá de lo que queramos hacer…
Para la lectura o escritura en ficheros de texto usamos FileReader
y
FileWriter. Ejemplo:
FileReader
fichLec = new FileReader(cadNombre1);
FileWriter
fichEsc = new FileWriter(cadNombre2);
…
char
caracter = fichLec.read();
fichEsc.write(carácter);
…
Para lectura o escritura de datos en ficheros usamos DataInputStream
y
DataOutputStream. Ejemplo:
DataInputStream
fichLec = new DataInputStream(cad1);
DataOutoutStream
fichEsc = new DataOutputStream(cad1);
…
int
codArticulo = fichLec.readInt();
int
numUnidades = fichLec.readInt();
double
precio = fichLec.readDouble();
fichEsc.writeInt(codArticulo);
fichEsc.writeInt(numUnidades);
fichEsc.writeDouble(precio);
Todos los constructores de streams de ficheros admiten como parámetro
de entrada un objeto de la clase File.
Se aconseja que el acceso a los ficheros se haga mediante un buffer de
memoria para mejorar la eficiencia. Para ello debemos usar un stream con
capacidad de almacenamiento.
Ejemplo:
File
fichero = new File(“pedidos.dat”);
DataOutputStream
dos = new DataOutputStream(fichero);
BufferedOutputStream
stream = new BufferedOutputStream(dos);
stream.writeInt(codArticulo);
stream.writeDouble(precio);
Presistencia de objetos
Hasta ahora todos los objetos que hemos creado se han alojado en la
memoria del ordenador. Ahora queremos almacenarlos en el disco duro, de manera
que pueda recuperarlos cuando reinicie el ordenador.
La persistencia de objetos se ocupa de escribirlos y leerlos desde el
disco duro.
Normalmente los objetos en:
è
Ficheros binarios
è
Bases de datos
Persistencia de objetos en
ficheros
Partimos del siguiente ejemplo:
Persona
p = new Persona(“Ana”, “Lopez Haro”);
Mascota
m = new Mascota(“Tobi”);
Coche
c = new Coche(“4324-FJT”);
p.compraMascota(m);
p.compraCoche(c);
Si ahora quieres escribir el objeto Persona al disco duro, se
escribiría lo siguiente
A la operación de convertir un objeto en una hiera de bytes se le llama
serialización.
Lo que ocurre después es que primero se escribe el árbol de objetos
referenciados por el objeto y después el objeto propio.
Cuando se realiza la lectura del fichero, se hace la operación inversa
y el objeto se reconstruye en memoria con todas sus referencias apuntado a los
objetos adecuados.
Implementación en Java
Para que un objeto pueda ser escrito y leído a un fichoer debe
implementar la interfaz Serializable.
Si el objeto posee referencias a otros objetos, éstos también deben implementar
la interfaz Serializable. En nuestro
ejemplo:
La interfaz Serializable no
contiene ningún método. Actúa como un marcador del objeto, indicando que éste
puede ser serializado.
Por otro lado, necesiamos un stream de entrada y otro de salida capaces
de trabajar con objetos:
è
java.io.ObjectInputStream
è
java.io.ObjectOutputStream
Ahora tenemos que hacer:
1.
Crear un objeto del stream que conecta con el fichero
2.
Conectar dicho stream con el stream de objetos
3.
Llamar al método que nos haga falta
a.
+ void writeObject(Object obj) para escribir un objeto
en disco
b.
+ Object readObject() para leerlo del fichero
4.
Capturar las excepciones que puedan aparecer
5.
Cerrar los streams
import
java.io.Serializable;
public
class Coche implements Serializable{
private String matricula;
private double maxLitros;
}
public
class Mascota implements Serializable{
...
}
public
class PruebaSerializable{
public static void main(String[] args){
FileOutputStream fichSalida = null;
ObjectOutputStream oos = null;
FileinputStream fichEntrada = null;
ObjectInputStream ois = null;
try{
fichSalida = new
FileOutputStream(“c:/persona.obi”);
oos = new
ObjectOutputStream(fichSalida);
oos.writeObject(p);
}
catch(IOException e){
System.out.println(“Se produjo un error
de apertura”);
}
finally{
try{
if(oos != null)
oos.close();
if(fichSalida != null)
fichSalida.close();
}
catch(IOException
e){
System.out.println(“No se consiguió cerrar el
fichero correctamente”);
}
}
try{
fichEntrada = new
FileInputStream(“c:\persona.obi”);
ois = new
ObjectInputStream(fichEntrada);
p = (Persona)ois.readObject();
}
catch(ClassNotFoundException e){
System.out.println(“Contenido de fichero
no esperado”);
}
finally{
try{
if (ois != null)
ois.close();
if (fichEntrada != null)
fichEntrada.close();
}
catch(IOException e){
System.out.println(“No se
consiguió cerrar el fichero correctamente”);
}
}
System.out.println(“Se leyó el fichero
correctamente”);
}
}
Persistencia de objetos en bases
de datos
Según su relación con el enfoque orientado a objetos podemos clasificar
las bases de datos en tres grupos:
è
Relacionales
puras
·
Tecnología madura y eficiente
·
Problema: Se basan en un modelo plano de datos.
Los objetos pueden contener a su vez otros objetos y éstos, otros a su vez.
·
Solución: Bastante costosa en tiempo y esfuerzo.
Hay que establecer una correspondencia entre objetos y tablas haciendo un buen
trabajo de diseño
è
Orientadas
a objetos puras
·
Se soluciona el problema anterior. Ahora la
correspondencia entre objeto de programación y el objeto guardado en disco es
directa, creándose un grafo de objetos interrelacionados y navegables.
·
Tecnología lenta, ineficiente y poco flexible a
la hora de hacer consultas (se pierde la flexibilidad del SQL).
è
Objeto-Relacionales
o mixtas:
·
Combina las ventajas de las dos anteriores
·
Son BBDD relacionales, así se conserva la
velocidad y la eficiencia. Sin embargo incorporan nuevos tipos de datos que
permiten modelar los objetos a elementos de una base de datos relacionales.
·
A partir de la versión 8i de Oracle se
incorporan los nuevos tipos abstractos de datos que pueden relacionarse
mediante herencia y poseen métodos que pueden ser implementados en Java o SQL.
El framework Collection
Tablas, listas, pilas, colas,
árboles, conjuntos, diccionarios…
Las colecciones o contenedores de objetos son estructuras de datos muy
recurridas y necesarias. Su utilización implica normalmente:
è
La adaptación de la colección al tipo de
elemento que queremos almacenar
è
La escritura de los algoritmos que nos hagan
falta: ordenación, búsqueda, recorrido…
Estas tareas son repetitivas, tediosas y propensas a errores.
Java soluciona el problema utilizando las siguientes herramientas:
è
Tipos
genéricos. Permite escribir el código de una colección sin especificar el
tipo del objeto que almacena.
è
FrameWork
Collection. Es una arquitectura unificada para la representación y
manipulación de colecciones de objetos.
Tipos
génericos
Vamos a crear un contenedor muy simple capaz de guardar un objeto de
una clase cualquiera:
public
class Caja{
private Object object;
public void set(Object object){
this.object = object;
}
public Object get(){
return object;
}
}
Si quisiera usar la caja para almacenar SÓLO objetos de la clase
Bombilla podríamos hacer:
public
class PruebaCajaBombillas{
public static void main(String args[]){
Caja cajaBombillas = new Caja();
// Creamos una bombilla y la guardamos
cajaBombillas.set(new
Bombilla());
// Al recuperarla hay que hacer un casting
Bombilla b = (Bombilla)
cajaBombillas.get();
// Nada nos impide guardar una cadena
cajaBombillas.set(new String(“hola”));
// Que provocaría una ClassCastException al
hacer
b = (Bombilla)cajaBombillas.get();
}
}
Esta forma de usar la caja nos plantea los siguientes problemas:
·
Tenemos que acordarnos de hacer el casting al
recuperar el objeto guardado
·
Podemos guardar otro tipo de objeto por
equivocación y el compilador no se da cuenta. Esto provoca que se lance una
excepción en tiempo de ejecución.
Ahora vamos a rescribir nuestra clase Caja usando tipos genéricos y se
resuelven problemas de un golpe:
public
class Caja<Tipo>{
private Tipo objeto;
public void set(Tipo objeto){
this.objeto = objeto;
}
public Tipo get(){
return objeto;
}
}
Indicamos que en la caja va a aparecer un tipo de datos que todavía no
se ha concretado, y después lo usamos como si se conociera.
public
class PruebaCajaBombillas{
public static void main(String args[]){
// Al declarar la referencia y crear el
objeto de la clase cala concreatamos el tipo genérico
Caja<Bombilla> cajaBombillas = new
Caja<Bombilla>();
cajaBombillas.set(new Bombilla());
// Ya no tenemos que hacer el casting
Bombilla b = cajaBombillas.get();
// El compilador identifica el error
cajaBombillas.set(new String(“Hola”));
}
}
Más sobre los tipos genéricos
Se puede usar el tipo genérico como parámetro al crear constructores.
Ejemplo:
public
class Caja(Tipo o){
this.objeto = o;
}
Además podemos usar más de un tipo genérico en una clase. Ejemplo:
public
class Armario<Tipo1, Tipo2>{
Tipo1 cajonSuperior;
Tipo2 cajonInferior;
…
}
Podemos limitar las clases que pueden concretar al tipo genérico.
Ejemplo:
public
class Jaula<Tipo extends Animal>{
// Admite enjaular cualquier objeto Animal
sea de la clase que sea: Tigre, Mosquito, Ballena. Ya no podríamos enjaular un
objeto String
}
Se puede usar con interfaces
public
interface Contenedor<Tipo>{
public void introducir(Tipo cosa);
public void extraer();
public Tipo consultarConenido();
}
Se pueden establecer relaciones de herencia entre clases o interfaces
genéricas, formando jerarquías.
El FrameWork
Collection
Lo forman un conjunto de clases e interfaces ubicadas mayoritariamente
en el paquete java.util y que se estructuran de la siguiente manera:
·
Las interfaces definen las operaciones de cada
colección de elementos
·
Las clases pueden ser de dos tipos
o
Las que implementan las interfaces anteriores
o
Las que implementan los algoritmos “clásicos” de
las colecciones: ordenar, buscar un elemento, buscar máximo…
Veamos que hace cada tipo de interfaz:
·
set(conjunto).
Es una colección de elementos en la que no existe un orden y que no admite
duplicados
·
list(lista).
Es una colección de elementos ordenada y en la que se admiten duplicados
·
queue(cola).
Es una colección de elementos ordenada con comportamiento FIFO (First In, First
Out).
·
map(mapa
o diccionario). Es una colección de pares de objetos Clave – Valor que
funciona como un diccionario en el que se busca una palabra (clave) para
encontrar su definición (Valor).
Las
claves no pueden repetirse pero sí los valores. Ejemplo: ‘súbito’ y ‘repentino’
podrían tener la misma definición.
·
sortedSet
y sortedMap. Además, almacena sus elementos según un criterio de orden.
Las implementaciones de las interfaces se basan en distintas
estructuras de datos.
·
ArrayList.
Tabla capaz de redimensionarse
·
TreeMap y
TreeSet. Árbol
·
LinkedList.
Lista enlazada
·
HashMap y
Haztree. Tabla de dispersión (o tabla Hash).
·
LinkedHashMap
y LinkedHashSet. Tabla de dispersión combinada con una lista enlazada que
mantiene el orden en el que se insertaron los pares Clave – Valor.
Programa de ejemplo. PruebaHashMap.java
import
java.util.HashMap;
import
java.io.*;
public
class PruebaHashMap{
public static void main(String [] args)
throws IOException{
HashMap<String, String> diccionario =
new HashMap<String, String>();
String palabra, respuesta;
Teclado t = new Teclado();
diccionario.put(“Hola”, “Hello”);
System.out.println(“Mini diccionario
español - inglés”);
do{
palabra = t.leerString(“dime una
palabra”);
if (diccionario.containKey(palabra))
System.out.println(palabra + “ se
dice en inglés ” + diccionario.get(palabra));
else
System.out.println(palabra + “ no
se encuentra en el diccionario”);
respuesta = t.leerString(“Quieres
buscar otra palabra(S/N):”);
}while (respuesta.equalsIgnoreCase(“S”); //
equalsIgnoreCase lee texto no case sensitive
}
}
Recorrido de colecciones
de elementos
Ahora queremos recorrer una colección (de cualquier tipo) desde el
primer hasta el último elemento.
Para ello Java aplica el patrón de diseño iterador que se basa en las
siguienter interfaces:
·
La interfaz Iterable posee sólo un método que
devuelve un objeto que implementar la interfaz Iterator
·
La interfaz Iterator posee los métodos hasNext y
next que nos permite recorrer una colección.
import
java.util.*;
public
class PruebaIteradores{
public static void main(String[] args){
int i;
Interator iter;
LinkedList<String> lista = new
LinkedList<String>();
HashSet<String> conjunto = new
HashSet<String>();
TreeSet<String> arbol = new
TreeSet<String>();
for (i = 1; i <= 10; i++){
lista.add(“Elemento ” + i + “ de la lista”);
conjunto.add(“Elemento ” + i + “ de la
lista”);
arbol.add(“Elemento ” + i + “ de la
lista”);
}
iter = lista.iterator();
while(iter.hasNext())
System.out.println(inter.next);
iter = conjunto.iterator();
while(iter.hasNext())
System.out.println(inter.next);
iter = arbol.iterator();
while(iter.hasNext())
System.out.println(inter.next);
}
}
Las clases que implementan los
algoritmos “clásicos” de las colecciones son:
è
Collections.
Capaz de operar con objetos que implementan la interfaz Collection.
è
Arrays.
Capaz de trabajar con objetos de tipo tabla
Podemos encontrar algoritmos para ordenar, desordenar, buscar el máximo
o mínimo, invertir el orden de los elementos, rellenar todo los elementos con
un valor, convertir de Collection a tabla y viceversa…
Estos algoritmos se presentan como métodos estáticos y que usan tipos
genéricos.
Nos fijamos en una de las versiones del método de ordenación de la
clase Collections:
public
static void sort(List<T> lista);
La documentación de Java dice que ordena una lista según el orden natural de los elementos… ¿y qué
orden es ese?
Para establecer el orden natural de los objetos de una clase Java
obliga a implementar la interfaz Comparable.
Si hacemos
objeto1.compareTo(objeto2)
Se debe obtener
·
-1 si objeto1 < objeto2
·
0 si objeto1 = objeto2
·
1 si objeto1 > objeto2
Programa de ejemplo:
public
class Persona implements Comparable{
// Propiedades
private String nombre;
private String apellido;
private String dni;
// Métodos de comparación
public int compareTo(Object o){
String nomCompleto = apellido + “ ” +
nombre;
Persona p = Persona(o);
return
nombreCompleto.compareToIgnoreCase.p.getApellido() + “ ” + p.getNombre();
}
}
import
java.util.*;
public
class PruebaOrdenacion{
public static void main(String[] args){
Iterator iter;
LinkedList<Persona> lista = new
LinkedList<Persona>();
lista.add(new Persona(“Juan”, “Lopez
Torres”);
lista.add(new Persona(“Ana”, “Lopez
Torres”);
lista.add(new Persona(“Paula”, “Zambrano Sanchez”);
lista.add(new Persona(“Ambrosio”, “Aguilar
Ramos”);
lista.add(new Persona(“Maria”, “Lopez
Torres”);
iter = lista.iterator();
while (iter.hasNext())
System.out.println(iter.next());
System.out.println(“Y ahora ordenados…”);
Collections.sort(lista);
iter = lista.iterator();
while (iter.hasNext())
System.out.println(iter.next());
}
}
La clase Arrays se comporta
de la misma forma que Collections
pero para objeto de tipo tablas (arrays).
0 comments:
Publicar un comentario