Sunteți pe pagina 1din 8

1

Fluxuri de intrare/ieşire

0. Suprapunerea obiectelor

Fie clasa:
class A {
... met(...) { ... }
}

Dorim ca o clasă B să poată utiliza metoda met.

O modalitate este oferită de mecanismul de extindere a claselor: declarăm o


clasă B care extinde A, construim un obiect b de tipul B şi putem invoca b.met().
Există însă limitări de aplicare, ţinând de context sau de limbaj, dintre care
menţionăm:
- de multe ori invocarea lui met trebuie făcută într-un anumit context, de
exemplu printr-un anumit obiect de tipul A, cu anumite câmpuri;
- este posibil să dorim ca din clasa B să utilizăm şi metode din alte clase; dar o
clasă poate extinde o singură clasă.

O altă modalitate, folosită în lucrul cu fluxuri, o constituie cea de suprapunere


de obiecte (în cazul fluxurilor va fi numită suprapunere de fluxuri).
Pentru simplificare, ilustrăm această variantă pentru cazul unei singure
metode:
class B {
A a;
B(A a) { this.a = a; }
... met(...) { ...; a.met(); ... }
}
situaţie în care procedăm astfel:
B b = new B(new A()); b.met(...).

Spunem că obiectul b suprapune obiectul a, respectiv că (prin suprapunere)


clasa B adaugă noi facilităţi clasei A.

1. Schema generală
Clasele ce oferă facilităţi de intrare/ieşire se găsesc în pachetul java.io.
Facilităţile de intrare/ieşire din Java au la bază noţiunea de flux. Un flux este
un obiect asociat unei succesiuni de elemente (octeţi sau caractere), citite şi scrise
secvenţial.
Pentru un flux de intrare sursa datelor poate fi un fişier, dar şi un şir sau tablou
de octeţi, respectiv caractere. Pentru un flux de ieşire, datele transmise sunt stocate
într-un fişier sau într-un tablou de octeţi, respectiv caractere.
2

Dacă la citire nu sunt încă date disponibile în flux şi nu s-a detectat sfârşitul
datelor, atunci firul de executare care realizează citirea va fi blocat până când vor
exista date disponibile.
O primă clasificare a fluxurilor are în vedere elementele de bază care sunt
transmise: caractere sau octeţi.
Object
InputStream (clasă abstractă pt. citire la nivel de octet)
OutputStream (clasă abstractă pt. scriere la nivel de octet)
Reader (clasă abstractă pentru citire la nivel de caracter)
Writer (clasă abstractă pt. scriere la nivel de caracter)

Toate clasele, interfeţele şi metodele din pachetul java.io au modificatorul


public, iar în plus aproape toate metodele conţin clauza throws IOException.

2. Fluxuri ce lucrează la nivel de octet


O structură simplificată, dar cea mai frecvent utilizată, de clase este
următoarea:

Object
DataInput (interfaţă)
InputStream (clasă abstractă)
FileInputStream
FilterInputStream
DataInputStream implements DataInput
DataOutput (interfaţă)
OuputStream (clasă abstractă)
FileOutputStream
FilterOutputStream
DataOutputStream implements DataOutput

Menţionăm că în clasele neabstracte, afară de FileInputStream şi


FileOutputStream, apare un constructor cu un parametru de tipul InputStream,
respectiv de tipul OutputStream.

Observaţie. Vom restrânge în continuare discuţia la a prezenta cele mai simple


facilităţi pentru a scrie în fişiere, respectiv de a citi din fişiere:
- octeţi : clasele FileOutputStream şi FileInputStream ;
- date de tipuri primitive şi şiruri de caractere : clasele DataOutputStream şi
DataInputStream;
- obiecte : vezi "Serializarea obiectelor".

 Scrierea şi citirea de octeţi


Clasele FileInputStream şi FileOutputStream permit crearea unui flux
de intrare, respectiv ieşire, asociat unui fişier cu un nume dat. Ele permit, printre
altele, citirea şi scrierea de octeţi.
3

Exemplul 1. Dorim să copiem un fişier cu un nume dat sub un alt nume.


Se încearcă întâi crearea unui flux asociat fişierului cu numele citit de la
intrare. Dacă acest fişier nu există, programul se opreşte.
Este apoi citit numele noului fişier şi acesta este creat, ştergând eventuala
versiune anterioară. Apoi din primul fişier este citit consecutiv câte un octet (prin
invocarea metodei read), care este copiat în al doilea fişier. Sfârşitul fişierului este
atins atunci când metoda read întoarce valoarea -1.

