Fluxos de Dades (Streams) en Java
Introducció
Quan un programa necessita comunicar-se amb l’exterior (llegir un fitxer, escriure un informe, rebre dades d’Internet o enviar informació a la consola), necessita un flux de dades (stream).
Un flux és un camí seqüencial pel qual les dades entren (entrada) o ixen (eixida) del programa.
Passos bàsics en qualsevol flux:
- Obrir el flux (crear l’objecte adequat).
- Usar el flux (llegir o escriure).
- Tancar amb
close()
per alliberar recursos.
Famílies de fluxos
En Java hi ha dues famílies segons el tipus de dades que tractes: els fluxos de bytes per a informació binària (imatges, vídeos, executables) i els fluxos de caràcters per a text Unicode (fitxers .txt
, .csv
, .xml
, .html
, .json
, etc.).
- Triarem bytes quan el contingut no és text o no vols que l’intèrpret faça conversions d’encoding.
- Triarem caràcters quan el contingut és text i necessites respectar l’encoding i la internacionalització.
Família | Classe base | Unitat de treball | Ús |
---|---|---|---|
Fluxos de bytes | InputStream / OutputStream | Byte (8 bits) | Dades binàries: imatges, vídeos, fitxers .exe |
Fluxos de caràcters | Reader / Writer | Caràcter (Unicode, 16 bits) | Textos humans: .txt , .csv , .xml , .json , etc. |
Nota: (encoding): L’encoding és la “traducció” caràcters ⇄ bytes (ex.: UTF-8). Per text, usarem
Reader/Writer
indicant UTF-8 i per binari,InputStream/OutputStream
(sense conversió).
Bones pràctiques
-
Tanca sempre els fluxos. Usa try-with-resources: és un
try ( … ) { … }
on cada recurs (classe que implementaAutoCloseable
, com els fluxos d’java.io
) es tanca automàticament en eixir del bloc, encara que hi haja excepció, i en ordre invers a la declaració. Elcatch
és opcional (pots capturar o propagar ambthrows
).// Exemple: llegir bytes d’un fitxer binari try (java.io.FileInputStream fis = new java.io.FileInputStream("fitxer.bin")) { // ús: llegim el primer byte int b = fis.read(); // retorna -1 si s'ha arribat al final System.out.println("Primer byte: " + b); } // fis.close() es crida automàticament, haja passat el que haja passat
-
Gestió d’excepcions habituals:
FileNotFoundException
: el fitxer no existeix o no tens permisos.EOFException
: lectura més enllà del final (sobretot ambDataInputStream
).IOException
: errors generals d’entrada/eixida.
-
flush()
: només té sentit en escriptura; força a buidar el buffer al disc. (Recorda:close()
ja faflush()
.) -
Ordre en fluxos encadenats: si uses
new DataInputStream(new BufferedInputStream(...))
, tanques només el de fora (dis.close()
), i tots els altres es tanquen en cascada.
Exemples try-with-resources
Exemple 1 — Diversos recursos encadenats (lectura binària amb DataInputStream
) El següent codi obri el fitxer, aplica buffer, i llix un int
(4 bytes) del principi. Si ocorre un error d’E/S, el capturem i informem.
try (FileInputStream fis = new FileInputStream("fitxer.bin");
// 1) font de bytes
BufferedInputStream bis = new BufferedInputStream(fis);
// 2) buffer per millorar rendiment
DataInputStream dis = new DataInputStream(bis)) {
// 3) lectura de tipus primitives
int x = dis.readInt(); // llix 4 bytes i els interpreta com a int
System.out.println("Enter llegit: " + x);
} catch (EOFException e) {
System.out.println("Final de fitxer inesperat.");
} catch (FileNotFoundException e) {
System.out.println("Fitxer inexistent o sense permisos.");
} catch (IOException e) {
System.out.println("Error d'entrada/eixida: " + e.getMessage());
}
// Nota: no cal dis.close()/bis.close()/fis.close(): es tanquen sols en ordre invers.
Exemple 2 — Text amb encoding explícit (lectura i escriptura) El següent codi obri in.txt
en mode text amb UTF-8 (via InputStreamReader
) i copia línies a out.txt
amb BufferedWriter
.
try (BufferedReader br = new BufferedReader(
new InputStreamReader(new FileInputStream("in.txt"), "UTF-8"));
// bytes -> caràcters (UTF-8)
BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream("out.txt"), "UTF-8"))) {
// caràcters -> bytes (UTF-8)
String linia;
while ((linia = br.readLine()) != null) { // lectura línia a línia
bw.write(linia); // escrivim la línia al fitxer d’eixida
bw.newLine(); // salt de línia de forma portable
}
// No cal bw.flush(); quan es tanquen en ordre invers, ja es buida el buffer
} catch (IOException e) {
System.out.println("Error d'entrada/eixida: " + e.getMessage());
}
Exemple 3 — Sense catch
, propagant amb throws
El següent codi crea un CSV simple. Qualsevol IOException
no es captura ací; es propaga al cridador gràcies al throws IOException
en la signatura.
// Propaguem l'excepció: qui cride exportaCSV() haurà de capturar o tornar a propagar
void exportaCSV() throws IOException {
try (BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream("dades.csv"), "UTF-8"))) {
bw.write("id;nom"); bw.newLine(); // capçalera
bw.write("1;Ana"); bw.newLine(); // fila 1
bw.write("2;Joan"); bw.newLine(); // fila 2
// en eixir del try, bw.close() es crida automàticament (també flush)
} // si hi ha IOException, ix del try i es propaga cap amunt (per 'throws')
}
En resum:
- Tanca automàticament tots els recursos declarats entre parèntesis, i en ordre invers.
- El
catch
és opcional: captura si vols gestionar localment; si no, posathrows
al mètode i que ho gestione qui crida.- Per a text, especifica l’encoding (p. ex.
"UTF-8"
).- Per a binari, treballa directament amb
InputStream
/OutputStream
.
FLUXOS DE BYTES
(Totes les classes deriven d’InputStream
i OutputStream
)
Els fluxos de bytes treballen amb dades binàries pures. No interpreten res com a text.
Són ideals per a:
- Imatges, vídeos, PDF.
- Fitxers
.dat
amb dades primitives. - Còpia d’arxius byte a byte.
1. Lectura binària (InputStream)
1.1 FileInputStream
Usarem esta classe per: llegir directament bytes d’un fitxer.
Constructors:
FileInputStream(String path)
: obri el fitxer ubicat en el camí indicat, en lectura binària.FileInputStream(File file)
: igual, però rep un objecteFile
previ.
Excepcions requerides (checked):
- Els constructors llancen
FileNotFoundException
si el fitxer no existeix o no hi ha permisos. - Les lectures (
read…
) llancenIOException
; caltry/catch
othrows
.
Mètodes:
int read()
→ llig un byte del fitxer i el retorna com un enter entre 0 i 255. Retorna-1
si s’ha arribat al final. Ús: quan volem processar dades byte a byte.int read(byte[] b)
→ intenta omplir l’arrayb
amb bytes del fitxer i retorna quants n’ha llegit realment. Ús: més eficient queread()
a soles, perquè fa menys crides al disc.int read(byte[] b, int off, int len)
→ igual que l’anterior, però ompli només una part de l’array, des de la posicióoff
i finslen
bytes.long skip(long n)
→ intenta saltarn
bytes sense llegir-los. No sempre salta exactament elsn
bytes (retorna quants ha saltat).int available()
→ retorna quants bytes es poden llegir sense bloquejar (no és el total que queda en el fitxer!).void close()
→ tanca el flux i allibera el descriptor de fitxer.
Quan triar-la: per a lectures binàries senzilles. Quan evitar-la: per a fitxers grans (millor BufferedInputStream
).
Exemple: llegir i mostrar bytes.
try (FileInputStream fis = new FileInputStream("logo.png")) {
int b;
while ((b = fis.read()) != -1) { // llegim byte a byte
System.out.println(b); // mostrem el valor
}
}
Al codi anterior:
new FileInputStream("logo.png")
→ obrim el fitxerlogo.png
en mode lectura binària.int b;
→ declarem una variable per guardar cada byte llegit.while ((b = fis.read()) != -1)
→ bucle que llig byte a byte fins al final del fitxer (-1
).System.out.println(b);
→ mostra cada byte com un número enter (0–255).try-with-resources
→ tanca automàticamentfis
al final, encara que hi haja errors.
1.2 BufferedInputStream
Usarem esta classe per: millorar la velocitat de lectura amb un buffer intern.
Constructor (descripció breu):
BufferedInputStream(InputStream in)
— embolica unInputStream
i afegeix buffer.
Excepcions requerides (checked):
- Les operacions de lectura llancen
IOException
(caltry/catch
othrows
).
Mètodes:
int read()
/int read(byte[] b)
→ funcionen com aFileInputStream
, però llegint d’un buffer en memòria que redueix l’accés al disc.void mark(int readlimit)
→ marca la posició actual en el flux. Es pot tornar a ella després ambreset()
. El paràmetrereadlimit
indica quants bytes podem llegir abans de perdre la marca.void reset()
→ torna a la posició marcada ambmark()
.boolean markSupported()
→ diu si el flux suportamark/reset
. EnBufferedInputStream
éstrue
.void close()
→ tanca el flux i buida el buffer intern.
Quan triar-la: fitxers grans, lectures repetides. Quan evitar-la: fitxers menuts.
Exemple: llegir amb buffer.
try (BufferedInputStream bis =
new BufferedInputStream(new FileInputStream("video.mp4"))) {
byte[] buf = new byte[4096];
int n;
while ((n = bis.read(buf)) != -1) {
System.out.println("Llegits " + n + " bytes");
}
}
Al codi anterior:
new FileInputStream("video.mp4")
→ obrim el fitxer de vídeo en mode lectura.new BufferedInputStream(...)
→ emboliquem el flux amb un buffer de memòria.byte[] buf = new byte[4096];
→ creem un array de 4 KB per llegir blocs sencers.bis.read(buf)
→ llig fins a 4096 bytes en cada iteració; retorna el nombre real llegit.while ((n = bis.read(buf)) != -1)
→ bucle que continua fins al final.System.out.println("Llegits " + n + " bytes");
→ mostra quants bytes s’han llegit cada volta.
1.3 DataInputStream
Usarem esta classe per: llegir dades primitives (int, double, String) en format binari.
Constructor (descripció breu):
DataInputStream(InputStream in)
— embolica unInputStream
per a primitives.
Excepcions requerides (checked):
- Lectures llancen
IOException
(i, segons el cas,EOFException
).
Mètodes:
boolean readBoolean()
,byte readByte()
,char readChar()
,short readShort()
,int readInt()
,long readLong()
,float readFloat()
,double readDouble()
→ lligen primitives en el mateix format binari en què es van escriure ambDataOutputStream
.String readUTF()
→ llig una cadena codificada en Modified UTF-8. Només funciona correctament si es va escriure ambwriteUTF()
.void close()
→ tanca el flux.
Important: l’ordre de lectura ha de coincidir exactament amb l’ordre d’escriptura.
Quan triar-la: per a fitxers .dat
amb registres. Quan evitar-la: quan no controles l’ordre d’escriptura.
Exemple: llegir alumnes.
try (DataInputStream dis = new DataInputStream(
new BufferedInputStream(new FileInputStream("notes.dat")))) {
int n = dis.readInt();
for (int i = 0; i < n; i++) {
long id = dis.readLong();
String nom = dis.readUTF();
double nota = dis.readDouble();
System.out.printf("%d %s %.2f%n", id, nom, nota);
}
}
Al codi anterior:
- Obrim el fitxer
notes.dat
ambFileInputStream
. - L’emboliquem amb
BufferedInputStream
per fer-lo més ràpid. - L’emboliquem amb
DataInputStream
per poder llegir primitives. dis.readInt()
→ llegim el nombre de registres guardats.-
Recorrem amb un bucle:
readLong()
→ llegim un identificador.readUTF()
→ llegim un nom.readDouble()
→ llegim una nota.
System.out.printf(...)
→ mostrem els valors amb format.- Tancament automàtic amb
try-with-resources
.
2. Escriptura binària (OutputStream)
2.1 FileOutputStream
Usarem esta classe per: escriure bytes en un fitxer.
Constructors habituals:
FileOutputStream(String path)
→ sobreescriu.FileOutputStream(String path, boolean append)
→ si true, afegeix.
Excepcions requerides (checked):
- Constructors i escriptura llancen
FileNotFoundException
/IOException
.
Mètodes:
void write(int b)
→ escriu un byte. Només els 8 bits baixos del valor de l’enter es guarden.void write(byte[] b)
→ escriu tot l’array de bytesb
.void write(byte[] b, int off, int len)
→ escriu només una part de l’array, des de la posicióoff
i durantlen
bytes.void flush()
→ força a guardar les dades escrites al sistema de fitxers immediatament.void close()
→ tanca el flux i allibera recursos. Ja fa unflush()
automàtic.
Quan triar-la: dades simples. Quan evitar-la: sense buffer en fitxers grans.
Exemple: escriure bytes.
try (FileOutputStream fos = new FileOutputStream("dades.bin")) {
fos.write(new byte[]{10,20,30});
}
Al codi anterior:
new FileOutputStream("dades.bin")
→ crea (o sobreescriu) el fitxer binari.fos.write(new byte[]{10,20,30});
→ escriu tres bytes amb valors 10, 20 i 30.try-with-resources
→ es tanca automàticament el flux.
2.2 BufferedOutputStream
Usarem esta classe per: millorar l’eficiència amb buffer.
Constructor:
BufferedOutputStream(OutputStream out)
— embolica unOutputStream
amb buffer.
Excepcions requerides (checked):
- Escriptures i
flush/close
llancenIOException
.
Mètodes:
void write(int b)
/void write(byte[] b)
/void write(byte[] b, int off, int len)
→ escriuen dades en el buffer intern i les guarden al disc només quan el buffer està ple o es cridaflush()
.void flush()
→ obliga a escriure totes les dades que queden al buffer encara que no estiga ple.void close()
→ tanca el flux i escriu qualsevol dada pendent.
Exemple: escriure bloc gran.
try (BufferedOutputStream bos =
new BufferedOutputStream(new FileOutputStream("gran_sortida.bin"))) {
byte[] bloc = new byte[8192];
bos.write(bloc);
}
Al codi anterior:
new FileOutputStream("gran_sortida.bin")
→ obrim un fitxer per escriure.new BufferedOutputStream(...)
→ afegim un buffer per agrupar l’escriptura.new byte[8192]
→ creem un bloc de 8 KB inicialitzat a zeros.bos.write(bloc);
→ escriu tot el bloc en memòria i després l’envia al fitxer.
2.3 DataOutputStream
Usarem esta classe per: escriure primitives en binari.
Constructor:
DataOutputStream(OutputStream out)
— embolica unOutputStream
per a primitives.
Excepcions requerides (checked):
- Escriptures llancen
IOException
.
Mètodes:
void writeBoolean(boolean v)
,writeByte(byte v)
,writeChar(char v)
,writeShort(short v)
,writeInt(int v)
,writeLong(long v)
,writeFloat(float v)
,writeDouble(double v)
→ escriuen valors primitius en binari.void writeUTF(String s)
→ escriu una cadena en format Modified UTF-8. Ha de ser llegida ambreadUTF()
.void flush()
→ força a escriure el buffer immediatament.void close()
→ tanca el flux.
Important: cal respectar sempre l’ordre i el tipus de dades quan després es llig amb DataInputStream
.
Quan triar-la: guardar registres binaris. Quan evitar-la: si necessites compatibilitat amb altres llenguatges.
Exemple: guardar alumnes.
try (DataOutputStream dos = new DataOutputStream(
new BufferedOutputStream(new FileOutputStream("notes.dat")))) {
dos.writeInt(2);
dos.writeLong(1L); dos.writeUTF("Anna"); dos.writeDouble(9.5);
dos.writeLong(2L); dos.writeUTF("Marc"); dos.writeDouble(7.8);
}
Al codi anterior:
- Obrim el fitxer
notes.dat
ambFileOutputStream
. - L’emboliquem amb
BufferedOutputStream
per fer-lo més eficient. - L’emboliquem amb
DataOutputStream
per poder escriure primitives. dos.writeInt(2);
→ indiquem que hi haurà 2 registres.- Primer registre:
writeLong(1L)
,writeUTF("Anna")
,writeDouble(9.5)
. - Segon registre:
writeLong(2L)
,writeUTF("Marc")
,writeDouble(7.8)
. try-with-resources
→ es tanca automàticament.
FLUXOS DE CARÀCTERS
(Totes les classes deriven de Reader
i Writer
)
Els fluxos de caràcters interpreten els bytes com a text, segons un charset (UTF-8, ISO…).
Són ideals per a:
- Fitxers
.txt
,.csv
,.xml
,.json
. - Qualsevol informació llegible per humans.
Nota sobre FileReader/FileWriter
Aquestes classes usen charset del sistema → pot fallar amb accents.
Pitjor pràctica:
FileReader fr = new FileReader("entrada.txt"); // depén del sistema
Millor pràctica:
new InputStreamReader(new FileInputStream("entrada.txt"), StandardCharsets.UTF_8);
1. Lectura de text (Reader)
1.1 FileReader
Usarem esta classe per: llegir caràcters amb charset del sistema.
Excepcions requerides (checked):
- Constructors/lectures llancen
FileNotFoundException
/IOException
.
Mètodes:
int read()
→ llig un caràcter i el retorna com a enter Unicode. Retorna-1
si arriba al final.int read(char[] cbuf)
→ llig diversos caràcters i els guarda en l’array. Retorna quants n’ha llegit.int read(char[] cbuf, int off, int len)
→ com l’anterior però en una porció de l’array.boolean ready()
→ diu si hi ha caràcters disponibles per llegir sense bloquejar.void close()
→ tanca el flux.
Usa el charset del sistema, pot donar problemes amb accents.
Exemple:
try (FileReader fr = new FileReader("poema.txt")) {
char[] buf = new char[256];
int n;
while ((n = fr.read(buf)) != -1) {
System.out.print(new String(buf, 0, n));
}
}
Al codi anterior:
new FileReader("poema.txt")
→ obrim el fitxer en mode lectura de caràcters.char[] buf = new char[256];
→ reservem un buffer de 256 caràcters.fr.read(buf)
→ ompli el buffer amb caràcters llegits i retorna quants n’ha llegit.while ((n = fr.read(buf)) != -1)
→ bucle que continua fins al final.System.out.print(new String(buf, 0, n))
→ construïm una cadena amb els caràcters llegits i la mostrem.try-with-resources
→ tanca automàticament el flux.
1.2 BufferedReader
Usarem esta classe per: llegir línies amb readLine()
.
Excepcions requerides (checked):
- Lectures llancen
IOException
.
Mètodes:
String readLine()
→ llig una línia sencera (sense incloure el salt de línia). Retornanull
si arriba al final.int read()
/int read(char[] cbuf)
→ llig caràcters de manera més eficient gràcies al buffer.void mark(int readAheadLimit)
→ marca la posició actual, permetent tornar-hi ambreset()
.void reset()
→ torna a la posició marcada.boolean markSupported()
→ retornatrue
.void close()
→ tanca el flux.
Exemple:
try (BufferedReader br = new BufferedReader(new FileReader("entrada.txt"))) {
String linia;
while ((linia = br.readLine()) != null) {
System.out.println(linia);
}
}
Al codi anterior:
new FileReader("entrada.txt")
→ obrim el fitxer en mode lectura.new BufferedReader(...)
→ afegim un buffer per optimitzar i permetrereadLine()
.br.readLine()
→ retorna una línia completa onull
si ja no hi ha més.while ((linia = br.readLine()) != null)
→ bucle fins al final.System.out.println(linia);
→ mostra cada línia per consola.- El flux es tanca automàticament al final.
1.3 InputStreamReader
Usarem esta classe per: convertir bytes a caràcters amb charset.
Excepcions requerides (checked):
- Lectures llancen
IOException
.
Mètodes:
int read()
/int read(char[] cbuf)
→ converteix bytes d’unInputStream
en caràcters segons el charset especificat.boolean ready()
→ diu si hi ha caràcters disponibles per llegir.String getEncoding()
→ retorna el nom del charset usat.void close()
→ tanca el flux.
Exemple:
try (BufferedReader br = new BufferedReader(
new InputStreamReader(new FileInputStream("text_utf8.txt"), StandardCharsets.UTF_8))) {
String linia;
while ((linia = br.readLine()) != null) {
System.out.println(linia);
}
}
Al codi anterior:
new FileInputStream("text_utf8.txt")
→ obrim el fitxer en mode binari.new InputStreamReader(..., StandardCharsets.UTF_8)
→ convertim bytes a caràcters amb el charset UTF-8.new BufferedReader(...)
→ afegim la lectura per línies amb buffer.br.readLine()
→ llig línies de text en UTF-8 correctament.while (...)
→ recorrem fins al final.System.out.println(linia)
→ mostrem el text.
2. Escriptura de text (Writer)
2.1 FileWriter
Usarem esta classe per: escriure caràcters amb charset del sistema.
Excepcions requerides (checked):
- Constructors/escriptura llancen
IOException
.
Mètodes:
void write(String s)
→ escriu una cadena completa en el fitxer.void write(char[] cbuf, int off, int len)
→ escriu només part d’un array de caràcters.void flush()
→ força a escriure les dades pendents al fitxer.void close()
→ tanca el flux i aboca les dades restants.
Usa el charset del sistema per defecte.
Exemple:
try (FileWriter fw = new FileWriter("nota.txt")) {
fw.write("Hola món");
}
Al codi anterior:
new FileWriter("nota.txt")
→ crea o sobreescriu el fitxer en mode text.fw.write("Hola món")
→ escriu la cadena.- El fitxer es tanca automàticament i es guarden les dades.
2.2 BufferedWriter
Usarem esta classe per: escriure línies.
Excepcions requerides (checked):
- Escriptures/flush/close llancen
IOException
.
Mètodes:
void write(String s)
/write(char[] cbuf, int off, int len)
→ escriuen text de forma més eficient gràcies al buffer.void newLine()
→ escriu un salt de línia segons el sistema operatiu (portàtil, millor que\n
).void flush()
→ força a escriure les dades que hi ha al buffer.void close()
→ tanca el flux.
Exemple:
try (BufferedWriter bw = new BufferedWriter(new FileWriter("sortida.txt"))) {
bw.write("Primera línia");
bw.newLine();
bw.write("Segona línia");
}
Al codi anterior:
new FileWriter("sortida.txt")
→ obrim fitxer de text.new BufferedWriter(...)
→ millorem l’eficiència d’escriptura.bw.write("Primera línia")
→ escriu text.bw.newLine()
→ inserix salt de línia.bw.write("Segona línia")
→ escriu més text.- Al tancar-se, s’escriu tot al disc.
2.3 OutputStreamWriter
Usarem esta classe per: escriure text amb charset concret.
Excepcions requerides (checked):
- Escriptura/flush/close llancen
IOException
.
Mètodes:
void write(String s)
/write(char[] cbuf, int off, int len)
→ escriu caràcters convertint-los a bytes segons el charset indicat.String getEncoding()
→ retorna el charset realment en ús.void flush()
→ força a escriure dades pendents.void close()
→ tanca el flux.
Exemple:
try (OutputStreamWriter osw =
new OutputStreamWriter(new FileOutputStream("resum.txt"), StandardCharsets.UTF_8)) {
osw.write("Accents i caràcters: informació, valència");
}
Al codi anterior:
new FileOutputStream("resum.txt")
→ crea el fitxer en mode binari.new OutputStreamWriter(..., UTF_8)
→ converteix caràcters a bytes amb codificació UTF-8.osw.write(...)
→ escriu la cadena amb accents.- El tancament guarda les dades al disc.
2.4 PrintWriter
Usarem esta classe per: escriure amb format.
Excepcions requerides (checked):
- Les operacions d’escriptura no llancen checked directament; comprova amb
checkError()
.
Mètodes:
void print(String s)
→ escriu un text sense salt de línia.void println(String s)
→ escriu text i després un salt de línia.PrintWriter printf(String fmt, Object... args)
/format(...)
→ escriu text amb format (semblant aprintf
de C).boolean checkError()
→ comprova si ha passat algun error d’escriptura.void flush()
→ força l’escriptura immediata.void close()
→ tanca el flux.- (Constructor amb
autoFlush=true
) → fa que cadaprintln()
oprintf()
aboque automàticament.
Exemple:
try (PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter("informe.txt")))) {
pw.println("ID\tNom\tNota");
pw.printf("%d\t%s\t%.2f%n", 1, "Anna", 9.25);
}
Al codi anterior:
- Obrim
informe.txt
ambFileWriter
. - L’emboliquem amb
BufferedWriter
iPrintWriter
. pw.println(...)
→ escriu la capçalera.pw.printf(...)
→ escriu un registre amb format.- Al tancar, es guarden les dades.
Streams estàndard
System.in
→ entrada de consola (InputStream
). Exemple:
try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
System.out.print("Introdueix el teu nom: ");
String nom = br.readLine();
System.out.println("Hola " + nom);
}
Al codi anterior:
System.in
→ entrada de consola en bytes.InputStreamReader(..., UTF-8)
→ converteix a caràcters.BufferedReader
→ llegim una línia.- Mostrem un missatge personalitzat.
System.out
→ sortida consola (PrintStream
), amb print
, println
, printf
.
System.err
→ errors de consola. Exemple:
System.err.println("Error: fitxer no trobat");
Al codi anterior:
System.err
→ canal separat per a errors.- Mostra un missatge independent de l’eixida normal (
System.out
).
En resum
- Fluxos de bytes (
InputStream
/OutputStream
) → dades binàries. - Fluxos de caràcters (
Reader
/Writer
) → text. - Buffered → rendiment.
- Data Stream → primitives binàries.
- StreamReader/Writer → control de charset.
- PrintWriter → text amb format.
- try-with-resources i gestió d’excepcions → bones pràctiques.
ANNEX I
Patró de disseny: Decorator (aplicat als Streams)
Idea clau: en Java I/O, quasi tots els “streams decoradors” emboliquen un altre stream per afegir funcionalitats sense heretar-ne el codi. Això et permet compondre capes: comences amb un stream base (accés a origen/destinació) i hi afegixes decoradors (buffer, primitives, text, línies, format…).
Per què usar-lo ací?
- Flexibilitat: combina només el que necessites (buffer? text? format?).
- Reutilització: capes independents i intercanviables.
- Simplicitat: tanques només la capa externa; la resta es tanquen en cascada.
Peces (amb les classes de streams)
-
Components base (bytes):
FileInputStream
,FileOutputStream
-
Decoradors de bytes:
BufferedInputStream
,BufferedOutputStream
(buffer de rendiment)DataInputStream
,DataOutputStream
(tipus primitius binaris) -
“Pont” bytes→caràcters (bridges que també actuen com a decoradors):
InputStreamReader
(llegir text amb charset),OutputStreamWriter
(escriure text amb charset) -
Decoradors de caràcters (text):
BufferedReader
(línies i buffer de lectura),BufferedWriter
(buffer d’escriptura),PrintWriter
(format)
Regla d’ORDE recomanada de capes
- Base (bytes) → 2) Buffer/Primitives (bytes) → 3) Pont a text (si cal) → 4) Decoradors de text → 5) Format (si cal)
- Lectura binària típica:
FileInputStream
→BufferedInputStream
→DataInputStream
- Lectura de text típica:
FileInputStream
→InputStreamReader(UTF-8)
→BufferedReader
- Escriptura binària típica:
FileOutputStream
→BufferedOutputStream
→DataOutputStream
- Escriptura de text típica:
FileOutputStream
→OutputStreamWriter(UTF-8)
→BufferedWriter
→PrintWriter
(opcional)
Nota: Si vas a text, el pont (
InputStreamReader
/OutputStreamWriter
) ha d’anar abans dels decoradors de text.
Combinacions possibles (amb les classes dels apunts)
Lectura (bytes / text):
FileInputStream
FileInputStream
→BufferedInputStream
FileInputStream
→DataInputStream
FileInputStream
→BufferedInputStream
→DataInputStream
FileInputStream
→InputStreamReader(UTF-8)
FileInputStream
→InputStreamReader(UTF-8)
→BufferedReader
Escriptura (bytes / text):
FileOutputStream
FileOutputStream
→BufferedOutputStream
FileOutputStream
→DataOutputStream
FileOutputStream
→BufferedOutputStream
→DataOutputStream
FileOutputStream
→OutputStreamWriter(UTF-8)
FileOutputStream
→OutputStreamWriter(UTF-8)
→BufferedWriter
FileOutputStream
→OutputStreamWriter(UTF-8)
→BufferedWriter
→PrintWriter
Evita dos buffers seguits en la mateixa direcció (p. ex.
BufferedInputStream
+BufferedReader
a la vegada): amb un n’hi ha prou.
Taula resum de combinacions típiques
Propòsit | Combinació (de fora cap a dins) | Notes ràpides |
---|---|---|
Lectura binària amb primitives | DataInputStream(BufferedInputStream(FileInputStream)) | Ràpid + readInt()/readUTF() |
Lectura text amb línies (UTF-8) | BufferedReader(InputStreamReader(FileInputStream, "UTF-8")) | readLine() i encoding controlat |
Escriptura binària amb primitives | DataOutputStream(BufferedOutputStream(FileOutputStream)) | writeInt()/writeUTF() |
Escriptura text (UTF-8) amb format | PrintWriter(BufferedWriter(OutputStreamWriter(FileOutputStream, "UTF-8"))) | printf/println + buffer |
Referències internes: per a detalls de cada classe, vegeu les seccions de
File*Stream
,Buffered*
,Data*Stream
,*StreamReader/Writer
,BufferedReader/Writer
iPrintWriter
.
Exemples més usats
A) Lectura BINÀRIA amb buffer + primitives
Combinació: FileInputStream
→ BufferedInputStream
→ DataInputStream
try (FileInputStream fis = new FileInputStream("notes.dat"); // base: bytes des del fitxer
BufferedInputStream bis = new BufferedInputStream(fis); // decorador: buffer de lectura
DataInputStream dis = new DataInputStream(bis)) { // decorador: llegir tipus primitius
int n = dis.readInt(); // primer camp: quants registres hi ha
for (int i = 0; i < n; i++) {
long id = dis.readLong(); // llegim un long
String nom = dis.readUTF(); // llegim un String (Modified UTF-8)
double nota = dis.readDouble(); // llegim un double
System.out.printf("%d %s %.2f%n", id, nom, nota); // eixida per consola
}
} catch (EOFException e) {
System.out.println("Final de fitxer inesperat.");
} catch (FileNotFoundException e) {
System.out.println("Fitxer inexistent o sense permisos.");
} catch (IOException e) {
System.out.println("Error d'entrada/eixida: " + e.getMessage());
}
Al codi anterior:
- Base (
FileInputStream
) per llegir bytes del fitxer. - Buffer (
BufferedInputStream
) per reduir accessos a disc. - Primitives (
DataInputStream
) per llegir int/long/double/String en binari. - L’ordre de lectura ha de coincidir amb l’ordre d’escriptura.
B) Lectura de TEXT amb charset + línies
Combinació: FileInputStream
→ InputStreamReader(UTF-8)
→ BufferedReader
try (BufferedReader br = new BufferedReader(
new InputStreamReader(new FileInputStream("entrada.txt"), "UTF-8"))) {
String linia;
while ((linia = br.readLine()) != null) { // llegim línia a línia
System.out.println(linia); // mostrem el text
}
} catch (IOException e) {
System.out.println("Error d'entrada/eixida: " + e.getMessage());
}
Variant sense charset explícit (FileReader + BufferedReader):
try (BufferedReader br = new BufferedReader(new FileReader("entrada.txt"))) {
String linia;
while ((linia = br.readLine()) != null) {
System.out.println(linia);
}
}
catch (IOException e) {
System.out.println("Error d'entrada/eixida: " + e.getMessage());
}
Al codi anterior:
- Base bytes (
FileInputStream
). - Pont (
InputStreamReader("UTF-8")
) per convertir bytes→caràcters amb encoding controlat. - Decorador de text (
BufferedReader
) per areadLine()
i rendiment en text.
Nota: aquesta variant depén del charset del sistema (pot fallar amb accents). Preferible usar la combinació amb InputStreamReader(“UTF-8”).
C) Escriptura BINÀRIA amb buffer + primitives
Combinació: FileOutputStream
→ BufferedOutputStream
→ DataOutputStream
try (DataOutputStream dos = new DataOutputStream(
new BufferedOutputStream(new FileOutputStream("notes.dat")))) {
dos.writeInt(2); // escriurem 2 registres
dos.writeLong(1L); dos.writeUTF("Anna"); dos.writeDouble(9.5);
dos.writeLong(2L); dos.writeUTF("Marc"); dos.writeDouble(7.8);
// tancant la capa externa, es fa flush i es tanquen totes les capes
} catch (IOException e) {
System.out.println("Error d'entrada/eixida: " + e.getMessage());
}
Al codi anterior:
- Base (
FileOutputStream
) per escriure bytes. - Buffer (
BufferedOutputStream
) per agrupar escriptures. - Primitives (
DataOutputStream
) per escriure int/long/double/String en binari. - Respecta l’ordre de camps si després els llegiràs amb
DataInputStream
.
D) Escriptura de TEXT amb charset + línies + format
Combinació: FileOutputStream
→ OutputStreamWriter(UTF-8)
→ BufferedWriter
→ PrintWriter
try (PrintWriter pw = new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(new FileOutputStream("informe.txt"), "UTF-8")))) {
pw.println("ID\tNom\tNota"); // capçalera amb tabuladors
pw.printf("%d\t%s\t%.2f%n", 1, "Anna", 9.25); // línia amb format
pw.printf("%d\t%s\t%.2f%n", 2, "Marc", 7.80);
// PrintWriter pot fer buffer (via BufferedWriter) i format (printf)
}
// PrintWriter/BufferedWriter/OutputStreamWriter/FileOutputStream es tanquen en cascada
Al codi anterior:
- Base (
FileOutputStream
). - Pont (
OutputStreamWriter("UTF-8")
) per convertir caràcters→bytes amb encoding correcte. - Decorador de text (
BufferedWriter
) per rendiment. - Format (
PrintWriter
) perprint/println/printf
còmode.
Trucs pràctics (Decorator amb Streams)
- Tanca només la capa externa; la resta es tanquen en cascada (patró Decorator facilita això).
- Un sol buffer per direcció sol ser suficient (
BufferedInputStream
oBufferedReader
;BufferedOutputStream
oBufferedWriter
). - Encoding sempre explícit quan passes a text (
"UTF-8"
), per a evitar sorpreses. - Capturar o propagar?
catch
és opcional: si no gestiones ací, declarathrows IOException
al mètode. - Coherència binari/text: una vegada creues el pont (Input/OutputStreamReader), ja estàs en caràcters; continua amb decoradors de text (no barreges
Data*Stream
després del reader/writer).