SlideShare una empresa de Scribd logo
1 de 22
Descargar para leer sin conexión
TEMA 1: MANEXO DE FICHEIROS
1. INTRODUCCIÓN
Un ficheiro ou arquivo é un conxunto de bits almacenado nun dispositivo, como por exemplo un
disco duro. A vantaxe de utilizar ficheiros é que os datos que gardamos permanecen no dispositivo
aínda cando apaguemos o ordenador, é dicir, non son volátiles.
2. CLASES ASOCIADAS ÁS OPERACIÓNS DE XESTIÓN DE
FICHEIROS
Antes de ver as clases que len e escreben datos en ficheiros, vamos manexar a clase File. Esta clase
proporciona un conxunto de utilidades relacionadas cos ficheiros que nos van proporcionar
información acerca dos mesmos, o seu nome, os seus atributos, os directorios, etc. Pode representar
o nome dun ficheiro particular ou os nomes dun conxunto de ficheiros dun directorio, tamén se
pode usar para crear un novo directorio ou unha traxectoria de directorios completa se esta non
existe.
Para crear un obxecto File, pódese utilizar calquera dos tres construtores seguintes:
• File (String directorio e ficheiro)
• File (String directorio, String nomeFicheiro)
• File (File directorio, String ficheiro)
Algúns métodos importantes do obxecto File son:
• getName()
• getPath
• getAbsolutePath()
• canRead()
• canWrite()
• length()
• createNewFile()
• delete()
• exists()
• getParent()
• isDirectory()
• isFile()
Acceso a Datos. Curso 2012-13 1
• mkdir()
• renameTo(File novoNome)
3. FLUXOS OU STREAMS. TIPOS.
O sistema de entrada/saída en Java presenta unha grande cantidade de clases que se implementan no
paquete java.io. Usa a abstracción do fluxo (stream) para tratar a comunicación de información
entre unha fonte e un destino; esta información pode estar nun ficheiro no disco duro, na memoria,
nalgún lugar da rede, e incluso, noutro programa. Calquera programa que teña que obter
información de calquera fonte necesita abrir un stream, igualmente se necesita enviar información
abrirá un stream e escribirase a información a través do mesmo. A vinculación dese stream ó
dispositivo físico faina o sistema de entrada e saída de Java. Defínense dous tipos de fluxos:
• Fluxos de bytes (8 bits): realizan operacións de entradas e saídas de bytes e o seu uso está
orientado á lectura/escritura de datos binarios. Tódalas clases de fluxos de bytes descenden
das clases InputStream e OutputStream, cada unha destas clases teñen varias subclases
que controlan as diferencias entre os distintos dispositivos de entrada/saída que se poden
utilizar.
• Fluxos de caracteres (16 bits): realizan operacións de entradas e saídas de caracteres. O
fluxo de caracteres ven gobernado polas clases Reader e Writer. A razón de ser destas
clases é a internacionalización; a antiga xerarquía de fluxos de E/S só soporta fluxos de 8
bits, non manexando caracteres Unicode de 16 bits que se utilizaba con fines de
internacionalización.
Acceso a Datos. Curso 2012-13 2
3.1. FLUXOS DE BYTES (BYTE STREAMS)
A clase InputStream representa as clases que producen entradas de distintas fontes, estas fontes
poden ser: un array de bytes, un obxecto String, un ficheiro, unha “tubería” (póñense os elementos
nun extremo e saen polo outro), unha secuencia doutros fluxos, outras fontes como unha conexión a
Internet, etc. Os tipos de InputStream resúmense na seguinte táboa:
CLASE FUNCIÓN
ByteArrayInputStream Permite usar un espazo de almacenamento
intermedio de memoria.
StringBufferInputStream Converte un String nun InputStream
FileInputStream Para ler información dun ficheiro.
PipepInputStream Implementa o concepto de “tubería”
FilterInputStream Proporciona funcionalidade útil a outras clases
InputStream
SequenceInputStream Converte dous ou máis obxectos InputStream
nun InputStream único
Os tipos de OutputStream inclúen as clases que deciden onde irá a saída: a un array de bytes, un
ficheiro ou unha “tubería”. Resúmense na seguinte táboa:
CLASE FUNCIÓN
ByteArrayOutputStream Crea un espazo de almacenamento intermedio en
memoria. Tódolos datos que se envían ó fluxo
ubícanse neste espazo
FileOutputStream Para enviar información a un ficheiro
PipedOutputStream Calquera información que se desexe escribir
aquí acaba automaticamente como entrada do
PipedInputStream asociado. Implementa o
concepto de “tubería”
FilterOutputStream Proporciona fincionalidade útil a outras clases
OutputStream
Dentro dos fluxos de bytes están as clases FileInputStream e FileOutputStream que manipulan os
fluxos de bytes procedentes ou dirixidos a ficheiros en disco e estudaránse nos seguintes apartados.
Acceso a Datos. Curso 2012-13 3
3.2. FLUXOS DE CARACTERES (CHARACTER STREAMS)
As clases Reader e Writer manexan bytes en combinación coas clases que nanexan caracteres
Unicode. Hai ocasións nas que hai que usar as clases que manexan bytes en combinación coas
clases que manexan caracteres. Para lograr isto hai clases “ponte” (é dicir, converte os streams de
bytes a streams de caracteres): InputStreamReader que converte un InputStream nun Reader (le
bytes e os converte en caracteres) e OutputStreamReader que converte un OutputStream nun Writer.
A seguinte táboa mostra a correspondencia entre as clases de fluxos de bytes e de caracteres:
CLASES DE FLUXOS DE BYTES CLASE CORRESPONDENTE DE FLUXO DE
CARACTERES
InputStream Reader, convertidor InputStreamReader
OutputStream Writer, convertidor OutputStreamWriter
FileInputStream FileReader
FileOutputStream FileWriter
StringBufferInputStream StringReader
(sen clase correspondente) StringWriter
ByteArrayInputStream CharArrayReader
ByteArrayOutputStream CharArrayWriter
PipedInputStream PipedReader
PipedOutputStream PipedWriter
As clases de fluxos de caracteres máis importantes son:
• Para acceso a ficheiros, lectura e escritura de caracteres en ficheiros: FileReader e
FileWriter.
• Para acceso a caracteres, len e escreben un fluxo de caracteres nun array de caracteres
CharArrayReader e CharArrayWriter.
• Para buferización de datos: BufferedReader e BufferedWriter, utilízanse para evitar que cada
lectura ou escritura acceda directamente ó ficheiro, xa que utilizan un buffer intermedio
entre a memoria e o stream.
Acceso a Datos. Curso 2012-13 4
4. FORMAS DE ACCESO A UN FICHEIRO.
Hai dúas formas de acceso á información almacenada nun ficheiro: acceso secuencial e acceso
directo ou aleatorio:
• Acceso secuencial: os datos ou rexistros lense e escrébense en orde, do mesmo xeito que se
fan nunha antiga cinta de vídeo. Se se quere acceder a un dato ou un rexistro que está cara á
metade do ficheiro é necesario ler antes tódolos anteriores. A escritura de datos farase a
partir do último dato escrito, non é posible facer insercións entre os datos que xa hai
escritos.
• Acceso directo ou aleatorio: permite acceder directamente a un dato ou rexistro sen
necesidade de ler os anteriores e pódese acceder á información en calquera orde. Os datos
están almacenados en rexistros de tamaño coñecido, podémonos mover dun rexistro a outro
de forma aleatoria para lelos ou modificalos.
En Java o acceso secuencial máis común en ficheiros pode ser binario ou a caracteres. Para o acceso
binario úsanse as clases FileInputStream e FileOutputStream. Para o acceso a caracteres (texto)
úsanse as clases FileReader e FileWriter. No acceso aleatorio utilízase a clase RandomAccessFile.
5. OPERACIÓNS SOBRE FICHEIROS.
As operacións básicas que se realizan sobre calquera ficheiro independentemente da forma de
acceso ó mesmo son as seguintes:
• Creación do ficheiro. O ficheiro créase no disco cun nome que despois se debe utilizar para
acceder a el. A creación é un proceso que se realiza unha vez.
• Apertura do ficheiro. Para que un programa poida operar cun ficheiro, a primeira operación
que ten que realizar é a apertura do mesmo. O programa utilizará algún método para
identificar o ficheiro co que quere traballar, por exemplo asignar a unha variable o descritor
do ficheiro.
• Peche do ficheiro. O ficheiro débese cerrar cando o programa non o vaia utilizar.
Normalmente soa ser a última instrución do programa.
• Lectura dos datos do ficheiro. Este proceso consiste en transferir información do ficheiro á
memoria principal, normalmente a través de algunha variable ou variables do noso
programa, nas que se depositarán os datos extraídos do ficheiro.
• Escritura de datos no ficheiro. Neste caso o proceso consiste en transferir información da
memoria (por medio das variables do programa) ó ficheiro.
Normalmente as operacións típicas que se realizan sobre un ficheiro unha vez aberto son as
seguintes:
• Altas: consiste en engadir un novo rexistro ó ficheiro.
• Baixas: consiste en eliminar do ficheiro un rexistro xa existente.
• Modificacións: consiste en cambiar parte do contido dun rexistro.
• Consultas: consiste en buscar no ficheiro un rexistro determinado.
Acceso a Datos. Curso 2012-13 5
6. CLASES PARA XESTIÓN DE FLUXOS DE DATOS
DENDE/HACIA FICHEIROS.
En Java podemos utilizar dous tipos de ficheiros: de texto ou binarios; e o acceso ós mesmos pódese
realizar de forma secuencial ou aleatoria. Os ficheiros de textxo están compostos de caracteres
lexibles, mentres que os binarios poden almacenar calquera tipo de dato (int, float, boolean,...)
6.1. FICHEIROS DE TEXTO.
Os ficheiros de texto, os que normalmente se xeran cun editor, almacenan caracteres alfanuméricos
nun formato estándar (ASCII, UNICODE, UTF8, etc.). Para traballar con eles usaremos as clases
FileReader para ler caracteres e FileWriter para escribir os caracteres no ficheiro. Cando
traballamos con ficheiros, cada vez que lemos ou escribimos nun, debemos facelo dentro dun
manexador de excepcións try-catch. Ó usar a clase FileReader pódese xerar a excepción
FileNotFoundException (porque o nome do ficheiro non existe ou non sexa válido) e ó usar a clase
FileWriter a excepción IOException (o disco está cheo ou protexido contra escritura).
Os métodos que proporciona a clase FileReader para lectura son os seguintes, estes métodos
devolven o número de caracteres lidos ou -1 se chegou ó final do ficheiro:
• int read(): le un caracter e o devolve.
• int read(char[] buf): le hasta buf.length caracteres de datos dunha matriz de caracteres (buf).
Os caracteres lidos do ficheiro vanse almacenando en buf.
• int read(char[] buf, int desprazamento,int n): le ata n caracteres de datos da matriz buf
comezando por buf[desprazamento] e devolve o número lido de caracteres.
Nun programa Java para crear ou abrir un ficheiro invócase á clase File e a continuación, créase o
fluxo de entrada coa clase FileReader. Despois realízanse as operacións de lectura ou escritura e
cando terminemos de usalo o cerraremos mediante o método close().
Os métodos que proporciona a clase FileWriter para escritura son:
• void write(int c): escrebe un caracter.
• void write(char[] buf): escrebe un array de caracteres.
• void write(char[] buf, int desprazamento, int n): escrebe n caracteres de datos na matriz buf
e comezando por buf[desprazamento].
• void write(String str): escrebe unha cadea de caracteres.
• append(char c): engade un caracter a un ficheiro.
FileReader non contén métodos que nos permitan ler liñas completas, pero BufferedReader si;
dispón do método readLine() que le unha liña do ficheiro e a devolve, ou devolve null se non hai
nada que ler ou chegamos ó final do ficheiro. Tamén dispón do método read() para ler un caracter.
Para construír un BufferedReader necesitamos a clase FileReader:
BufferedReader ficheiro = new BufferedReader (new FileReader (nomeFicheiro));
A clase BufferedWriter engade un buffer para realizar unha escritura eficiente de caracteres. Para
construír un BufferedWriter necesitamos a clase FileWriter:
BufferedWriter ficheiro = new BufferedWriter (new FileWriter (nomeFicheiro));
Acceso a Datos. Curso 2012-13 6
A clase PrintWriter posúe os métodos print(String) e println (String) (idénticos ós de System.out)
para escribir nun ficheiro. Ambos reciben un String e o imprimen nun ficheiro, o segundo método
salta de liña. Para construír un PrintWriter necesitamos a clase FileWriter:
PrintWriter ficheiro = new PrintWriter (new FileWriter(nomeFicheiro));
6.2. FICHEIROS BINARIOS.
Os ficheiros binarios almacenan secuencias de díxitos binarios que non son lexibles directamente
polo usuario como sucedía cos ficheiros de texto. Teñen a vantaxe de que ocupan menos espazo en
disco. En Java, as dúas clases que nos permiten traballar con ficheiros son FileInputStream (para
entrada) e FileOutputStream (para saída), estas traballan con fluxos de bytes e crean un enlace entre
o fluxo de bytes e o ficheiro.
Os métodos que proporciona a clase FileInputStream para lectura son similares ós vistos para a
clase FileReader, estes métodos devolven o número de bytes lidos ou -1 se se chegou ó final do
ficheiro:
• int read(): le un caracter e o devolve.
• int read(byte[] b): le hasta b.length bytes dunha matriz de bytes.
• int read(byte[] b, int desprazamento,int n): le ata n bytes da matriz b comezando por
b[desprazamento] e devolve o número lido de bytes.
Os métodos que proporciona a clase FileOutputStream para escritura son:
• void write (int b): escrebe un byte.
• void write(byte[] b): escrebe un array de bytes.
• void write(byte[] b, int desprazamento, int n): escrebe n bytes na matriz b e comezando por
b[desprazamento].
Para engadir bytes ó final do ficheiro usaremos FileOutputStream da seguinte maneira, colocando
no segundo parámetro do construtor o valor true:
FileOutputStream fileout = new FileOutputStream (ficheiro, true);
Para ler e escribir datos de tipos primitivos: int, float, long, etc. usaremos as clases DataInputStream
e DataOutputStream. Estas clases ademais dos métodos read() e write() vistos anteriormente
proporcionan métodos para a lectura e escritura de tipos primitivos dun modo independiente da
máquina. Algúns dos métodos móstranse na seguinte táboa:
MÉTODOS PARA LECTURA MÉTODOS PARA ESCRITURA
boolean readBoolean();
byte readByte();
short readShort()
char readChar()
int readInt()
float readFloat()
double readDouble()
String readUTF()
void writeBoolean(boolean v)
void writeByte(int v)
void writeBytes(String s)
void writeChars(String s)
void writeChar (int v)
void writeInt(int v)
void writeFloat(float v)
void writeDouble(double v)
void writeUTF(String str)
Acceso a Datos. Curso 2012-13 7
Para abrir un obxecto DataInputStream, utilízanse os mesmos métodos que para FileInputStream,
exemplo:
File ficheiro = new File(“C:exerciciosuni1ficheiro.dat”);
FileInputStream filein = new FileInputStream(ficheiro);
DataInputStream dataIS = new DataInputStream(filein);
Para abrir un obxecto DataOutputStream, utilízanse os mesmos métodos que para
FileOutputStream, exemplo:
File ficheiro = new File(“C:exerciciosuni1ficheiro.dat”);
FileOutputStream fileout = new FileOutputStream(ficheiro);
DataOutputStream dataOS = new DataOutputStream(fileout)
OBXECTOS SERIALIZABLES.
Vimos como se gardan os tipos de datos primitivos nun ficheiro, pero por exemplo, se temos un
obxecto de tipo empregado con varios atributos (o nome, a dirección, o salario, o departamento, o
oficio, etc.) e queremos gardalo nun ficheiro, teriamos que gardar cada atributo que forma parte do
obxecto por separado, isto vólvese engorroso se temos grande cantidade de obxectos. Por isto Java
permítenos gardar obxectos en ficheiros binarios; para poder facelo, o obxecto ten que implementar
a interface Serializable.
A interface Serializable non ten métodos nin atributos e serve só para indicar que os obxectos da
clase son serializables.
As clases que se usan durante a serialización e deserialización deben implementar unha serie de
métodos cos que poderemos gardar e ler obxectos en ficheiros binarios. Os máis importantes a
utilizar son:
• void readObject (java.io.ObjectInputStream stream) throws IOException,
ClassNotFoundException: para ler un obxecto.
• void writeObject (ObjectOutputStream stream) throws IOException: para escribir un
obxecto.
A serialización de obxectos de Java permite tomar calquera obxecto que implemente a interface
Serializable e convertilo nunha secuencia de bits, que pode ser posteriormente restaurada para
rexenerar o obxecto orixinal.
Para ler e escribir obxectos serializables a un stream utilízanse as clases Java ObjectInputStream,
que dispón do método readObject(), e ObjectOutputStream, que dispón do método writeObject().
Acceso a Datos. Curso 2012-13 8
6.3. FICHEIROS DE ACCESO ALEATORIO.
Ata agora tódalas operacións que realizamos sobre os ficheiros realizábanse de forma secuencial.
Empezábase a lectura no primeiro byte ou o primeiro caracter ou o primeiro obxecto e
seguidamente, líanse os seguintes un a continuación doutro ata chegar ó final do ficheiro.
Igualmente cando escribiamos os datos no ficheiro íanse escribindo a continuación da última
información escrita. Java dispón da clase RandomAccessFile que dispón de métodos para acceder ó
contido dun ficheiro binario de forma aleatoria (non secuencial) e para posicionarnos nunha
posición concreta do mesmo. Esta clase non é parte da xerarquía InputStream/OutputStream, xa que
o seu comportamento é totalmente distinto posto que se pode avanzar e retroceder dentro dun
ficheiro.
Dispoñemos de dúas posibilidades para crear o ficheiro de acceso aleatorio:
RandomAccessFile ficheiro = new RandomAccessFile(String nome, String modoAcceso);
RandomAccessFile ficheiro = new RandomAccessFile(File fich, String modoAcceso);
O argumento modoAcceso pode ser: “r” para so lectura ou “rw” para lectura e escritura. Unha vez
aberto o ficheiro poden usarse os métodos read() e write() das clases DataInputStream e
DataOutputStream (vistos anteriormente). A clase RandomAccessFile manexa un punteiro que
indica a posición actual no ficheiro. Cando no ficheiro se crea o punteiro colócase en 0, apuntando ó
principio do mesmo. As sucesivas chamadas ós métodos read() e write() axustan o punteiro segundo
a cantidade de bytes lidos ou escritos.
Os métodos máis importantes son:
long getFilePointer: devolve a posición actual do punteiro do ficheiro.
void seek (long posicion): coloca o punteiro do ficheiro nunha posición determinada desde o
comezo do mesmo.
long length(): devolve o tamaño do ficheiro en bytes. A posición length() marca o final do ficheiro.
int skipBytes(int desprazamento): despraza o punteiro desde a posición actual o número de bytes
indicados en desprazamento.
Acceso a Datos. Curso 2012-13 9
7. TRABALLO CON FICHEIROS XML.
Os ficheiros XML pódense utilizar para proporcionar datos a unha base de datos ou para almacenar
copias de partes do contido da base de datos. Tamén se utilizan para escribir ficheiros de
configuración de programas ou no protocolo SOAP (Simple Object Access Protocol), para executar
comandos en servidores remotos; a información enviada ó servidor remoto e o resultado da
execución do comando envíanse en ficheiros XML.
Para ler os ficheiros XML e acceder ó seu contido e estrutura, utilízase un procesador de XML ou
parser. O procesador le os documentos e proporciona acceso ó seu contido e estrutura. Algúns dos
procesadores máis empregados son: DOM: Modelo de Obxectos de Documento e SAX: API Simple
para XML. Son independentes da linguaxe de programación e existen versións particulares para
Java, VisualBasic, C, etc. Utilizan dous enfoques moi diferentes:
DOM: un procesador SML que utilice este plantexamento almacena toda a estrutura do documento
en memoria en forma de árbore con nodos pai, nodos fillo e nodos finais (que son aqueles que non
teñen descendentes). Unha vez creada a árbore, vanse percorrendo os diferentes nodos e analízase a
que tipo particular pertencen. Ten a súa orixe no W3C. Este tipo de procesamento necesita máis
recursos de memoria e tempo sobre todo se os ficheiros XML a procesar son bastante grandes e
complexos.
SAX: un procesador que utilice este plantexamento le un ficheiro SML de forma secuencial e
produce unha secuencia de eventos (comezo/fin de documento, comezo/fin dunha etiqueta, etc.) en
función dos resultados da lectura. Cada evento invoca a un método definido polo programador. Este
tipo de procesamento practicamente non consume memoria, pero por outra parte, impide ter unha
visión global do documento polo que navegar.
Acceso a Datos. Curso 2012-13 10
7.1. ACCESO A FICHEIROS XML CON DOM.
PAra poder traballar con DOM en Java necesitamos as clases e interfaces que compoñen o paquete
org.w3c.dom (contido no JSDK) e o paquete javax.xml.parsers da API estándar de Java que
proporciona un par de clases abstratas que toda implementación DOM para Java debe extender.
Estas clases ofrecen métodos para cargar documentos desde unha fonte de datos (ficheiro,
InputStream, etc.). Contén dúas clases fundamentais: DocumentBuilderFactory e DocumentBuilder.
DOM non define ningún mecanismo para xerar un ficheiro XML a partir dunha árbore DOM. Para
iso usaremos o paquete javax.xml.transform que permite especificar unha fonte e un resultado. A
fonte e o resultado poden ser ficheiros, fluxos de datos ou nodos DOM entre outros.
Interfaces que usan os programas que utilizan DOM (só se indican algunhas):
• Document. É un obxecto que equivale a un exemplar dun documento XML. Permite crear
novos nodos no documento.
• Element. Cada elemento do documento XML ten un equivalente nun obxecto deste tipo.
Expón propiedades e métodos para manipular os elementos do documento e os seus
atributos.
• Node. Representa a calquera nodo do documento.
• NodeList. Contén unha lista cos nodos fillos dun nodo.
• Attr. Permite acceder ós atributos dun nodo.
• Text. Son os datos carácter dun elemento.
• CharacterData. Representa ós datos carácter presentes no documento. Proporciona atributos
e métodos para manipular os datos de caracteres.
• DocumentType. Proporciona información contida na etiqueta <!DOCTYPE>
Acceso a Datos. Curso 2012-13 11
7.2. ACCESO A FICHEIROS XML CON SAX.
SAX (API Simple para XML) é un conxunto de clases e interfaces que ofrecen unha ferramenta moi
útil para o procesamento de documentos XML. Permite analizar os documentos de forma secuencial
(é dicir, non carga todo o ficheiro en memoria como fai DOM), isto implica pouco consumo de
memoria aínda que os documentos sexan de grande tamaño, en contraposición, impide ter unha
visión global do documento que se vai analizar. SAX é máis complexo de programar que DOM, é
un API totalmente escrita en Java e incluída dentro do JRE que nos permite crear o noso propio
parser de XML.
A lectura dun documento XML produce eventos que ocasiona a chamada a métodos, os eventos son
atopar a etiqueta de inicio e fin do documento (startDocument() e endDocument()), a etiqueta de
inicio e fin dun elemento (startElement() e endElement()), os caracteres entre etiquetas
(characters()), etc.
Os obxectos que posúen métodos que tratarán os eventos serán normalmente implementacións das
seguintes interfaces:
• ContentHandler: recibe as notificacións dos eventos que ocorren no documento.
• DTDHandler: recolle eventos relacionados coa DTD
• ErrorHandler: define métodos de tratamentos de erros.
• EntityResolver: os seus métodos chámanse cada vez que se atopa unha referencia a unha
entidade.
• DefaultHandler: clase que prove unha implementación por defecto para tódolos seus
métodos, o programador definirá os métodos que sexan utilizados polo programa.
Acceso a Datos. Curso 2012-13 12
7.3. SERIALIZACIÓN DE OBXECTOS A XML.
Vemos como se poden serializar de forma sinxela obxectos Java a XML e viceversa. Utilizaremos
para isto a librería XStream. Para poder utilizala debemos descargar os JAR desde o sitio web:
http://xstream.codehaus.org/download.html, para o exemplo descargouse o ficheiro Binary
distribution (xstream-distribution-1.4.2-bin.zip) que descomprimimos e buscamos o JAR xstream-
1.4.2.jar que está na carpeta lib que é o que usaremos para o exemplo. Tamén necesitamos o ficheiro
kxml2-2.3.0.jar que se pode descargar desde o apartado Optional Dependencies. Unha vez que
temos os sous ficheiros, os definimos no CLASSPATH.
7.4. CONVERSIÓN DE FICHEIROS XML A OUTRO FORMATO.
XSL (Extensible Stylesheet LAnguage) é toda unha familia de recomendacións do World Wide Web
Consortium (http://www.w3.org/Style/XSL/) para expresar follas de estilo en linguaxe XML. Unha
folla de estilo XSL describe o proceso de presentación a través dun pequeno conxunto de elementos
XML. Esta folla, pode conter elementos de regras que representan ás regras de construcción e
elementos de regras de estilo que representan ás regras de mezcla de estilos.
Acceso a Datos. Curso 2012-13 13
8. EXCEPCIÓNS: DETECCIÓN E TRATAMENTO.
Introdución.
O momento ideal para capturar un erro é en tempo de compilación, antes incluso de intentar executar o
programa. Sen embargo, non tódolos erros se poden detectar en tempo de compilación. O resto dos
problemas deberán ser manexados en tempo de execución, mediante algunha formalidade que permita á
fonte do erro pasar a información adecuada a un receptor que saberá facerse cargo da dificultade de maneira
adecuada.
Na linguaxe Java, unha Excepción é un certo tipo de erro ou unha condición anormal que se produciu
durante a execución do programa. Algunhas excepcións son fatais e provocan que se deba finalizar a
execución do programa. Neste caso convén terminar ordenadamente e dar unha mensaxe explicando o tipo
de erro que se produciu. Outras, como por exemplo non atopar un ficheiro no que hai que ler ou escribir algo,
poden ser recuperables. Neste caso o programa debe dar ó usuario a oportunidade de corrixir o erro
(indicando unha nova localización do ficheiro non atopado).
Un bo programa debe xestionar correctamente todos ou a maior parte dos erros que se poden producir. Hai
dous "estilos" de facer isto:
1. Á "vella usanza": os métodos devolven un código de erro. Este código chequéase no entorno que
chamou ó método cunha serie de if elseif ..., xestionando de forma diferente o resultado correcto ou cada
un dos posibles erros. Este sistema resulta moi complicado cando hai varios niveis de chamadas ós
métodos.
2. Con soporte na propia linguaxe: Neste caso a propia linguaxe proporciona construccións especiais para
xestionar os erros ou excepcións. Coe ser o habitual en linguaxes modernas, como C++, Visual Basic e
Java.
Mediante o uso de excepcións para manexar erros, os programas Java teñen as seguintes vantaxes fronte ás
técnicas de manexo de erros tradicionais:
1. Separar o manexo de erros do código "normal".
2. Propagar os erros sobre a pila de chamadas.
3. Agrupar os tipos de erros e a diferenciación destes.
Acceso a Datos. Curso 2012-13 14
Excepcións estándar de Java.
Os erros represéntanse mediante dous tipos de clases derivadas da clase Throwable: Error e Exception. A
seguinte figura mostra parcialmente a xerarquía de clases relacionada con Throwable:
A clase Error está relacionada con erros de compilación, do sistema ou da JVM. De ordinario estes erros son
irrecuperables e nin dependen do programador nin debe preocuparse de capturalos e tratalos.
A clase Exception ten máis interese. Dentro dela pódense distinguir:
1. RuntimeException: Son excepcións moi frecuentes, de ordinario relacionadas con erros de
programación. Pódense chamar excepcións implícitas.
2. As demais clases derivadas de Exception son excepcións explícitas. Java obriga a telas en conta e
comprobar si se producen.
O caso de RuntimeException é un pouco especial. O propio Java durante a execución dun programa chequea
e lanza automaticamente as excepcións que derivan de RuntimeException. O programador non necesita
establecer os bloques try/catch para controlar este tipo de excepcións. Representan dous casos de erros de
programación:
1. Un erro que normalmente non soe ser chequeado polo programador, como por exemplo recibir unha
referencia null nun método.
2. Un erro que o programador debería ter chequeado ó escirbir o código, como sobrepasar o tamaño
asignado dun array (xera un ArrayIndexOutOfBoundsException automáticamente).
En realidade sería posible comprobar estes tipos de erros, pero o código complicaríase excesivamente si se
necesitara comprobar continuamente todo tipo de erros (que as referencias son distintas de null, que tódolos
argumentos dos métodos son correctos, e un longo etcétera).
Acceso a Datos. Curso 2012-13 15
Capturar unha excepción.
Nos métodos pódense producir excepcións. Si se trata de excepcións explícitas e o usuario chama a estes
métodos sen telo en conta prodúcese un erro de compilación cunha mensaxe do tipo: "...Exception
java.oi.IOException must be caught or it must be declared in the throws clause of this method". O programa
non compilará salvo que o usuario realice unha das seguintes accións:
1. Xestione a excepción cunha construcción do tipo try{...} catch{...}
2. Relance a excepción hacia un método anterior na pila, declarando que o seu método tamén lanza esta
excepción, utilizando para isto a construcción throws na cabeceira do método.
O compilador obriga a capturar as chamadas excepcións explícitas, pero non protesta si se captura e despois
non se fai nada con ela. En xeral, é conveniente polo menos imprimir unha mensaxe indicando que tipo de
excepción se produciu.
Bloques try e catch.
No caso das excepcións que non pertencen ás RuntimeException e que polo tanto Java obriga a telas en conta
haberá que utilizar os bloques try, catch e finally. O código dentro do bloque try está "vixiado": Si se produce
unha situación anormal e se lanza polo tanto unha excepción o control salta ou sae do bloque try e pasa ó
bloque catch, que se fai cargo da situación e decide o que hai que facer. Pódense incluír tantos bloques catch
como sexan necesarios, cada un dos cales tratará un tipo de excepción.
As instruccións inclúense nun bloque coa seguinte sintaxe:
try{
//instruccións susceptibles de causar certo tipo de excepcións
}
catch (TipoDeExcepcion Identificador){
//instruccións de control de situacións excepcionais
}
As excepcións pódense capturar individualmente ou en grupo, por medio dunha superclase da que deriven
todas elas.
Entre o bloque try e o bloque catch non pode haber ningunha instrucción.
Acceso a Datos. Curso 2012-13 16
O funcionamento é o seguinte: execútanse as instruccións do bloque try ata que se produza a situación de
excepción de tipo TipoDeExcepcion; nótese que, habitualmente, non se producirá a excepción e se
executarán tódalas instruccións do bloque try. Si se da a excepción, pásase a executar as instruccións do
bloque catch e, posteriormente, as instruccións seguintes ó bloque catch. Se non se produce a excepción, non
se executan as instruccións do bloque catch.
O bloque finally é opcional. Si se inclúe as súas sentencias execútanse sempre, sexa cal sexa a excepción que
se produza ou se non se produce ningunha. O bloque finally execútase aínda que no bloque try haxa un
return.
Acceso a Datos. Curso 2012-13 17
Acceso a Datos. Curso 2012-13 18
Cando se lanza unha excepción nas instruccións do bloque try, recórrense en orde os bloques catch ata que se
atopa un co mesmo tipo ou un tipo que é superclase da excepción que chegou. Execútanse unicamente as
instruccións do bloque catch que cumpre os requisitos (se é que algún os cumpre).
É posible crear un manexador que capture calquera tipo de excepción. Isto faise capturando a excepción de
clase base Exception:
Isto capturará calquera excepción, de forma que si se usa, haberá que poñelo ó final da lista de manexadores
para evitar que os manexadores que poidan vir despois queden ignorados.
O bloque finally.
O bloque finally{...} debe ir detrás de tódolos bloques catch considerados. Si se inclúe (xa que é opcional) as
súas sentencias execútanse sempre, sexa cal sexa o tipo de excepción que se produza, ou incluso se non se
produce ningunha. O bloque finally execútase incluso se dentro dos bloques try/catch hai unha sentencia
continue, break ou return. A forma xeral dunha sección onde se controlan as excepcións é polo tanto:
Acceso a Datos. Curso 2012-13 19
Relanzar unha excepción.
Existen algúns casos nos cales o código dun método pode xerar unha excepción e non se desexa incluír nese
método a xestión do erro. Java permite que este método pase a excepción ó método desde o que foi chamado,
sen incluír no método os bucles try/catch correspondentes. Isto conséguese mediante a adición de throws
máis os nomes das excepcións despois da lista de argumentos do método. Á súa vez o método superior
deberá incluír os bloques try/catch ou volver a pasar a excepción. Desta forma pódese ir pasando a excepción
dun método a outro ata chegar ó último método do programa, o método main().
O exemplo anterior (metodo1) realizaba a xestión das excepcións dentro do propio método. Agora preséntase
un novo exemplo (metodo2) que relanza as excepcións ó seguinte método:
por exemplo:
public void lerFicheiro(String fich) throws EOFException, FileNotFoundException {...}
Pódese poñer unicamente unha superclase de excepcións para indicar que se poden lanzar excepcións de
calquera das súas clases derivadas. O caso anterior sería equivalente a:
public void lerFicheiro(String fich) throws IOException {...}
xa que as clases EOFException e FileNotFoundException derivan de IOException.
As excepcións poden ser lanzadas directamente por lerFicheiro() ou por algún dos métodos chamados por
lerFicheiro().
En ocasións captúrase a excepción con try/catch dentro do método pero deséxase volver a lanzar a excepción
que se acaba de capturar, especialmente cando se usa Exception para capturar calquera excepción. Dado que
xa se ten a referencia á excepción actual, pódese volver a lanzar esa referencia:
catch (Exception e){
System.err.println("Lanzousa a excepción");
throw e;
}
Volver a lanzar a excepción fai que a excepción vaia ó lugar desde o cal se invocou o método actual.
Acceso a Datos. Curso 2012-13 20
Estado dunha excepción.
As excepcións derivan da clase Throwable e teñen acceso ós seus dous constructores e ós seus sete métodos.
O estado das instancias desta clase componse dun String que serve de mensaxe indicando as características
da excepción e unha pila de execución que contén a relación de métodos que se atopan en execución no
momento no que se produce a excepción.
Os métodos máis significativos da clase Throwable son:
- getMessage(): Devolve (como String) a mensaxe de erro almacenada neste obxecto. Esta mensaxe
componse do nome da excepción mais unha breve referencia.
- printStackTrace(): Imprime polo dispositivo de saída de erros (normalmente a consola) a mensaxe e a
pila de execución almacenados no obxecto Throwable. Este mesmo método está sobrecargado para poder
sacar o resultado por diferentes saídas: printStackTrace(PrintStream s) e printStackTrace(PrintWriter s).
- toString(): Devolve (como String) unha descrición curta do obxecto.
3.10.5. Creación de novas excepcións.
O programador pode crear as súas propias excepcións só con herdar da clase Exception ou dunha das
súas clases derivadas. O lóxico é herdar da clase da xerarquía de Java que mellor se adapte ó tipo de
excepción. As clases Exception soen ter dous constructores:
1. Un constructor sen argumentos.
2. Un constructor que recibe un String como argumento. Neste String sóese definir unha mensaxe que
explica o tipo de excepción xerada. Convén que este constructor chame ó constructor da clase da que
deriva: super(String).
p.e.
class MiExcepcion extends Exception{
MiExcepcion(){ //Constructor por defecto
super();
}
MiExcepcion(String s){ //Constructor con mensaxe
super(s);
}
Unha vez que dispoñemos dunha excepción propia, poderemos programar a funcionalidade das nosas
aplicacións lanzando a excepción cando detectemos algunha das situacións anómalas asociadas.
Acceso a Datos. Curso 2012-13 21
O proceso de lanzamento dunha excepción é o seguinte:
1. Créase un obxecto Exception da clase adecuada.
2. Lánzase a excepción coa sentencia throw seguida do obxecto Exception creado.
p.e. MyException me = new MyException ("MyException message");
throw me;
Esta excepción deberá ser capturada (catch) e xestionada no propio método ou nalgún outro lugar do
programa, segundo se explica no seguinte apartado.
Restriccións ás excepcións.
Se un método redefine outro método dunha super-clase que utiliza throws, no método da clase derivada só se
poden lanzar as excepcións que se especificaron no método da clase base. É posible no método da subclase
lanzar as mesmas excepcións ou menos, pero non se poden lanzar máis excepcións.
Acceso a Datos. Curso 2012-13 22

Más contenido relacionado

Similar a Tema1 (8)

Elaboración e emprego de materias didácticos dixitais: Emprego de eXe-Learnin...
Elaboración e emprego de materias didácticos dixitais: Emprego de eXe-Learnin...Elaboración e emprego de materias didácticos dixitais: Emprego de eXe-Learnin...
Elaboración e emprego de materias didácticos dixitais: Emprego de eXe-Learnin...
 
Dev con Joomla componentes modulos plugins
Dev con Joomla componentes modulos pluginsDev con Joomla componentes modulos plugins
Dev con Joomla componentes modulos plugins
 
Redes e internet
Redes e internetRedes e internet
Redes e internet
 
Sistemas operativos
Sistemas operativosSistemas operativos
Sistemas operativos
 
Introdución a Linux: Primeiros pasos
Introdución a Linux: Primeiros pasosIntrodución a Linux: Primeiros pasos
Introdución a Linux: Primeiros pasos
 
O Servidor de Recursos Educativos
O Servidor de Recursos EducativosO Servidor de Recursos Educativos
O Servidor de Recursos Educativos
 
Ponencia Drupal
Ponencia DrupalPonencia Drupal
Ponencia Drupal
 
Introdución á edición de textos con LaTeX
Introdución á edición de textos con LaTeXIntrodución á edición de textos con LaTeX
Introdución á edición de textos con LaTeX
 

Tema1

  • 1. TEMA 1: MANEXO DE FICHEIROS 1. INTRODUCCIÓN Un ficheiro ou arquivo é un conxunto de bits almacenado nun dispositivo, como por exemplo un disco duro. A vantaxe de utilizar ficheiros é que os datos que gardamos permanecen no dispositivo aínda cando apaguemos o ordenador, é dicir, non son volátiles. 2. CLASES ASOCIADAS ÁS OPERACIÓNS DE XESTIÓN DE FICHEIROS Antes de ver as clases que len e escreben datos en ficheiros, vamos manexar a clase File. Esta clase proporciona un conxunto de utilidades relacionadas cos ficheiros que nos van proporcionar información acerca dos mesmos, o seu nome, os seus atributos, os directorios, etc. Pode representar o nome dun ficheiro particular ou os nomes dun conxunto de ficheiros dun directorio, tamén se pode usar para crear un novo directorio ou unha traxectoria de directorios completa se esta non existe. Para crear un obxecto File, pódese utilizar calquera dos tres construtores seguintes: • File (String directorio e ficheiro) • File (String directorio, String nomeFicheiro) • File (File directorio, String ficheiro) Algúns métodos importantes do obxecto File son: • getName() • getPath • getAbsolutePath() • canRead() • canWrite() • length() • createNewFile() • delete() • exists() • getParent() • isDirectory() • isFile() Acceso a Datos. Curso 2012-13 1
  • 2. • mkdir() • renameTo(File novoNome) 3. FLUXOS OU STREAMS. TIPOS. O sistema de entrada/saída en Java presenta unha grande cantidade de clases que se implementan no paquete java.io. Usa a abstracción do fluxo (stream) para tratar a comunicación de información entre unha fonte e un destino; esta información pode estar nun ficheiro no disco duro, na memoria, nalgún lugar da rede, e incluso, noutro programa. Calquera programa que teña que obter información de calquera fonte necesita abrir un stream, igualmente se necesita enviar información abrirá un stream e escribirase a información a través do mesmo. A vinculación dese stream ó dispositivo físico faina o sistema de entrada e saída de Java. Defínense dous tipos de fluxos: • Fluxos de bytes (8 bits): realizan operacións de entradas e saídas de bytes e o seu uso está orientado á lectura/escritura de datos binarios. Tódalas clases de fluxos de bytes descenden das clases InputStream e OutputStream, cada unha destas clases teñen varias subclases que controlan as diferencias entre os distintos dispositivos de entrada/saída que se poden utilizar. • Fluxos de caracteres (16 bits): realizan operacións de entradas e saídas de caracteres. O fluxo de caracteres ven gobernado polas clases Reader e Writer. A razón de ser destas clases é a internacionalización; a antiga xerarquía de fluxos de E/S só soporta fluxos de 8 bits, non manexando caracteres Unicode de 16 bits que se utilizaba con fines de internacionalización. Acceso a Datos. Curso 2012-13 2
  • 3. 3.1. FLUXOS DE BYTES (BYTE STREAMS) A clase InputStream representa as clases que producen entradas de distintas fontes, estas fontes poden ser: un array de bytes, un obxecto String, un ficheiro, unha “tubería” (póñense os elementos nun extremo e saen polo outro), unha secuencia doutros fluxos, outras fontes como unha conexión a Internet, etc. Os tipos de InputStream resúmense na seguinte táboa: CLASE FUNCIÓN ByteArrayInputStream Permite usar un espazo de almacenamento intermedio de memoria. StringBufferInputStream Converte un String nun InputStream FileInputStream Para ler información dun ficheiro. PipepInputStream Implementa o concepto de “tubería” FilterInputStream Proporciona funcionalidade útil a outras clases InputStream SequenceInputStream Converte dous ou máis obxectos InputStream nun InputStream único Os tipos de OutputStream inclúen as clases que deciden onde irá a saída: a un array de bytes, un ficheiro ou unha “tubería”. Resúmense na seguinte táboa: CLASE FUNCIÓN ByteArrayOutputStream Crea un espazo de almacenamento intermedio en memoria. Tódolos datos que se envían ó fluxo ubícanse neste espazo FileOutputStream Para enviar información a un ficheiro PipedOutputStream Calquera información que se desexe escribir aquí acaba automaticamente como entrada do PipedInputStream asociado. Implementa o concepto de “tubería” FilterOutputStream Proporciona fincionalidade útil a outras clases OutputStream Dentro dos fluxos de bytes están as clases FileInputStream e FileOutputStream que manipulan os fluxos de bytes procedentes ou dirixidos a ficheiros en disco e estudaránse nos seguintes apartados. Acceso a Datos. Curso 2012-13 3
  • 4. 3.2. FLUXOS DE CARACTERES (CHARACTER STREAMS) As clases Reader e Writer manexan bytes en combinación coas clases que nanexan caracteres Unicode. Hai ocasións nas que hai que usar as clases que manexan bytes en combinación coas clases que manexan caracteres. Para lograr isto hai clases “ponte” (é dicir, converte os streams de bytes a streams de caracteres): InputStreamReader que converte un InputStream nun Reader (le bytes e os converte en caracteres) e OutputStreamReader que converte un OutputStream nun Writer. A seguinte táboa mostra a correspondencia entre as clases de fluxos de bytes e de caracteres: CLASES DE FLUXOS DE BYTES CLASE CORRESPONDENTE DE FLUXO DE CARACTERES InputStream Reader, convertidor InputStreamReader OutputStream Writer, convertidor OutputStreamWriter FileInputStream FileReader FileOutputStream FileWriter StringBufferInputStream StringReader (sen clase correspondente) StringWriter ByteArrayInputStream CharArrayReader ByteArrayOutputStream CharArrayWriter PipedInputStream PipedReader PipedOutputStream PipedWriter As clases de fluxos de caracteres máis importantes son: • Para acceso a ficheiros, lectura e escritura de caracteres en ficheiros: FileReader e FileWriter. • Para acceso a caracteres, len e escreben un fluxo de caracteres nun array de caracteres CharArrayReader e CharArrayWriter. • Para buferización de datos: BufferedReader e BufferedWriter, utilízanse para evitar que cada lectura ou escritura acceda directamente ó ficheiro, xa que utilizan un buffer intermedio entre a memoria e o stream. Acceso a Datos. Curso 2012-13 4
  • 5. 4. FORMAS DE ACCESO A UN FICHEIRO. Hai dúas formas de acceso á información almacenada nun ficheiro: acceso secuencial e acceso directo ou aleatorio: • Acceso secuencial: os datos ou rexistros lense e escrébense en orde, do mesmo xeito que se fan nunha antiga cinta de vídeo. Se se quere acceder a un dato ou un rexistro que está cara á metade do ficheiro é necesario ler antes tódolos anteriores. A escritura de datos farase a partir do último dato escrito, non é posible facer insercións entre os datos que xa hai escritos. • Acceso directo ou aleatorio: permite acceder directamente a un dato ou rexistro sen necesidade de ler os anteriores e pódese acceder á información en calquera orde. Os datos están almacenados en rexistros de tamaño coñecido, podémonos mover dun rexistro a outro de forma aleatoria para lelos ou modificalos. En Java o acceso secuencial máis común en ficheiros pode ser binario ou a caracteres. Para o acceso binario úsanse as clases FileInputStream e FileOutputStream. Para o acceso a caracteres (texto) úsanse as clases FileReader e FileWriter. No acceso aleatorio utilízase a clase RandomAccessFile. 5. OPERACIÓNS SOBRE FICHEIROS. As operacións básicas que se realizan sobre calquera ficheiro independentemente da forma de acceso ó mesmo son as seguintes: • Creación do ficheiro. O ficheiro créase no disco cun nome que despois se debe utilizar para acceder a el. A creación é un proceso que se realiza unha vez. • Apertura do ficheiro. Para que un programa poida operar cun ficheiro, a primeira operación que ten que realizar é a apertura do mesmo. O programa utilizará algún método para identificar o ficheiro co que quere traballar, por exemplo asignar a unha variable o descritor do ficheiro. • Peche do ficheiro. O ficheiro débese cerrar cando o programa non o vaia utilizar. Normalmente soa ser a última instrución do programa. • Lectura dos datos do ficheiro. Este proceso consiste en transferir información do ficheiro á memoria principal, normalmente a través de algunha variable ou variables do noso programa, nas que se depositarán os datos extraídos do ficheiro. • Escritura de datos no ficheiro. Neste caso o proceso consiste en transferir información da memoria (por medio das variables do programa) ó ficheiro. Normalmente as operacións típicas que se realizan sobre un ficheiro unha vez aberto son as seguintes: • Altas: consiste en engadir un novo rexistro ó ficheiro. • Baixas: consiste en eliminar do ficheiro un rexistro xa existente. • Modificacións: consiste en cambiar parte do contido dun rexistro. • Consultas: consiste en buscar no ficheiro un rexistro determinado. Acceso a Datos. Curso 2012-13 5
  • 6. 6. CLASES PARA XESTIÓN DE FLUXOS DE DATOS DENDE/HACIA FICHEIROS. En Java podemos utilizar dous tipos de ficheiros: de texto ou binarios; e o acceso ós mesmos pódese realizar de forma secuencial ou aleatoria. Os ficheiros de textxo están compostos de caracteres lexibles, mentres que os binarios poden almacenar calquera tipo de dato (int, float, boolean,...) 6.1. FICHEIROS DE TEXTO. Os ficheiros de texto, os que normalmente se xeran cun editor, almacenan caracteres alfanuméricos nun formato estándar (ASCII, UNICODE, UTF8, etc.). Para traballar con eles usaremos as clases FileReader para ler caracteres e FileWriter para escribir os caracteres no ficheiro. Cando traballamos con ficheiros, cada vez que lemos ou escribimos nun, debemos facelo dentro dun manexador de excepcións try-catch. Ó usar a clase FileReader pódese xerar a excepción FileNotFoundException (porque o nome do ficheiro non existe ou non sexa válido) e ó usar a clase FileWriter a excepción IOException (o disco está cheo ou protexido contra escritura). Os métodos que proporciona a clase FileReader para lectura son os seguintes, estes métodos devolven o número de caracteres lidos ou -1 se chegou ó final do ficheiro: • int read(): le un caracter e o devolve. • int read(char[] buf): le hasta buf.length caracteres de datos dunha matriz de caracteres (buf). Os caracteres lidos do ficheiro vanse almacenando en buf. • int read(char[] buf, int desprazamento,int n): le ata n caracteres de datos da matriz buf comezando por buf[desprazamento] e devolve o número lido de caracteres. Nun programa Java para crear ou abrir un ficheiro invócase á clase File e a continuación, créase o fluxo de entrada coa clase FileReader. Despois realízanse as operacións de lectura ou escritura e cando terminemos de usalo o cerraremos mediante o método close(). Os métodos que proporciona a clase FileWriter para escritura son: • void write(int c): escrebe un caracter. • void write(char[] buf): escrebe un array de caracteres. • void write(char[] buf, int desprazamento, int n): escrebe n caracteres de datos na matriz buf e comezando por buf[desprazamento]. • void write(String str): escrebe unha cadea de caracteres. • append(char c): engade un caracter a un ficheiro. FileReader non contén métodos que nos permitan ler liñas completas, pero BufferedReader si; dispón do método readLine() que le unha liña do ficheiro e a devolve, ou devolve null se non hai nada que ler ou chegamos ó final do ficheiro. Tamén dispón do método read() para ler un caracter. Para construír un BufferedReader necesitamos a clase FileReader: BufferedReader ficheiro = new BufferedReader (new FileReader (nomeFicheiro)); A clase BufferedWriter engade un buffer para realizar unha escritura eficiente de caracteres. Para construír un BufferedWriter necesitamos a clase FileWriter: BufferedWriter ficheiro = new BufferedWriter (new FileWriter (nomeFicheiro)); Acceso a Datos. Curso 2012-13 6
  • 7. A clase PrintWriter posúe os métodos print(String) e println (String) (idénticos ós de System.out) para escribir nun ficheiro. Ambos reciben un String e o imprimen nun ficheiro, o segundo método salta de liña. Para construír un PrintWriter necesitamos a clase FileWriter: PrintWriter ficheiro = new PrintWriter (new FileWriter(nomeFicheiro)); 6.2. FICHEIROS BINARIOS. Os ficheiros binarios almacenan secuencias de díxitos binarios que non son lexibles directamente polo usuario como sucedía cos ficheiros de texto. Teñen a vantaxe de que ocupan menos espazo en disco. En Java, as dúas clases que nos permiten traballar con ficheiros son FileInputStream (para entrada) e FileOutputStream (para saída), estas traballan con fluxos de bytes e crean un enlace entre o fluxo de bytes e o ficheiro. Os métodos que proporciona a clase FileInputStream para lectura son similares ós vistos para a clase FileReader, estes métodos devolven o número de bytes lidos ou -1 se se chegou ó final do ficheiro: • int read(): le un caracter e o devolve. • int read(byte[] b): le hasta b.length bytes dunha matriz de bytes. • int read(byte[] b, int desprazamento,int n): le ata n bytes da matriz b comezando por b[desprazamento] e devolve o número lido de bytes. Os métodos que proporciona a clase FileOutputStream para escritura son: • void write (int b): escrebe un byte. • void write(byte[] b): escrebe un array de bytes. • void write(byte[] b, int desprazamento, int n): escrebe n bytes na matriz b e comezando por b[desprazamento]. Para engadir bytes ó final do ficheiro usaremos FileOutputStream da seguinte maneira, colocando no segundo parámetro do construtor o valor true: FileOutputStream fileout = new FileOutputStream (ficheiro, true); Para ler e escribir datos de tipos primitivos: int, float, long, etc. usaremos as clases DataInputStream e DataOutputStream. Estas clases ademais dos métodos read() e write() vistos anteriormente proporcionan métodos para a lectura e escritura de tipos primitivos dun modo independiente da máquina. Algúns dos métodos móstranse na seguinte táboa: MÉTODOS PARA LECTURA MÉTODOS PARA ESCRITURA boolean readBoolean(); byte readByte(); short readShort() char readChar() int readInt() float readFloat() double readDouble() String readUTF() void writeBoolean(boolean v) void writeByte(int v) void writeBytes(String s) void writeChars(String s) void writeChar (int v) void writeInt(int v) void writeFloat(float v) void writeDouble(double v) void writeUTF(String str) Acceso a Datos. Curso 2012-13 7
  • 8. Para abrir un obxecto DataInputStream, utilízanse os mesmos métodos que para FileInputStream, exemplo: File ficheiro = new File(“C:exerciciosuni1ficheiro.dat”); FileInputStream filein = new FileInputStream(ficheiro); DataInputStream dataIS = new DataInputStream(filein); Para abrir un obxecto DataOutputStream, utilízanse os mesmos métodos que para FileOutputStream, exemplo: File ficheiro = new File(“C:exerciciosuni1ficheiro.dat”); FileOutputStream fileout = new FileOutputStream(ficheiro); DataOutputStream dataOS = new DataOutputStream(fileout) OBXECTOS SERIALIZABLES. Vimos como se gardan os tipos de datos primitivos nun ficheiro, pero por exemplo, se temos un obxecto de tipo empregado con varios atributos (o nome, a dirección, o salario, o departamento, o oficio, etc.) e queremos gardalo nun ficheiro, teriamos que gardar cada atributo que forma parte do obxecto por separado, isto vólvese engorroso se temos grande cantidade de obxectos. Por isto Java permítenos gardar obxectos en ficheiros binarios; para poder facelo, o obxecto ten que implementar a interface Serializable. A interface Serializable non ten métodos nin atributos e serve só para indicar que os obxectos da clase son serializables. As clases que se usan durante a serialización e deserialización deben implementar unha serie de métodos cos que poderemos gardar e ler obxectos en ficheiros binarios. Os máis importantes a utilizar son: • void readObject (java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException: para ler un obxecto. • void writeObject (ObjectOutputStream stream) throws IOException: para escribir un obxecto. A serialización de obxectos de Java permite tomar calquera obxecto que implemente a interface Serializable e convertilo nunha secuencia de bits, que pode ser posteriormente restaurada para rexenerar o obxecto orixinal. Para ler e escribir obxectos serializables a un stream utilízanse as clases Java ObjectInputStream, que dispón do método readObject(), e ObjectOutputStream, que dispón do método writeObject(). Acceso a Datos. Curso 2012-13 8
  • 9. 6.3. FICHEIROS DE ACCESO ALEATORIO. Ata agora tódalas operacións que realizamos sobre os ficheiros realizábanse de forma secuencial. Empezábase a lectura no primeiro byte ou o primeiro caracter ou o primeiro obxecto e seguidamente, líanse os seguintes un a continuación doutro ata chegar ó final do ficheiro. Igualmente cando escribiamos os datos no ficheiro íanse escribindo a continuación da última información escrita. Java dispón da clase RandomAccessFile que dispón de métodos para acceder ó contido dun ficheiro binario de forma aleatoria (non secuencial) e para posicionarnos nunha posición concreta do mesmo. Esta clase non é parte da xerarquía InputStream/OutputStream, xa que o seu comportamento é totalmente distinto posto que se pode avanzar e retroceder dentro dun ficheiro. Dispoñemos de dúas posibilidades para crear o ficheiro de acceso aleatorio: RandomAccessFile ficheiro = new RandomAccessFile(String nome, String modoAcceso); RandomAccessFile ficheiro = new RandomAccessFile(File fich, String modoAcceso); O argumento modoAcceso pode ser: “r” para so lectura ou “rw” para lectura e escritura. Unha vez aberto o ficheiro poden usarse os métodos read() e write() das clases DataInputStream e DataOutputStream (vistos anteriormente). A clase RandomAccessFile manexa un punteiro que indica a posición actual no ficheiro. Cando no ficheiro se crea o punteiro colócase en 0, apuntando ó principio do mesmo. As sucesivas chamadas ós métodos read() e write() axustan o punteiro segundo a cantidade de bytes lidos ou escritos. Os métodos máis importantes son: long getFilePointer: devolve a posición actual do punteiro do ficheiro. void seek (long posicion): coloca o punteiro do ficheiro nunha posición determinada desde o comezo do mesmo. long length(): devolve o tamaño do ficheiro en bytes. A posición length() marca o final do ficheiro. int skipBytes(int desprazamento): despraza o punteiro desde a posición actual o número de bytes indicados en desprazamento. Acceso a Datos. Curso 2012-13 9
  • 10. 7. TRABALLO CON FICHEIROS XML. Os ficheiros XML pódense utilizar para proporcionar datos a unha base de datos ou para almacenar copias de partes do contido da base de datos. Tamén se utilizan para escribir ficheiros de configuración de programas ou no protocolo SOAP (Simple Object Access Protocol), para executar comandos en servidores remotos; a información enviada ó servidor remoto e o resultado da execución do comando envíanse en ficheiros XML. Para ler os ficheiros XML e acceder ó seu contido e estrutura, utilízase un procesador de XML ou parser. O procesador le os documentos e proporciona acceso ó seu contido e estrutura. Algúns dos procesadores máis empregados son: DOM: Modelo de Obxectos de Documento e SAX: API Simple para XML. Son independentes da linguaxe de programación e existen versións particulares para Java, VisualBasic, C, etc. Utilizan dous enfoques moi diferentes: DOM: un procesador SML que utilice este plantexamento almacena toda a estrutura do documento en memoria en forma de árbore con nodos pai, nodos fillo e nodos finais (que son aqueles que non teñen descendentes). Unha vez creada a árbore, vanse percorrendo os diferentes nodos e analízase a que tipo particular pertencen. Ten a súa orixe no W3C. Este tipo de procesamento necesita máis recursos de memoria e tempo sobre todo se os ficheiros XML a procesar son bastante grandes e complexos. SAX: un procesador que utilice este plantexamento le un ficheiro SML de forma secuencial e produce unha secuencia de eventos (comezo/fin de documento, comezo/fin dunha etiqueta, etc.) en función dos resultados da lectura. Cada evento invoca a un método definido polo programador. Este tipo de procesamento practicamente non consume memoria, pero por outra parte, impide ter unha visión global do documento polo que navegar. Acceso a Datos. Curso 2012-13 10
  • 11. 7.1. ACCESO A FICHEIROS XML CON DOM. PAra poder traballar con DOM en Java necesitamos as clases e interfaces que compoñen o paquete org.w3c.dom (contido no JSDK) e o paquete javax.xml.parsers da API estándar de Java que proporciona un par de clases abstratas que toda implementación DOM para Java debe extender. Estas clases ofrecen métodos para cargar documentos desde unha fonte de datos (ficheiro, InputStream, etc.). Contén dúas clases fundamentais: DocumentBuilderFactory e DocumentBuilder. DOM non define ningún mecanismo para xerar un ficheiro XML a partir dunha árbore DOM. Para iso usaremos o paquete javax.xml.transform que permite especificar unha fonte e un resultado. A fonte e o resultado poden ser ficheiros, fluxos de datos ou nodos DOM entre outros. Interfaces que usan os programas que utilizan DOM (só se indican algunhas): • Document. É un obxecto que equivale a un exemplar dun documento XML. Permite crear novos nodos no documento. • Element. Cada elemento do documento XML ten un equivalente nun obxecto deste tipo. Expón propiedades e métodos para manipular os elementos do documento e os seus atributos. • Node. Representa a calquera nodo do documento. • NodeList. Contén unha lista cos nodos fillos dun nodo. • Attr. Permite acceder ós atributos dun nodo. • Text. Son os datos carácter dun elemento. • CharacterData. Representa ós datos carácter presentes no documento. Proporciona atributos e métodos para manipular os datos de caracteres. • DocumentType. Proporciona información contida na etiqueta <!DOCTYPE> Acceso a Datos. Curso 2012-13 11
  • 12. 7.2. ACCESO A FICHEIROS XML CON SAX. SAX (API Simple para XML) é un conxunto de clases e interfaces que ofrecen unha ferramenta moi útil para o procesamento de documentos XML. Permite analizar os documentos de forma secuencial (é dicir, non carga todo o ficheiro en memoria como fai DOM), isto implica pouco consumo de memoria aínda que os documentos sexan de grande tamaño, en contraposición, impide ter unha visión global do documento que se vai analizar. SAX é máis complexo de programar que DOM, é un API totalmente escrita en Java e incluída dentro do JRE que nos permite crear o noso propio parser de XML. A lectura dun documento XML produce eventos que ocasiona a chamada a métodos, os eventos son atopar a etiqueta de inicio e fin do documento (startDocument() e endDocument()), a etiqueta de inicio e fin dun elemento (startElement() e endElement()), os caracteres entre etiquetas (characters()), etc. Os obxectos que posúen métodos que tratarán os eventos serán normalmente implementacións das seguintes interfaces: • ContentHandler: recibe as notificacións dos eventos que ocorren no documento. • DTDHandler: recolle eventos relacionados coa DTD • ErrorHandler: define métodos de tratamentos de erros. • EntityResolver: os seus métodos chámanse cada vez que se atopa unha referencia a unha entidade. • DefaultHandler: clase que prove unha implementación por defecto para tódolos seus métodos, o programador definirá os métodos que sexan utilizados polo programa. Acceso a Datos. Curso 2012-13 12
  • 13. 7.3. SERIALIZACIÓN DE OBXECTOS A XML. Vemos como se poden serializar de forma sinxela obxectos Java a XML e viceversa. Utilizaremos para isto a librería XStream. Para poder utilizala debemos descargar os JAR desde o sitio web: http://xstream.codehaus.org/download.html, para o exemplo descargouse o ficheiro Binary distribution (xstream-distribution-1.4.2-bin.zip) que descomprimimos e buscamos o JAR xstream- 1.4.2.jar que está na carpeta lib que é o que usaremos para o exemplo. Tamén necesitamos o ficheiro kxml2-2.3.0.jar que se pode descargar desde o apartado Optional Dependencies. Unha vez que temos os sous ficheiros, os definimos no CLASSPATH. 7.4. CONVERSIÓN DE FICHEIROS XML A OUTRO FORMATO. XSL (Extensible Stylesheet LAnguage) é toda unha familia de recomendacións do World Wide Web Consortium (http://www.w3.org/Style/XSL/) para expresar follas de estilo en linguaxe XML. Unha folla de estilo XSL describe o proceso de presentación a través dun pequeno conxunto de elementos XML. Esta folla, pode conter elementos de regras que representan ás regras de construcción e elementos de regras de estilo que representan ás regras de mezcla de estilos. Acceso a Datos. Curso 2012-13 13
  • 14. 8. EXCEPCIÓNS: DETECCIÓN E TRATAMENTO. Introdución. O momento ideal para capturar un erro é en tempo de compilación, antes incluso de intentar executar o programa. Sen embargo, non tódolos erros se poden detectar en tempo de compilación. O resto dos problemas deberán ser manexados en tempo de execución, mediante algunha formalidade que permita á fonte do erro pasar a información adecuada a un receptor que saberá facerse cargo da dificultade de maneira adecuada. Na linguaxe Java, unha Excepción é un certo tipo de erro ou unha condición anormal que se produciu durante a execución do programa. Algunhas excepcións son fatais e provocan que se deba finalizar a execución do programa. Neste caso convén terminar ordenadamente e dar unha mensaxe explicando o tipo de erro que se produciu. Outras, como por exemplo non atopar un ficheiro no que hai que ler ou escribir algo, poden ser recuperables. Neste caso o programa debe dar ó usuario a oportunidade de corrixir o erro (indicando unha nova localización do ficheiro non atopado). Un bo programa debe xestionar correctamente todos ou a maior parte dos erros que se poden producir. Hai dous "estilos" de facer isto: 1. Á "vella usanza": os métodos devolven un código de erro. Este código chequéase no entorno que chamou ó método cunha serie de if elseif ..., xestionando de forma diferente o resultado correcto ou cada un dos posibles erros. Este sistema resulta moi complicado cando hai varios niveis de chamadas ós métodos. 2. Con soporte na propia linguaxe: Neste caso a propia linguaxe proporciona construccións especiais para xestionar os erros ou excepcións. Coe ser o habitual en linguaxes modernas, como C++, Visual Basic e Java. Mediante o uso de excepcións para manexar erros, os programas Java teñen as seguintes vantaxes fronte ás técnicas de manexo de erros tradicionais: 1. Separar o manexo de erros do código "normal". 2. Propagar os erros sobre a pila de chamadas. 3. Agrupar os tipos de erros e a diferenciación destes. Acceso a Datos. Curso 2012-13 14
  • 15. Excepcións estándar de Java. Os erros represéntanse mediante dous tipos de clases derivadas da clase Throwable: Error e Exception. A seguinte figura mostra parcialmente a xerarquía de clases relacionada con Throwable: A clase Error está relacionada con erros de compilación, do sistema ou da JVM. De ordinario estes erros son irrecuperables e nin dependen do programador nin debe preocuparse de capturalos e tratalos. A clase Exception ten máis interese. Dentro dela pódense distinguir: 1. RuntimeException: Son excepcións moi frecuentes, de ordinario relacionadas con erros de programación. Pódense chamar excepcións implícitas. 2. As demais clases derivadas de Exception son excepcións explícitas. Java obriga a telas en conta e comprobar si se producen. O caso de RuntimeException é un pouco especial. O propio Java durante a execución dun programa chequea e lanza automaticamente as excepcións que derivan de RuntimeException. O programador non necesita establecer os bloques try/catch para controlar este tipo de excepcións. Representan dous casos de erros de programación: 1. Un erro que normalmente non soe ser chequeado polo programador, como por exemplo recibir unha referencia null nun método. 2. Un erro que o programador debería ter chequeado ó escirbir o código, como sobrepasar o tamaño asignado dun array (xera un ArrayIndexOutOfBoundsException automáticamente). En realidade sería posible comprobar estes tipos de erros, pero o código complicaríase excesivamente si se necesitara comprobar continuamente todo tipo de erros (que as referencias son distintas de null, que tódolos argumentos dos métodos son correctos, e un longo etcétera). Acceso a Datos. Curso 2012-13 15
  • 16. Capturar unha excepción. Nos métodos pódense producir excepcións. Si se trata de excepcións explícitas e o usuario chama a estes métodos sen telo en conta prodúcese un erro de compilación cunha mensaxe do tipo: "...Exception java.oi.IOException must be caught or it must be declared in the throws clause of this method". O programa non compilará salvo que o usuario realice unha das seguintes accións: 1. Xestione a excepción cunha construcción do tipo try{...} catch{...} 2. Relance a excepción hacia un método anterior na pila, declarando que o seu método tamén lanza esta excepción, utilizando para isto a construcción throws na cabeceira do método. O compilador obriga a capturar as chamadas excepcións explícitas, pero non protesta si se captura e despois non se fai nada con ela. En xeral, é conveniente polo menos imprimir unha mensaxe indicando que tipo de excepción se produciu. Bloques try e catch. No caso das excepcións que non pertencen ás RuntimeException e que polo tanto Java obriga a telas en conta haberá que utilizar os bloques try, catch e finally. O código dentro do bloque try está "vixiado": Si se produce unha situación anormal e se lanza polo tanto unha excepción o control salta ou sae do bloque try e pasa ó bloque catch, que se fai cargo da situación e decide o que hai que facer. Pódense incluír tantos bloques catch como sexan necesarios, cada un dos cales tratará un tipo de excepción. As instruccións inclúense nun bloque coa seguinte sintaxe: try{ //instruccións susceptibles de causar certo tipo de excepcións } catch (TipoDeExcepcion Identificador){ //instruccións de control de situacións excepcionais } As excepcións pódense capturar individualmente ou en grupo, por medio dunha superclase da que deriven todas elas. Entre o bloque try e o bloque catch non pode haber ningunha instrucción. Acceso a Datos. Curso 2012-13 16
  • 17. O funcionamento é o seguinte: execútanse as instruccións do bloque try ata que se produza a situación de excepción de tipo TipoDeExcepcion; nótese que, habitualmente, non se producirá a excepción e se executarán tódalas instruccións do bloque try. Si se da a excepción, pásase a executar as instruccións do bloque catch e, posteriormente, as instruccións seguintes ó bloque catch. Se non se produce a excepción, non se executan as instruccións do bloque catch. O bloque finally é opcional. Si se inclúe as súas sentencias execútanse sempre, sexa cal sexa a excepción que se produza ou se non se produce ningunha. O bloque finally execútase aínda que no bloque try haxa un return. Acceso a Datos. Curso 2012-13 17
  • 18. Acceso a Datos. Curso 2012-13 18
  • 19. Cando se lanza unha excepción nas instruccións do bloque try, recórrense en orde os bloques catch ata que se atopa un co mesmo tipo ou un tipo que é superclase da excepción que chegou. Execútanse unicamente as instruccións do bloque catch que cumpre os requisitos (se é que algún os cumpre). É posible crear un manexador que capture calquera tipo de excepción. Isto faise capturando a excepción de clase base Exception: Isto capturará calquera excepción, de forma que si se usa, haberá que poñelo ó final da lista de manexadores para evitar que os manexadores que poidan vir despois queden ignorados. O bloque finally. O bloque finally{...} debe ir detrás de tódolos bloques catch considerados. Si se inclúe (xa que é opcional) as súas sentencias execútanse sempre, sexa cal sexa o tipo de excepción que se produza, ou incluso se non se produce ningunha. O bloque finally execútase incluso se dentro dos bloques try/catch hai unha sentencia continue, break ou return. A forma xeral dunha sección onde se controlan as excepcións é polo tanto: Acceso a Datos. Curso 2012-13 19
  • 20. Relanzar unha excepción. Existen algúns casos nos cales o código dun método pode xerar unha excepción e non se desexa incluír nese método a xestión do erro. Java permite que este método pase a excepción ó método desde o que foi chamado, sen incluír no método os bucles try/catch correspondentes. Isto conséguese mediante a adición de throws máis os nomes das excepcións despois da lista de argumentos do método. Á súa vez o método superior deberá incluír os bloques try/catch ou volver a pasar a excepción. Desta forma pódese ir pasando a excepción dun método a outro ata chegar ó último método do programa, o método main(). O exemplo anterior (metodo1) realizaba a xestión das excepcións dentro do propio método. Agora preséntase un novo exemplo (metodo2) que relanza as excepcións ó seguinte método: por exemplo: public void lerFicheiro(String fich) throws EOFException, FileNotFoundException {...} Pódese poñer unicamente unha superclase de excepcións para indicar que se poden lanzar excepcións de calquera das súas clases derivadas. O caso anterior sería equivalente a: public void lerFicheiro(String fich) throws IOException {...} xa que as clases EOFException e FileNotFoundException derivan de IOException. As excepcións poden ser lanzadas directamente por lerFicheiro() ou por algún dos métodos chamados por lerFicheiro(). En ocasións captúrase a excepción con try/catch dentro do método pero deséxase volver a lanzar a excepción que se acaba de capturar, especialmente cando se usa Exception para capturar calquera excepción. Dado que xa se ten a referencia á excepción actual, pódese volver a lanzar esa referencia: catch (Exception e){ System.err.println("Lanzousa a excepción"); throw e; } Volver a lanzar a excepción fai que a excepción vaia ó lugar desde o cal se invocou o método actual. Acceso a Datos. Curso 2012-13 20
  • 21. Estado dunha excepción. As excepcións derivan da clase Throwable e teñen acceso ós seus dous constructores e ós seus sete métodos. O estado das instancias desta clase componse dun String que serve de mensaxe indicando as características da excepción e unha pila de execución que contén a relación de métodos que se atopan en execución no momento no que se produce a excepción. Os métodos máis significativos da clase Throwable son: - getMessage(): Devolve (como String) a mensaxe de erro almacenada neste obxecto. Esta mensaxe componse do nome da excepción mais unha breve referencia. - printStackTrace(): Imprime polo dispositivo de saída de erros (normalmente a consola) a mensaxe e a pila de execución almacenados no obxecto Throwable. Este mesmo método está sobrecargado para poder sacar o resultado por diferentes saídas: printStackTrace(PrintStream s) e printStackTrace(PrintWriter s). - toString(): Devolve (como String) unha descrición curta do obxecto. 3.10.5. Creación de novas excepcións. O programador pode crear as súas propias excepcións só con herdar da clase Exception ou dunha das súas clases derivadas. O lóxico é herdar da clase da xerarquía de Java que mellor se adapte ó tipo de excepción. As clases Exception soen ter dous constructores: 1. Un constructor sen argumentos. 2. Un constructor que recibe un String como argumento. Neste String sóese definir unha mensaxe que explica o tipo de excepción xerada. Convén que este constructor chame ó constructor da clase da que deriva: super(String). p.e. class MiExcepcion extends Exception{ MiExcepcion(){ //Constructor por defecto super(); } MiExcepcion(String s){ //Constructor con mensaxe super(s); } Unha vez que dispoñemos dunha excepción propia, poderemos programar a funcionalidade das nosas aplicacións lanzando a excepción cando detectemos algunha das situacións anómalas asociadas. Acceso a Datos. Curso 2012-13 21
  • 22. O proceso de lanzamento dunha excepción é o seguinte: 1. Créase un obxecto Exception da clase adecuada. 2. Lánzase a excepción coa sentencia throw seguida do obxecto Exception creado. p.e. MyException me = new MyException ("MyException message"); throw me; Esta excepción deberá ser capturada (catch) e xestionada no propio método ou nalgún outro lugar do programa, segundo se explica no seguinte apartado. Restriccións ás excepcións. Se un método redefine outro método dunha super-clase que utiliza throws, no método da clase derivada só se poden lanzar as excepcións que se especificaron no método da clase base. É posible no método da subclase lanzar as mesmas excepcións ou menos, pero non se poden lanzar máis excepcións. Acceso a Datos. Curso 2012-13 22