import java.io.*;
import java.util.*;
class Copiere {
public static void main(String[] args) throws IOException {
Scanner sc = new Scanner(System.in);
System.out.print("Fisier cerut: ");
String fisier = sc.next();
InputStream is = null;
try { is = new FileInputStream(fisier); }
catch(FileNotFoundException e) {
System.out.println("Fisier inexistent");
System.exit(0);
}
System.out.print("Numele copiei : ");
fisier = sc.next();
OutputStream os = new FileOutputStream(fisier);
int c = 123;
while( (c=is.read()) != -1) os.write(c);
System.out.println("\nFisier copiat!");
is.close(); os.close();
}
}

 Interfaţa DataOutput
Conţine metode menite a scrie, într-un flux de ieşire neprecizat, date de tipuri
primitive, precum şi şiruri de caractere.
void write(int b)
void write(byte[] b) throws NullPointerException
void write(byte[] b, int init, int lung)
throws NullPointerException, IndexOutOfBoundsException
void writeBytes(String s) throws NullPointerException
void writeChars(String s) throws NullPointerException
void writeUTF(String s) void writeBoolean(boolean v)
void writeByte(int v) void writeShort(int v)
void writeChar(int v) void writeInt(int v)
void writeLong(long v) void writeFloat(float v)
void writeDouble(double v)
4

 Interfaţa DataInput
Este "complementara" interfeţei DataOutput. Metodele citesc octeţi din
fluxul de intrare; pe baza acestor octeţi sunt reconstituite date de tipuri primitive sau
şiruri de caractere.
int skipBytes(int n) boolean readBoolean()
byte readByte() short readShort()
char readChar() int readInt()
long readLong() float readFloat()
double readDouble() String readLine()
String readUTF()
Observaţie. Dacă s-a ajuns la sfârşitul fluxului de intrare înainte de a se fi citit
numărul dorit de octeţi, va fi lansată excepţia EOFException.
Se presupune că datele citite au fost scrise în fişier cu metodele
complementare anunţate în interfaţa DataOutput.

 Clasa abstractă OutputStream


Această clasă include metodele:
void write(byte[] b) throws NullPointerException
void write(byte[] b, int init, int lung)
throws NullPointerException, IndexOutOfBoundsException
void flush()
void close()

 Clasa abstractă InputStream


Această clasă cuprinde metodele:
void read(byte[] b) throws NullPointerException
void read(byte[] b, int init, int lung)
throws NullPointerException, IndexOutOfBoundsException
long skip(long n)
int available()
void close()

 Clasele FilterInputStream şi FilterOutputStream


class FilterOutputStream extends OutputStream {
protected OutputStream out;
FilterOutputStream(OutputStream out)
redefiniri ale metodelor clasei OutputStream
}
class FilterInputStream extends InputStream {
protected InputStream in;
FilterInputStream(InputStream in)
redefiniri ale metodelor clasei InputStream
}
5

 Clasele FileOutputStream şi FileInputStream


Metodele clasei FileOutputStream implementează, respectiv redefinesc
metodele cu aceleaşi nume şi signatură din OutputStream, aplicându-le pentru
fluxul de ieşire primit ca argument de constructor.
class FileOutputStream extends OutputStream {
FileOutputStream(String nume)
throws SecurityException,FileNotFoundException
FileOutputStream(String nume, boolean adaug)
throws SecurityException,FileNotFoundException
redefiniri ale metodelor write, flush şi close din OutputStream
}
Constructorul cu un parametru deschide fluxul de ieşire nume.
class FileInputStream extends InputStream {
FileInputStream(String nume)
throws SecurityException,FileNotFoundException
int read()
redefiniri ale metodelor read, skip, available şi close
din OutputStream
}
Constructorul clasei deschide fluxul de intrare constituit de fişierul precizat
prin şirul de caractere nume.

 Scrierea şi citirea de date primitive

Dacă dorim să scriem/citim tipuri primitive sau şiruri de caractere, vom folosi
clasele DataOutputStream şi DataInputStream.

Exemplul 2. Într-o primă etapă vom citi de la intrarea standard un număr


natural n şi apoi n numere întregi; vom crea în directorul curent un fişier cu numele
out.dat în care vom scrie datele citite. Într-o a doua etapă vom citi din fişierul
out.dat valoarea n şi cele n numere şi le vom tipări la ieşirea standard.

Prima etapă este realizată de următorul program:


import java.io.*;
import java.util.*;
class Unu {
public static void main(String[] sir) throws Exception {
int n;
Scanner sc = new Scanner(System.in);
DataOutputStream dos = new DataOutputStream(
new FileOutputStream("out.dat"));
System.out.println("n= "); n = sc.nextInt();
dos.writeInt(n);
System.out.println("Introduceti " +n+ " numere intregi:");
for(int i=0; i<n; i++) dos.writeInt(sc.nextInt());
6

dos.close();
}
}
unde:
- prin new FileOutputStream("out.dat") este creat fişierul out.dat;
- fluxul dos de tipul DataOutputStream foloseşte metodele writeInt şi
writeDouble (anunţate în interfaţa DataOutput şi implementate în clasa
DataOutputStream) pentru a scrie în fişierul out.dat;
- pentru închiderea fişierului este invocată metoda close a clasei
FilterInputStream (moştenită din DataOutputStream).

A doua etapă este realizată de următorul program:


import java.io.*;
class Doi {
public static void main(String[] ss) throws Exception {
double d; int n;
DataInputStream dis = new DataInputStream(
new FileInputStream("out.dat") );
n = dis.readInt();
for (int i=0; i<n; i++)
System.out.print(dis.readInt() + "\t" );
System.out.println(); dis.close();
}
}
unde:
- prin new FileInputStream("out.dat") este deschis fişierul out.dat;
- fluxul dis de tipul DataInputStream foloseşte metodele readInt şi
readDouble (anunţate în interfaţa DataInput şi implementate în clasa
DataInputStream) pentru a citi din fişierul out.dat;
- pentru închiderea fişierului este invocată metoda close a clasei
FilterInputStream (moştenită din DataInputStream).

 Clasele DataOutputStream şi DataInputStream


Clasele DataOutputStream şi DataInputStream oferă în plus
posibilitatea ca fluxurile să nu mai fie privite la nivel de octet, ci ca succesiuni de date
primitive sau şiruri de caractere. Datele vor fi scrise în fluxul de ieşire într-un format
independent de modul de reprezentare al datelor în sistemul pe care se lucrează.

class DataOutputStream extends FilterOutputStream


implements DataOutput {
DataOutputStream(OutputStream out)
protected int written;
implementarea metodelor write din interfaţa DataOutput
void flush()
final int size()
}
7

class DataInputStream extends FilterInputStream


implements DataInput {
DataInputStream(InputStream in)
implementarea metodelor read şi skipBytes din interfaţa DataOutput
}

3. Fluxuri ce lucrează la nivel de caracter


O structură simplificată de clase este următoarea:
Object
Writer (clasă abstractă)
OutputStreamWriter
FileWriter
PrintWriter
Reader (clasă abstractă)
InputStreamReader
FileReader

din care vom folosi numai clasa PrintWriter, care oferă facilitatea de a scrie date
în fluxul de ieşire într-un mod formatat, specific metodelor toString. Nu este
lansată niciodată excepţia IOException.

class PrintWriter extends Writer {


protected Writer out;
PrintWriter(Writer out)
PrintWriter(Writer out, boolean adaug)
PrintWriter(OutputStream out)
PrintWriter(OutputStream out, boolean adaug)
void flush() void close()
void write(int c) void write(String s)
void write(String s, int init, int lung)
void print(boolean b) void println(boolean b)
void print(char c) void println(char c)
void print(int i) void print(int i)
void print(long l) void println(long l)
void print(float f) void println(float f)
void print(double d) void println(double f)
void print(char[] s) void println(char[] s)
void print(String s) void println(String x)
void print(Object obj) void println(Object obj)
void println()
}

Exemplul 3. Utilizarea clasei PrintWriter pentru scrierea în fişiere text.


Şirul cifrelor este scris atât la ieşirea standard, cât şi în fişierul out.
8

import java.io.*;
class Print {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("out");
PrintWriter out = new PrintWriter(fos);
PrintWriter stdout = new PrintWriter(System.out);
out.println("Cifrele zecimale sunt:");
for(int i=0; i<10; i++) out.print(i + "\t");
out.println();
stdout.println("Cifrele zecimale sunt:");
for(int i=0; i<10; i++) stdout.print(i + "\t");
stdout.println();
out.close(); // stdout.close();
}
}

Facem observaţia că neînchiderea explicită a unui flux poate conduce la


neînregistrarea în fişier a tuturor datelor prevăzute a fi scrise în el prin program.