Java - curs

249

Click here to load reader

description

Java este un limbaj de programare de nivel înalt, dezvoltat de JavaSoft, companie în cadrul firmei Sun Microsystems. Dintre caracteristicile principale ale limbajului amintim: • simplitate, elimina supraîncarcarea operatorilor, mostenirea multipla si toate "facilitatile" ce pot provoca scrierea unui cod confuz. • robustete, elimina sursele frecvente de erori ce apar in programare prin eliminarea pointerilor, administrarea automata a memoriei si eliminarea fisurilor de memorie printr-o procedura de colectare a 'gunoiului' care ruleaza în fundal. Un program Java care a trecut de compilare are proprietatea ca la executia sa nu "crapa sistemul". • complet orientat pe obiecte - elimina complet stilul de programare procedural • usurinta in ceea ce priveste programarea in retea • securitate, este cel mai sigur limbaj de programare disponibil în acest moment, asigurând mecanisme stricte de securitate a programelor concretizate prin: verificarea dinamica a codului pentru detectarea secventelor periculoase, impunerea unor reguli stricte pentru rularea programelor lansate pe calculatoare aflate la distanta, etc • este neutru din punct de vedere arhitectural • portabililtate, cu alte cuvinte Java este un limbaj independent de platforma de lucru, aceeasi aplicatie ruland, fara nici o modificare, pe sisteme diferite cum ar fi Windows, UNIX sau Macintosh, lucru care aduce economii substantiale firmelor care dezvolta aplicatii pentru Internet. • compilat si interpretat • asigura o performanta ridicata a codului de octeti • permite programarea cu fire de executie (multitheaded) • dinamicitate • este modelat dupa C si C++, trecerea de la C, C++ la Java facându-se foarte usor. • permite creearea unor documente Web îmbunatatite cu animatie si multimedia.

Transcript of Java - curs

Curs de Programare

HYPERLINK "http://java.sun.com/" JavaTM

Curs 1 Introducere in JavaCurs 2 Obiecte si clase in JavaCurs 3 ExceptiiCurs 3 Fluxuri (Intrari / Iesiri)Curs 4 InterfeteCurs 4 PacheteCurs 5 Serializarea obiectelorCurs 5 ColectiiCurs 6 Interfata graficaCurs 7 DesenareaCurs 8 Fire de executieCurs 9 Programarea n reteaCurs 10 Applet-uriCurs 11 Lucrul cu baze de date n JavaCurs 12 Organizarea fisierelor .java si .class

Curs 1 Introducere in Java

Ce este Java ?

Crearea unei aplicatii simple

Crearea unui applet simplu

Structura lexicala a limbajului Java

Setul de caractere

Cuvinte cheie

Identificatori

Literali

Separatori

Operatori

Comentarii

Tipuri de date

Variabile

Controlul executiei

Vectori

Siruri de caractere

Folosirea argumentelor de la linia de comanda

Top

Ce este Java ?

Limbajul de programare Java

Java este un limbaj de programare de nivel nalt, dezvoltat de JavaSoft, companie n cadrul firmei Sun Microsystems. Dintre caracteristicile principale ale limbajului amintim:

simplitate, elimina suprancarcarea operatorilor, mostenirea multipla si toate "facilitatile" ce pot provoca scrierea unui cod confuz.

robustete, elimina sursele frecvente de erori ce apar in programare prin eliminarea pointerilor, administrarea automata a memoriei si eliminarea fisurilor de memorie printr-o procedura de colectare a 'gunoiului' care ruleaza n fundal. Un program Java care a trecut de compilare are proprietatea ca la executia sa nu "crapa sistemul".

complet orientat pe obiecte - elimina complet stilul de programare procedural

usurinta in ceea ce priveste programarea in retea

securitate, este cel mai sigur limbaj de programare disponibil n acest moment, asigurnd mecanisme stricte de securitate a programelor concretizate prin: verificarea dinamica a codului pentru detectarea secventelor periculoase, impunerea unor reguli stricte pentru rularea programelor lansate pe calculatoare aflate la distanta, etc

este neutru din punct de vedere arhitectural

portabililtate, cu alte cuvinte Java este un limbaj independent de platforma de lucru, aceeasi aplicatie ruland, fara nici o modificare, pe sisteme diferite cum ar fi Windows, UNIX sau Macintosh, lucru care aduce economii substantiale firmelor care dezvolta aplicatii pentru Internet.

compilat si interpretat

asigura o performanta ridicata a codului de octeti

permite programarea cu fire de executie (multitheaded)

dinamicitate

este modelat dupa C si C++, trecerea de la C, C++ la Java facndu-se foarte usor.

permite creearea unor documente Web mbunatatite cu animatie si multimedia.

Java : un limbaj compilat si interpretat

In functie de modul de executie al programelor, limbajele de programare se mpart n doua categorii :

interpretate : instructiunile sunt citite linie cu linie de un program numit interpretor si traduse n instructiuni masina; avantaj : simplitate; dezavantaje : viteza de executie redusa

compilate : codul sursa al programelor este transformat de compilator ntr-un cod ce poate fi executat direct de procesor; avantaj : executie rapida; dezavantaj : lipsa portabilitatii, codul compilat ntr-un format de nivel scazut nu poate fi rulat dect pe platforma pe care a fost compilat.

Programele Java sunt fi att interpretate ct si compilate.Codul de octeti este diferit de codul masina. Codul masina este reprezentat de o succesiune de 0 si 1; codurile de octeti sunt seturi de instructiuni care seamana cu codul scris n limbaj de asamblare. Codul masina este executat direct de catre procesor si poate fi folosit numai pe platforma pe care a fost creat; codul de octeti este interpretat de mediul Java si de aceea poate fi rulat pe orice platforma care foloseste mediul de executie Java.

Cod sursa Java -> (compilare) -> Cod de octeti -> (interpretare)

Crearea unei aplicatii simple

1. Scriererea codului sursa

2. 2. class FirstApp {

3. 3. public static void main( String args[]) {

4. 4.

System.out.println("Hello world");

5. 5. }

6. 6. }

Toate aplicatiile Java contin o clasa principala(primara) n care trebuie sa se gaseasca metoda main. Clasele aplicatiei se pot gasi fie ntr-un singur fisier, fie n mai multe.

7. Salvarea fisierelor sursa

Se va face n fisiere cu extensia .javaFiserul care contine codul sursa al clasei primare trebuie sa aiba acelasi nume cu clasa primara a aplicatiei (clasa care contine metoda main)Obs: Java face distinctie ntre literele mari si mici.

C:/java/FirstApp.java

8. Compilarea aplicatiei

Se foloseste compilatorul Java, javacApelul compilatorului se face pentru fisierul ce contine clasa principala a aplicatiei. Compilatorul creeaza cte un fisier separat pentru fiecare clasa a programului; acestea au extensia .class si sunt plasate n acelasi director cu fisierele sursa.

javac FirstApp.java -> FirstApp.class

9. Rularea aplicatiei

Se face cu interpretorul java, apelat pentru unitatea de compilare corespunzatoare clasei principale, fiind nsa omisa extensia .class asociata acesteia.

java FirstApp

Rularea unei aplicatii care nu foloseate interfata grafica, se va face ntr-o fereastra sistem.

Crearea unui applet

Crearea structurii de fisere si compilarea applet-urilor sunt identice ca n cazul aplicatiilor. Difera n schimb structura programului si modul de rulare al acestuia.

1. Scrierea codului sursa si salvarea n fisier

2. 2. import java.awt.* ;

3. 3. import java.applet.* ;

4. 4. public class FirstApplet extends Applet {

5. 5. Image img;

6. 6. public void init() {

7. 7.

img = getImage(getCodeBase(), "taz.gif");

8. 8. }

9. 9. public void paint (Graphics g) {

10. 10.

g.drawImage(img, 0, 0, this);

11. 11.

g.drawOval(100,0,150,50);

12. 12.

g.drawString("Hello! My name is Taz!", 110, 25);

13. 13. }

14. 14. }

Salvarea se va face n fisierul FirstApplet.java

15. Compilarea applet-ului

javac FirstApplet.java -> FirstApplet.class

16. Rularea applet-ului

Applet-urile nu ruleaza independent. Ele pot fi rulate doar prin intermediul unui browser : Internet Explorer, Netscape sau printr-un program special cum ar fi appletviewer-ul din setul JDK.

1. Creearea unui fisier HTML pentru miniaplicatie (exemplu.html)

2. 2.

3. 3.

4. 4.

First Java Applet

5. 5.

6. 6.

7. 7.

8. 8.

9. 9.

10. 10.

11. Vizualizarea appletului

appletviewer exemplu.html

Structura lexicala a limbajului

Setul de caractere

Limbajului Java lucreaza n mod nativ folosind setul de caractere Unicode. Acesta este un standard international care nlocuieste vechiul set de caractere ASCII si care foloseste pentru reprezentarea caracterelor 2 octeti, ceea ce nseamna ca se pot reprezenta 65536 de semne, spre deosebire de ASCII, unde era posibila reprezentarea a 256 de caractere. Primele 256 caractere Unicode corespund celor din ASCII, referirea la celelate facndu-se prin \uxxxx, unde xxxx reprezinta codul caracterului.

Ex:

\u0030 - \u0039 : cifre ISO-Latin 0 - 9 \u0660 - \u0669 : cifre arabic-indic 0 - 9 \u4e00 - \u9fff : litere din alfabetul Han (Chinez, Japonez, Coreean)

Cuvinte cheie

Cuvintele rezervate n Java sunt cele din C++, cu cteva exceptii

Identificatorii

Sunt secvente nelimitate de litere si cifre Unicode, ncepand cu o litera. Identificatorii nu au voie sa fie identici cu cuvintele rezervate.

Literalii (constantele)

Literalii pot fi de urmatoarele tipuri

literali ntregiSunt acceptate 3 baze de numeratie : baza 10, baza 16 (ncep cu caracterele 0x) si baza 8 (ncep cu cifra 0) si pot fi de doua tipuri:

normali, (se reprez pe 4 octeti - 32 biti)

lungi (8 octeti - 64 biti) : se termina cu caracterul L (sau l).

literali flotantiPentru ca un literal sa fie considerat flotant el trebuie sa aiba cel putin o zecimala dupa virgula, sa fie n notatie exponentiala sau sa aiba sufixul F sau f pentru valorile normale (reprez. pe 32 biti), respectiv D sau d pentru valorile duble (reprez. pe 64 biti)

literali logicitrue : valoarea booleana de adevarfalse : valoarea booleana de falsAtentie: spre deosebire de C++, literalii ntregi 1 si 0 nu mai au rolul de adevarat si false

literali caracterUn literal de tip caracter este utilizat pentru a exprima caracterele codului Unicode. Reprezentarea se face fie folosind o litera, fie o secventa escape scrisa ntre apostrofuri. Secventele escape permit reprezentarea caracterelor care nu au reprezentare grafica si reprezentarea unor caractere speciale precum backslash, caracterul apostrof, etc. Secvente escape predefinite n Java:

Cod Secventa Escape Caracter

\u0008 '\b' Backspace(BS)

\u0009 '\t' Tab orizontal (HT)

\u000a '\n' Linie noua - linefeed (LF)

\u000c '\f' Pagina noua - formfeed (FF)

\u000d '\r' Inceput de rand (CR)

\u0022 '\"' Ghilimele

\u0027 '\'' Apostrof

\u005c '\\' Backslash

literali siruri de caractereUn literal sir de caractere este format din zero sau mai multe caractere ntre ghilimele. Caracterele care formeaza sirul de caractere pot fi caractere grafice sau secvente escape ca cele definite la literalii caracter. Daca sirul este prea lung el poate fi scris ca o concatenare de subsiruri de dimensiune mai mica. Concatenarea sirurilor se face cu operatorul + ("Ana " + " are " + " mere "). Sirul vid este "". Dupa cum vom vedea, orice sir este de fapt o instanta a clasei String, definita n pachetul java.lang.

Separatori

Un separator este un caracter care indica sfrsitul unei unitati lexicale si nceputul alteia. In Java separatorii sunt urmatorii: ( ) { } [ ] ; , . Instructiunile unui program se separa cu punct si virgula

Operatori

atribuirea: = operatori matematici: +, -, *, /, % Este permisa notatia prescurtata de forma lval op= rval (ex: n += 2) Exista operatorii pentru autoincrementare si autodecrementare (post si pre) ex: x++, ++x, n--, --n Observatie: evaluarea expresiilor logice se face prin metoda scurtcircuitului (evaluarea se opreste n momentul n care valoarea de adevar a expresiei este sigur determinata)

operatori logici: &&(and), ||(or), !(not)

operatori relationali: > (shift la dreapta fara semn)

operatorul if-else: expresie_logica ? val_pt_true : val_pt_false ;

operatorul , (virgula) folosit pentru evaluarea secventiala a operatiilor int x=0, y=1, z=2;

operatorul + pentru concatenarea sirurilor:

String s="abcd"

int x=100;

System.out.println(s + " - " + x);

operatori pentru conversii (cast) : (tip_de_data)

int i = 200;

long l = (long)i; //widening conversion - conversie prin extensie

long l2 = (long)200;

int i2 = (int)l2; //narrowing conversion - conversie prin contractie

Comentarii

In Java exista trei feluri de comentarii:

Comentarii pe mai multe linii, nchise ntre /* si */.

Comentarii pe mai multe linii care tin de documentatie, nchise ntre /** si */. Textul dintre cele dou secvente este automat mutat n documentatia aplicatiei de catre generatorul automat de documentatie javadoc.

comentarii pe o singura linie care ncep cu //.

Observatii:

1. nu putem sa scriem comentarii n interiorul altor comentarii.

2. nu putem introduce comentarii n interiorul literalilor caracter sau sir de caractere.

3. secventele /* si */ pot sa apara pe o linie dupa secventa // dar si pierd semnificatia; la fel se ntmpl cu secventa // n comentarii care ncep cu /* sau /**.

Tipuri de date

In Java tipurile de date se mpart n doua categorii: tipuri primitive de date si tipuri referinta. Java porneste de la premiza ca "orice este un obiect". Asadar tipurile de date ar trebui sa fie de fapt definite de clase si toate variabilele ar trebui sa memoreze de fapt instante ale acestor clase (obiecte). In principiu acest lucru este adevarat, nsa, pentru usurinta programarii, mai exista si asa numitele tipurile primitive de date, care sunt cele uzuale :

aritmetice

ntregi: byte (1 octet), short (2), int (4), long (8)

reale: float (4 octeti), double (8) caracter : char (2 octeti)

logic : boolean (true si false) In alte limbaje formatul si dimensiunea tipurilor primitive de date folosite ntr-un program pot depinde de platforma pe care ruleaza programul. In Java acest lucru nu mai este valabil, orice dependenta de o anumita platforma specifica fiind eliminata. Vectorii, clasele si interfetele sunt tipuri referinta. Valoarea unei variabile de acest tip este, n contrast cu tipurile primitive, o referinta (adresa de memorie) catre valoarea sau multimea de valori reprezentata de variabila respectiva. Exista trei tipuri de date C care nu sunt suportate de limbajul Java. Acestea sunt: pointer, struct si union. Pointerii au fost eliminati din cauza ca erau o sursa constanta de erori, locul lor fiind luat de tipul referinta, iar struct si union nu si mai au rostul att timp ct tipurile compuse de date sunt formate n Java prin intermediul claselor.

Variabile

Variabilele pot avea ca tip fie un tip primitiv de data, fie o referinta la un obiect.

Declararea variabilelor Tip nume_variabila

Initializarea variabilelor Tip nume_variabila = valoare

Declararea constantelor final Tip nume_variabila

Conventia de notare a variabilelor in Java este data de urmatoarele criterii:

1. variabilele finale (constante) se scriu cu majuscule

2. variabilele normale se scriu astfel : prima litera cu litera mica, daca numele variabilei este format din mai multi atomi lexicali, atunci primele litere ale celorlalti atomi se scriu cu majuscule, de exemplu:

3. 3. final double PI = 3.14;

4. 4. int valoare = 100;

5. 5. long numarElemente = 12345678L;

6. 6. String bauturaMeaPreferata = "apa";

In functie de locul n care sunt declarate variabile se mpart n urmatoatele categorii:

1. Variabile membre, declarate n interiorul unei clase, vizibile pentru toate metodele clasei respective si pentru alte clase n functie de nivelul lor de acces (vezi "Declararea variabilelor membre")

2. Variabile locale, declarate ntr-o metoda sau ntr-un bloc de cod, vizibile doar n metoda/blocul respectiv

3. Parametri metodelor, vizibili doar n metoda respectiva

4. Parametrii de la tratarea exceptiilor

Obs: variabilele declarate ntr-un for pentru controlul ciclului, ramn locale corpului ciclului.

for(int i=0; i eroare la compilare

Prin conventie numele variabilelor finale se scriu cu litere mari. Folosirea lui final aduce o flexibilitate sporita n lucrul cu constante, n sensul ca valoarea unei variabile nu trebuie specificata neaparat la declararea ei (ca n exemplul de mai sus), ci poate fi specificata si ulterior, dupa care ea nu va mai putea fi modificata.

class Test {

final int MAX;

Test() {

MAX = 100;// legal

MAX = 200;// ilegal -> eroare la compilare

}

}

transient Este folosit la serializarea obiectelor, pentru a specifica ce variabile membre ale unui obiect nu participa la serializare (vezi "Serializarea obiectelor")

volatile Este folosit pentru a semnala compilatorului sa nu execute anumite optimizari asupra membrilor unei clase. Este o facilitate avansata a limbajului Java.

Implementarea metodelor

Metodele sunt responsabile cu descrierea comportamentului unui obiect. Generic, o metoda se declara astfel:

Declararea metodelor membre

[modificatori] TipReturnat numeMetoda ( [argumente] )

[throws TipExceptie]

{

//corpul metodei

}

unde un modificator poate fi :

un specificator de acces : public, protected, private (vezi "Specificatori de acces pentru membrii unei clase")

unul din cuvintele rezervate: static, abstract, final, native, synchronized

staticDeclararea metodelor de clasa Prezenta lui declara ca o metoda este metoda de clasa si nu de instanta:

void metoda1() ;//metoda de instanta

static void metoda2();//metoda de clasa

(vezi "Membri de instanta si membri de clasa")

abstractDeclararea metodelor abstracte O metoda abstracta este o metoda care nu are implementare si trebuie sa faca parte dintr-o clasa abstracta.(vezi "Clase si metode abstracte")

final Specifica faptul ca acea metoda nu mai poate fi supradefinita n subclasele clasei n care ea este definita ca fiind finala. (vezi "Metode finale")

native In cazul n care aveti o librarie nsemnata de functii scrise n alt limaj de programare dect Java (C de exemplu), acestea pot fi refolosite din programele Java.

synchronized Este folosit n cazul n care se lucreaza cu mai multe fire de executie iar metoda respectiva se ocupa cu partajarea unei resurse comune. Are ca efect construirea unui semafor care nu permite executarea metodei la un moment dat dect unui singur fir de executie. (vezi "Fire de executie")

Tipul returnat de o metoda

Metodele pot sau nu sa returneze o valoare (un obiect) la terminarea lor. Tipul returnat poate fi att un tip primitiv de date (int, double, etc.) sau o referinta la un obiect al unei clase. In cazul n care o metoda nu returneaza nimic atunci la rubrica TipReturnat trebuie obligatoriu sa apara cuvntul cheie void. (Ex: void afisareRezultat() ) Daca o metoda trebuie sa returneze o valoare acest lucru se realizeaza prin intermediul instructiunii return, care trebuie sa apara n toate situatiile de terminare a functiei.

double radical(double x) {

if (radical >= 0)

return Math.sqrt(x);

else

System.out.println("Eroare - numar negativ!");

//Eroare la compilare - lipseste return pe aceasta ramura

}

In cazul n care n declaratia functiei tipul returnat este un tip primitiv de date, valoarea returnata la terminarea functiei trebuie sa aiba obligatoriu acel tip, altfel va fi furnizata o eroare la compilare. Daca valoarea returnata este o referinta la un obiect al unei clase, atunci clasa obiectului returnat rebuie sa coincida sau sa fie o subclasa a clasei specificate la declararea metodei. De exemplu, fie clasa Poligon si subclasa acesteia Patrat.

Poligon metoda1( ) {

Poligon p = new Poligon();

Patrat t = new Patrat();

if (...)

return p; // legal

else

return t;// legal

}

Patrat metoda2( ) {

Poligon p = new Poligon();

Patrat t = new Patrat();

if (...)

return p; // ilegal

else

return t;// legal

}

Trimiterea parametrilor catre o metoda

Signatura unei metode este data de numarul si tipul argumentelor primite de acea metoda:

metoda([tip1 argument1] [, tip2 argument2] ... )

Tipul de date al unui argument poate fi orice tip valid al limbajului, att tip primitiv de date ct si referinta la un obiect.

Ex: adaugarePersoana(String nume, int varsta, float salariu)

String este tip referinta, int si float sunt tipuri primitive

Spre deosebire de alte limbaje, n Java nu pot fi trimise ca parametri ai unei metode referinte la alte metode (functii), nsa pot fi trimise obiecte care sa contina implementarea acelor metode, pentru a fi apelate. De asemenea, n Java o metoda nu poat primi un numar variabil de argumente, ceea ce nseamna ca apelul unei metode trebuie sa se faca cu specificarea exacta a numarului si tipurilor argumentelor. Numele argumentelor primite trebuie sa difere ntre ele si nu trebuie sa coincida cu numele nici uneia din variabilele locale ale metodei. Pot nsa sa coincida cu numele variabilelor membre ale clasei caz n care diferentierea se va face prin intermediul variabile this.

class Cerc {

int x, y, raza;

public Cerc(int x, int y, int raza) {

this.x = x;

this.y = y;

this.raza = raza;

}

}

Atentie: In Java argumentele sunt trimise doar prin valoare (pass-by-value)

Acest lucru nsemna ca metoda receptioneaza doar valorile variabilelor primite ca parametri. Cnd argumentul are tip primitiv de date metoda nu-i poate schimba valoarea dect local (n cadrul metodei); la revenirea din metoda variabila are aceeasi valoare ca la apelul inittial al metodei (modificarile facute n cadrul metodei sunt pierdute). Cnd argumentul este de tip referinta metoda nu poate schimba referinta obiectului nsa poate apela metodele acelui obiect si poate modifica orice variabila membra accesibila. Asadar, daca dorim ca o metoda sa schimbe starea(valoarea) unui argument primit, atunci el trebuie sa fie neaparat de tip referinta (trebuie sa fie un obiect!). De exemplu, sa consideram clasa Cerc descrisa anterior si dorim sa implementam o metoda care sa returneze parametrii cercului:

Varianta incorecta

...

int x = -1, y = -1, r = -1;

cerc.aflaParametri(x, y, r);

System.out.println("x = " + x + ", y = " + y + ", raza = " + r);

. . .

In acesct scop n clasa Cerc ar trebui sa avem o metoda de genul:

class Cerc {

int x, y, raza;

...

public void aflaParametri(int valx, int valy, int valr) {

//metoda nu functioneaza cum ar trebui!

valx = x;

valy = y;

valr = raza;

}

}

Aceasta metoda nsa nu va realiza lucrul propul ntruct ea primeste doar valorile variabilelor x, y si r adica (-1, -1, -1) si nu referinte la ele (adresele lor de memorie) astfel nct sa le poata modifica valoarea. In concluzie, metoda nu realizeaza nimic pentru ca nu poate schimba valorile unor variabile aflate n afara corpului ei.

Varianta corecta Definim o clasa suplimentara care descrie parametrii pe care dorim sa-i aflam:

class CParam {

public int x, y, raza;

}

class Cerc {

int x, y, raza;

public void aflaParametri(CParam param) {

param.x = x;

param.y = y;

param.raza = raza;

}

}

...

CParam p = new CParam();

cerc.aflaParametri(p);

System.out.println("x = " + p.x + ", y = " + p.y + ", raza = " + p.raza);

Suprancarcarea si supradefinirea metodelor

Sunt doua concepte extrem de utile ale POO si se refera la:

suprancarcarea (overloading) : n cadrul unei clase pot exista metode cu acelasi nume cu conditia ca signaturile lor sa fie diferite (lista de argumente primite sa difere fie prin numarul argumentelor, fie prin tipul lor) astfel nct la apelul functiei cu acel nume sa se poata stabili n mod unic care dintre ele se executa. Fenomenul de supradefinire a metodelor se mai numeste si polimorfism.

supradefinirea (overriding): o subclasa a unei clase poate rescrie o metoda a clasei parinte, prin implementarea unei metode cu acelasi nume si aceeasi signatura ca ale superclasei.

Exemplificare:

class A {

void metoda() {

System.out.println("A: metoda fara parametru");

}

//supraincarcare - polimorfism

void metoda(int arg) {

System.out.println("A: metoda cu un parametru");

}

}

class B extends A {

//supradefinire

void metoda() {

System.out.println("B: metoda fara parametru");

}

}

O metoda supradefinita poate sa:

ignore complet codul metodei corespunzatoare din superclasa (cazul de mai sus)

B b = new B();

b.metoda();-> afiseaza "B: metoda fara parametru"

sa extinda codul metodei parinte, executnd nainte de codul propriu si functia parinte.

class B extends A {

//supradefinire prin extensie

void metoda() {

super.metoda();

System.out.println("B: metoda fara parametru");

}

}

. . .

B b = new B();

b.metoda();-> afiseaza :

"A: metoda fara parametru"

"B: metoda fara parametru"

Metode finale

Specifica faptul ca acea metoda nu mai poate fi supradefinita n subclasele clasei n care ea este definita ca fiind finala. Acest lucru este util daca respectiva metoda are o implementare care nu trebuie schimbata sub nici o forma n subclasele ei, fiind critica pentru consistenta starii unui obiect. De exemplu studentilor unei universitati trebuie sa li se calculeze media finala n functie de notele obtinute la examene n aceeasi maniera, indiferent de facultatea la care sunt.

class Student {

. . .

final float calcMedie(int nrExamene, float note[], float ponderi[]) {

...

}

. . .

}

class StudentInformatica extends Student{

float calcMedie(int nrExamene, float note[], float ponderi[]) {

return 10.00;

}

}//eroare la compilare!

Specificatori de acces pentru membrii unei clase

Sunt cuvinte rezervate ce controleaza accesul celorlate clase la membrii unei clasei. Specificatorii de acces pentru variabilele si metodele unei clase sunt: public, protected, private si cel implicit (package), iar nivelul lor de acces este dat n tabelul de mai jos:

SpecificatorClasaSubclasaPachetToti

private X

protected X X** X

public X X X X

package* X X

Exemple de declaratii:

private int secretPersonal;

proteceted String secretDeFamilie;

public Vector elemente;

long doarIntrePrieteni ;-> package

private void metodaInterna();

public double calculeazaRezultat();

Obs1(*): Daca nu este specificat nici un modificator de acces, implicit nivelul de acces este la nivelul pachetului (package). Asadar, modificatorul "package" nu apare explicit n declararea unei variabile/metode (n cazul n care apare, compilatorul va furniza o eroare). Obs2(**): In cazul n care declaram un membru "protected" atunci accesul la acel membru din subclasele clasei n care a fost declarata variabila depinde si de pachetul n care se gaseste subclasa: daca sunt n acelasi pachet accesul este permis, daca nu sunt n acelasi pachet accesul nu este permis dect pentru obiecte de tipul subclasei.

Membri de instanta si membri de clasa

O clasa Java poate contine doua tipuri de variabile si metode :

de instanta: declarate fara modificatorul static, specifice fiecarei instante si

de clasa: declarate cu modificatorul static, specifice clasei

Variabile

Cnd declarati o variabila membra cum ar fi x n exemplul de mai jos:

class MyClass {

int x ; //variabila de instanta

}

se declara de fapt o variabila de instanta, cee ce nsemna ca la fiecare creare a unei instante a clasei MyClass sistemul aloca o zona de memorie separata pentru memorarea valorii lui x.

MyClass o1 = new MyClass();

o1.x = 100;

MyClass o2 = new MyClass();

o2.x = 200;

System.out.println(o1.x) -> afiseaza 100

System.out.println(o2.x) -> afiseaza 200

Asadar, fiecare obiect nou creat va putea memora valori diferite pentru variabilele sale de instanta. Pentru variabilele de clasa (statice) sistemul aloca o singura zona de memorie la care au acces toate instantele clasei respective, ceea ce nseamna ca daca un obiect modifica valoarea unei variabile statice ea se va modifica si pentru toate celelate obiecte.

class MyClass {

int x ;//variabila de instanta

static long n;//variabila de clasa

}

. . .

MyClass o1 = new MyClass();

MyClass o2 = new MyClass();

o1.n = 100;

System.out.println(o2.n) -> afiseaza 100

o2.n = 200;

System.out.println(o1.n) -> afiseaza 200

Metode

Similar ca la variabile, metodele declarate fara modificatorul static sunt metode de instanta iar cele declarate cu static sunt metode de clasa (statice). Diferenta ntre cele doua metode este urmatoarea:

metodele de instanta opereaza att pe variabilele de instanta ct si pe cele statice ale clasei

metodele de clasa opereaza doar pe variabilele statice ale clasei

class MyClass {

int x ;//variabila de instanta

static long n;//variabila de clasa

void metodaDeInstanta() {

n ++;

//legal

x --;

//legal

static void metodaStatica() {

n ++;

//legal

x --;

//ilegal }

Intruct metodele de clasa nu depind de starea obiectelor clasei respective, apelul lor se poate face astfel:

MyClass.metodaStatica();//legal, echivalent cu

MyClass obj = new MyClass();

obj.metodaStatica();

//legal

spre deosebire de metodele de instanta care nu pot fi apelate dect unei instante a clasei respective:

MyClass.metodaDeInstanta(),

//ilegal

MyClass obj = new MyClass();

obj.metodaDeInstanta();

//legal

Utilitatea membrilor de clasa (statici)

Sunt folositi pentru a pune la dispozitie valori si metode independente de starea obiectelor dintr-o anumita clasa.

1. Declararea constantelor

2. 2. class MyClass {

3. 3.

final double PI = 3.14; //variabila finala de instanta

4. 4. }

La fiecare instantiere a clasei MyClass va fi rezervata zona de memorie pentru variabilele finale ale obiectului respectiv, ceea ce este o risipa ntruct aceste constante au aceleasi valori pentru toate instantele clasei. Declararea corecta a constantelor trebuie asadar facuta cu modificatorii static si final, pentru a le rezerva o singura zona de memorie, comuna tuturor obiectelor:

class MyClass {

static final double PI = 3.14; //variabila finala de clasa

}

5. Numararea obiectelor unei clase

6. 6. class MyClass {

7. 7.

static long nrInstante = 0;

8. 8.

MyClass() {//constructorul este apelat la fiecare instantiere

9. 9.

nrInstante ++;

10. 10.

}

11. 11. }

Folosind variabile statice putem controla diversi parametri legati de crearea obiectelor unei clase

12. Implementarea functiilor globale Spre deosebire de C++, n Java nu putem avea functii globale definite ca atare, ntruct "orice este un obiect". Din acest motiv si metodele care au o functionalitate globala trebuie implementate n cadrul unei clase. Acest lucru se va face prin intermediul metodelor de clasa (globale), deoarece acestea nu depind de starea particulara a obiectelor din clasa respectiva. De exemplu, sa consideram functia globala sqrt care extrage radicalul unui numar si care se gaseste n clasa Math. Daca nu ar fi fost functie de clasa, apelul ei ar fi trebuit facut astfel (incorect, de altfel):

13. 13. Math obj = new Math();

14. 14. double rad121 = obj.sqrt(121);

ceea ce ar fi fost extrem de neplacut pentru programatori. Fiind nsa functie statica ea poate fi apelata prin: Math.sqrt(121) .

Initializarea variabilelor de clasa

Se poate face n momentul declararii lor :

class MyClass {

static final double PI = 3.14;

static long nrInstante = 0;

static final double EPS = 0.01;

}

sau prin intermediul unui bloc static de initializare:

class MyClass {

static {

final double PI = 3.14;

long nrInstante = 0;

final double EPS = 0.01;

}

}

Clase imbricate

O clasa imbricata este, prin definitie, o clasa membra a unei alte clase

class ClasaDeAcoperire{

. . .

class ClasaImbricata {

. . .

}

}

Folosirea claselor imbricate se face atunci cnd o alta clasa are nevoie n implementarea ei de o alta clasa si nu exista nici un motiv pentru care clasa imbricata sa fie declarata de sine statatoare (nu mai este folosita nicaieri).

public class Pachet {//clasa de acoperire

class Continut {//clasa imbricata

private String marfa;

private float cantitate;

Continut (String marfa, float cantitate) {

this.marfa = marfa;

this.cantitate = cantitate;

}

}

class Destinatie {//clasa imbricata

private String dest;

private int termen;

Destinatie(String undePleaca, int inCateZile) {

dest = undePleaca;

termen = inCateZile;

}

}

public void trimite(String marfa, float cant, String dest,

int termen) {

Continut c = new Continut(marfa, cant);

Destinatie d = new Destinatie(dest, termen);

}

public static void main(String[] args) {

Pachet p = new Pachet();

p.trimite("banane", 100, "Romania", 7);

}

}

Ca membra a unei clase, o clasa imbricata are un privilegiu special fata de celelalte clase: acces nelimitat la variabilele clasei de acoperire, chiar daca acestea sunt private.

Clase interne

Ca orice alta clasa o clasa imbricata poate fi declarata statica sau nu. O clasa imbricata nestatica se numeste clasa interna.

class ClasaDeAcoperire{

. . .

static class ClasaImbricataStatica {

. . .

}

class ClasaInterna {

. . .

}

}

Diferentierea acestor denumiri se face deoarece:

o "clasa imbricata" reflecta relatia sintactica a doua clase; codul unei clase apare n interiorul dodului altei clase;

o "clasa interna" reflecta relatia dintre instantele a doua clase, n sensul ca o instanta a unei clase interne nu poate exista dect n cadrul unei instante a clasei de acoperire.

In general cele mai folosite clase imbricate sunt clasele interne Asadar, o clasa interna este o clasa imbricata ale carei instante nu pot exista dect n cadrul instantelor clasei de acoperire si care are acces direct la toti membrii clasei sale de acoperire.

Identificarea claselor imbricate

Dupa cum stim orice clasa produce la compilare asa numitele "unitati de compilare", care sunt fisiere avnd numele clasei respective si extensia .class, si care contin toate informatiile despre clasa respectiva. Pentru clasele imbricate aceste unitati de compilare sunt denumite astfel: numele clasei de acoperire, urmat de simbolul '$' apoi de numele clasei imbricate.

class ClasaDeAcoperire{

class ClasaInterna1 {}

class ClasaInterna2 {}

}

Pentru exemplul de mai sus vor fi generate trei fisiere:

ClasaDeAcoperire.class

ClasaDeAcoperire$ClasaInterna1.class

ClasaDeAcoperire$ClasaInterna2.class

In cazul n care clasele imbricate au la rndul lor alte clase imbricate (situatie mai putin uzuala) denumirea lor se face dupa aceeasi metoda : adaugarea unui '$' si apoi numele clasei imbricate.

Clase si metode abstracte

Uneori n proiectarea unei aplicatii este necesar sa reprezentam cu ajutorul claselor concepte abstracte care sa nu poata fi instantiate si care sa foloseasca doar la dezvoltarea ulterioara a unor clase ce descriu obiecte concrete. De exemplu n pachetul java.lang exista clasa abstracta Number care modeleaza conceptul generic de "numar". Intr-un program nu avem nsa nevoie de numere generice ci de numere ntregi, reale, etc. Clasa Number serveste ca superclasa pentru clase cum ar fi Byte, Double, Float, Integer, Long si Short ce implementeaza obiecte pentru descrierea numerelor de un anumit tip. Asadar clasa Number reprezinta un concept abstract si nu vom putea instantia obiecte de acest tip:

Number numarGeneric = new Number(); //eroare

Declararea unei clase abstracte

Se face folosind cuvntul rezervat abstract n declaratia clasei:

abstract class ClasaAbstracta {

. . .

}

Daca vom ncerca sa instantiem un obiect al clasei ClasaAbstracta vom obtine o eroare la compilarea programului de genul:class ClasaAbstracta is an abstract class. It can't be instantiated.

Metode abstracte

Spre deosebire de clasele obisnuite care trebuie sa furnizeze implementari pentru toate metodele declarate o clasa abstracta poate contine metode fara nici o implementare. Metodele fara nici o implementare se numesc metode abstracte si pot aparea doar n clase abstracte. In fata unei metode abstracte trebuie sa apara cuvntul cheie abstract, altfel va fi furnizata o eroare de compilare.

abstract void metodaAbstracta();//corect

void metoda();//incorect

(Method metoda requires a method body; otherwisw declare it abstract)

In felul acesta o clasa abstracta poate pune la dispozitia subclaselor sale un model complet pe care trebuie sa-l implementeze, furniznd chiar implementarea unor metode comune tuturor claselor sau lasnd explicitarea unor metode fiecarei subclase n parte. Un exemplu elocvent de folosire a claselor si metodelor abstracte este descrierea obiectelor grafice ntr-o maniera orientata-obiect.

Obiecte grafice : linii, dreptunghiuri, cercuri, curbe Bezier, etc

Stari comune : pozitia(originea), dimensiunea, culoarea, etc

Comportament : mutare, redimensionare, desenare, colorare, etc.

Pentru a folosi starile si comportamentele comune acestor obiecte n avantajul nostru putem declara o clasa generica GraphicObject care sa fie superclasa pentru celelalte clase.

Metodele abstracte vor fi folosite pentru implementarea comportamentului specific fiecarui obiect, cum ar fi desenarea iar cele obisnuite pentru comportamentul comun tuturor, cum ar fi schimbarea originii. Implementarea clasei abstracte GraphicObject:

abstract class GraphicObject {

int x, y;

. . .

void moveTo(int newX, int newY) {//metoda normala

. . .

}

abstract void draw();

//metoda abstracta

}

//nici o implementare

Atentie: Orice subclasa non-abstracta a unei clase abstracte trebui sa furnizeze implementari ale metodelor abstracte din superclasa. Implementarea claselor pentru obiecte grafice ar fi:

class Circle extends GraphicObject {

void draw() {

. . .//obligatoriu implementarea

}

}

class Rectangle extends GraphicObject {

void draw() {

. . .//obligatoriu implementarea

}

}

Observatii: O clasa abstracta poate sa nu aiba nici o metoda abstracta. O metoda abstracta nu poate aparea dect ntr-o clasa abstracta. Orice clasa care are o metoda abstracta trebuie declarata ca fiind abstracta.

Clasa Object

Orice clasa are o superclasa

Dupa cum am vazut la crearea unei clase clauza "extends" specifica faptul ca acea clasa este o subclasa a altei clase, numita superclasa. O clasa poate avea o singura superclasa (Java nu suporta mostenirea multipla) si chiar daca nu specificati clauza "extends" la crearea unei clase ea totusi va avea o superclasa. Cu alte cuvinte, in Java orice clasa are o superclasa si numai una.

Superclasa tuturor celorlalte clase este clasa Object, care este radacina ierarhiei de clase n Java. Clasa Object este si superclasa implicita a claselor care nu specifica o alta superclasa. Declaratia class MyClass {} este echivalenta cu class MyClass extends Object {}.

Clasa Object

Este cea mai generala dintre clase, orice obiect fiind, direct sau indirect, descendent al acestei clase. Defineste si implementeaza comportamentul comun al tuturor claselor Java cum ar fi:

posibilitatea compararii obiectelor ntre ele

specificarea unei reprezentari ca sir de caractere a unui obiect

returnarea clasei din care face parte un obiect

notificarea altor obiecte ca o variabila de conditie s-a schimbat, etc

Fiind sublcasa a lui Object, orice clasa poate supradefini metodele clasei Object care nu sunt finale. Metode care pot fi supradefinite sunt: clone, equals/hashCode, finalize ,toString clone Acesasta metoda este folosita pentru duplicarea obiectelor (creearea unor clone). Clonarea unui obiect presupune crearea unui nou obiect de acelasi tip si care sa aiba aceeassi stare (aceleasi valori pentru variabilele sale)

equals, hashCode Aceste metode trebuie supradefinite mpreuna. In metoda equals este scris codul pentru compararea a doua obiecte. Implicit (implementarea din clasa Object) aceasta metoda compara referintele obiectelor. Uzual este redefinita pentru a testa daca starile obiectelor coincid sau daca doar o parte din variabilele lor coincid. Metoda hashCode returneaza un cod ntreg pentru fiecare obiect pentru a testa consistenta obiectelor: acelasi obiect trebuie sa returneze acelasi cod pe durata executiei programului. Daca doua obiecte sunt egale conform metodei equals atunci apelul metodei hashCode pentru fiecare din cele doua obiecte trebuie sa returneze acelasi ntreg.

finalize In aceasta metoda se scrie codul care "curata dupa un obiect" nainte de a fi eliminat din memorie de colectorul de gunoaie.(vezi "Distrugerea obiectelor")

toString Este folosita pentru a returna o reprezentare ca sir de caractere a unui obiect. Este utila pentru concatenarea sirurilor cu diverse obiecte n vederea tiparirii.

MyObject obj = new MyObject();

System.ot.println("Obiect=" + obj); //echivalent cu

System.ot.println("Obiect=" + obj.toString());

Exemplu de supradefinire a metodelor clasei Objectclass MyObject extends Object {

//clasa in care este supradefinita metoda equals

public int x;

public boolean equals(Object obj) {

if (obj == null) return false;

if (!(obj instanceof MyObject)) return false;

if ( ((MyObject) obj).x == x)

return true;

else

return false;

}

public synchronized Object clone() {

MyObject m = new MyObject();

m.x = x;

return m;

}

public final synchronized String toString() {

return "[" + x + "]";

}

public void finalize() {

System.out.println("Finalizare");

x = -1;

}

}

class OtherObject {

public int x;

//clasa in care NU este supradefinita metoda equals

}

class TestObject {//clasa principala - contine metoda main

public static void main(String args[]) {

OtherObject o1 = new OtherObject();

OtherObject o2 = new OtherObject();

o1.x = 1;

o2.x = 1;

if (o1.equals(o2))

System.out.println("test1: o1 == o2");

else

System.out.println("test1: o1 != o2");//corect

//Desi x=1 pt ambele obiecte egalitatea se obtine doar cand

//adresele lor de memorie coincid deoarece nu este implementata

//metoda equals

o2 = o1;

if (o1.equals(o2))

System.out.println("test2: o1 == o2");//corect

else

System.out.println("test2: o1 != o2");

//////////////////////////////////////////////

MyObject m1 = new MyObject();

MyObject m2 = new MyObject();

m1.x = 1;

m2.x = 1;

if (m1.equals(m2))

System.out.println("test3: m1 == m2");//corect

else

System.out.println("test3: m1 != m2");

//x=1 pt ambele obiecte -> metoda equals returneaza true

MyObject m3;

m3 = (MyObject) m1.clone();

System.out.println("Obiectul clonat: " + m3); //echivalent cu

System.out.println("Obiectul clonat: " + m3.toString());

//Se tipareste: Obiectul clonat: [1]

}

}

Curs 3 Exceptii

Ce sunt exceptiile ?

Avantajele exceptiilor

"Prinderea" si tratarea exceptiilor (Instructiunile try-catch-finally)

"Aruncarea" exceptiilor (Clauza throws, Instructiunea throw)

Ierarhia claselor ce descriu exceptii (Clasa Throwable)

Exceptii la executie

Crearea propriilor exceptii

TopCe sunt exceptiile?

Termenul exceptie este o prescurtare pentru "eveniment exceptional" si poate fi definit astfel:

Definitie

O exceptie este un eveniment ce se produce n timpul executiei unui program si care provoaca ntreruperea cursului normal al executiei.

Exceptiile pot aparea din diverse cauze si pot avea nivele diferite de gravitate: de la erori fatale cauzate de echipamentul hardware pna la erori ce tin strict de codul programului, cum ar fi accesarea unui element din afara spatiului alocat unui vector. In momentul cnd o asemenea eroare se produce n timpul executiei sistemul genereaza automat un obiect de tip exceptie ce contine:

informatii despre exceptia respectiva

starea programului n momentul producerii acelei exceptii

public class Exceptii {

public static void main(String argsst) {

int v[] = new int[10];

v[10] = 0;//exceptie, vectorul are elementele v[0]...v[9]

System.out.println("Aici nu se mai ajunge...");

}

}

La rularea programului va fi generata o exceptie si se va afisa mesajul :

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException :10

at Exceptii.main (Exceptii.java:4)

Crearea unui obiect de tip exceptie se numeste aruncarea unei exceptii ("throwing an exception"). In momentul n care o metoda genereaza o exceptie (arunca o exceptie) sistemul de executie este responsabil cu gasirea unei secvente de cod dintr-o metoda care sa trateze acea exceptie. Cautarea se face recursiv, ncepnd cu metoda care a generat exceptia si mergnd napoi pe linia apelurilor catre acea metoda. Secventa de cod dintr-o metoda care trateaza o anumita exceptie se numeste analizor de exceptie ("exception handler") iar interceptarea si tratarea exceptie se numeste prinderea exceptiei ("catch the exception"). Cu alte cuvinte la aparitia unei erori este "aruncata" o exceptie iar cineva trebuie sa o "prinda" pentru a o trata. Daca sistemul nu gaseste nici un analizor pentru o anumita exceptie atunci programul Java se opreste cu un mesaj de eroare (n cazul exemplului de mai sus mesajul "Aici nu se mai ajunge..." nu va fi tiparit).

Atentie: In Java tratarea erorilor nu mai este o optiune ci o constrngere. Orice cod care poate provoca exceptii trebui sa specfice modalitatea de tratare a acestora.

Avantajele exceptiilor

Prin modalitatea sa de tratare a exceptiilor Java are urmatoarele avantaje fata de mecanismul traditional de tratare a erorilor:

1. Separarea codului pentru tratarea unei erori de codul n care ea poate sa apara

2. Propagarea unei erori pna la un analizor de exceptii corespunzator

3. Gruparea erorilor dupa tipul lor

Separarea codului pentru tratarea unei erori de codul n care ea poate sa apara

In programarea traditionala tratarea erorilor se combina cu codul ce poate produce aparitia lor conducnd la ada numitul "cod spaghetti". Sa consideram urmatorul exemplu: o functie care ncarca un fisier n memorie:

citesteFisier {

deschide fisierul;

determina dimensiunea fisierului;

aloca memorie;

citeste fisierul in memorie;

inchide fisierul;

}

Problemele care pot aparea la aceasta functie, aparent simpla sunt de genul: "Ce se ntmpla daca: ... ?"

fisierul nu poate fi deschis

nu se poate determina dimensiunea fisierului

nu poate fi alocata suficienta memorie

nu se poate face citirea din fisier

fisierul nu poate fi nchis

Un cod traditional care sa trateze aceste erori ar arata astfel:

int citesteFisier {

int codEroare = 0;

deschide fisier;

if (fisierul s-a deschis) {

determina dimensiunea fisierului;

if (s-a determinat dimensiunea) {

aloca memorie;

if (s-a alocat memorie) {

citeste fisierul in memorie;

if (nu se poate citi din fisier) {

codEroare = -1;

}

} else {

codEroare = -2;

}

} else {

codEroare = -3;

}

inchide fisierul;

if (fisierul nu s-a inchis && codEroare == 0) {

codEroare = -4;

} else {

codEroare = codEroare & -4;

}

} else {

codEroare = -5;

}

return codEroare;

}//cod "spaghetti"

Acest stil de progamare este extrem de susceptibil la erori si ngreuneaza extrem de mult ntelegerea sa. In Java, folosind mecansimul exceptiilor, codul ar arata astfel:

int citesteFisier {

try {

deschide fisierul;

determina dimensiunea fisierului;

aloca memorie;

citeste fisierul in memorie;

inchide fisierul;

}

catch (fisierul nu s-a deschis) {trateaza eroarea;}

catch (nu s-a determinat dimensiunea) {trateaza eroarea;}

catch (nu s-a alocat memorie) {trateaza eroarea}

catch (nu se poate citi dun fisier) {trateaza eroarea;}

catch (nu se poate inchide fisierul) {trateaza eroarea;}

}

Propagarea unei erori pna la un analizor de exceptii corespunzator

Sa presupunem ca apelul la metoda citesteFisier este consecinta unor apeluri imbricate de metode:

int metoda1 {

apel metoda2;

. . .

}

int metoda2 {

apel metoda3;

. . .

}

int metoda3 {

apel citesteFisier;

. . .

}

Sa presupunem de asemenea ca dorim sa facem tratarea erorilor doar n metoda1. Traditional, acest lucru ar trebui facut prin propagarea erorii ntoarse de metoda citesteFisier pna la metoda1.

int metoda1 {

int codEroare = apel metoda2;

if (codEroare != 0)

proceseazaEroare;

. . .

}

int metoda2 {

int codEroare = apel metoda3;

if (codEroare != 0)

return codEroare;

. . .

}

int metoda3 {

int codEroare = apel citesteFisier;

if (codEroare != 0)

return codEroare;

. . .

}

Java permite unei metode sa arunce exceptiile aparute n cadrul ei la un nivel superior, adica functiilor care o apeleaza sau sistemului. Cu alte cuvinte o metoda poate sa nu si asume responsabilitatea tratarii exceptiilor aparute n cadrul ei:

metoda1 {

try {

apel metoda2;

}

catch (exceptie) {

proceseazaEroare;

}

. . .

}

metoda2 throws exceptie{

apel metoda3;

. . .

}

metoda3 throws exceptie{

apel citesteFisier;

. . .

}

Gruparea erorilor dupa tipul lor

In Java exista clase corespunzatoare tuturor exceptiilor care pot aparea la executia unui program. Acestea sunt grupate n functie de similaritatile lor ntr-o ierarhie de clase. De exemplu, clasa IOException se ocupa cu exceptiile ce pot aparea la operatii de intrare/iesire si diferentiaza la rndul ei alte tipuri de exceptii, cum ar fi FileNotFoundException, EOFException, etc. La rndul ei clasa IOException se ncadreaza ntr-o categorie mai larga de exceptii si anume clasa Exception. Radacina acestei ierarhii este clasa Throwable (vezi "Ierarhia claselor ce descriu exceptii"). Interceptarea unei exceptii se poate face fie la nivelul clasei specifice pentru acea exceptie fie la nivelul uneia din superclasele sale, n functie de necesitatile programului:

try {

FileReader f = new FileReader("input.dat");

//acest apel poate genera exceptie de tipul FileNotFoundException

//tratarea ei poate fi facuta in unul din modurile de mai jos

}

catch (FileNotFoundException e) {

//exceptie specifica provocata de absenta fisierului 'input.dat'

} //sau

catch (IOException e) {

//exceptie generica provocata de o operatie de intrare/iesire

} //sau

catch (Exception e) {

//cea mai generica exceptie - NERECOMANDATA!

}

"Prinderea" si tratarea exceptiilor

Tratarea exceptiilor se realizeaza prin intermediul blocurilor de instructiuni try, catch si finally. O secventa de cod care trateaza anumite exceptii trebuie sa arate astfel:

try {

Instructiuni care pot genera o exceptie

}

catch (TipExceptie1 ) {

Prelucrarea exceptiei de tipul 1

}

catch (TipExceptie2 ) {

Prelucrarea exceptiei de tipul 2

}

. . .

finally {

Cod care se executa indiferent daca apar sau nuexceptii

}

Sa consideram urmatorul exemplu : citirea unui fisier si afisarea lui pe ecran. Fara a folosi tratarea exceptiilor codul programului ar arata astfel:

//ERONAT!import java.io.*;

public class CitireFisier {

public static void citesteFisier() {

FileInputStream sursa = null; //s este flux de intrare

int octet;

sursa = new FileInputStream("fisier.txt");

octet = 0;

//citesc fisierul caracter cu caracter

while (octet != -1) {

octet = sursa.read();

System.out.print((char)octet);

}

sursa.close();

}

public static void main(String args[]) {

citesteFisier();

}

}

Acest cod va furniza erori la compilare deoarece n Java tratarea erorilor este obligatorie. Folosind mecanismul exceptiilor metoda citesteFisier si poate trata singura erorile pe care le poate provoca:

//CORECTimport java.io.*;

public class CitireFisier {

public static void citesteFisier() {

FileInputStream sursa = null; //s este flux de intrare

int octet;

try {

sursa = new FileInputStream("fisier.txt");

octet = 0;

//citesc fisierul caracter cu caracter

while (octet != -1) {

octet = sursa.read();

System.out.print((char)octet);

}

catch (FileNotFoundException e) {

System.out.println("Fisierul nu a fost gasit !");

System.out.println("Exceptie: " + e.getMessage());

System.exit(1);

}

catch (IOException e) {

System.out.println("Eroare de intrare/iesire");

System.out.println("Exceptie: " + e.getMessage());

System.exit(2);

}

finally {

if (sursa != null) {

System.out.println("Inchidem fisierul...");

try {

sursa.close();

}

catch (IOException e) {

System.out.println("Fisierul poate fi inchis!");

System.out.println("Exceptie: " + e.getMessage());

System.exit(3);

}

}

}

}

public static void main(String args[]) {

citesteFisier();

}

}

Blocul "try" contine instructiunile de deschidere a unui fisier si de citire dintr-un fisier ambele putnd produce exceptii. Exceptiile provocate de aceste instructiuni sunt tratate n cele doua blocuri "catch", cte unul pentru fiecare tip de exceptie. Inchiderea fisierului se face n blocul "finally", deoarece acesta este sigur ca se va executa. Fara a folosi blocul "finally" nchiderea fisierului ar fi trebuit facuta n fiecare situatie n care fisierul ar fi fost deschis, ceea ce ar fi dus la scrierea de cod redundant:

try {

. . .

sursa.close();

}

. . .

catch (IOException e) {

. . .

sursa.close(); //cod redundant

}

Atentie: Obligatoriu un bloc de instructiuni "try" trebuie sa fie urmat de unul sau mai multe blocuri "catch", n functie de exceptiile provocate de acele instructiuni sau (optional) de un bloc "finally"

"Aruncarea" exceptiilor In cazul n care o metoda nu si asuma responsabilitatea tratarii uneia sau mai multor exceptii pe care le pot provoca anumite instructiuni din codul sau atunci ea poate sa "arunce" aceste exceptii catre metodele care o apeleaza, urmnd ca acestea sa implementeze tratarea lor sau, la rndul lor, sa "arunce" mai departe exceptiile respective. Acet lucru se realizeaza prin specificarea n declaratia metodei a clauzei throws:

metoda throws TipExceptie1, TipExceptie2, ... {

. . .

}

Atentie: O metoda care nu trateaza o anumita exceptie trebuie obligatoriu sa o "arunce". In exemplul de mai sus daca nu facem tratarea exceptiilor n cadrul metodei citesteFisier atunci metoda apelanta (main) va trebui sa faca acest lucru:

import java.io.*;public class CitireFisier {

public static void citesteFisier() throws FileNotFoundException, IOException {

FileInputStream sursa = null; //s este flux de intrare

int octet;

sursa = new FileInputStream("fisier.txt");

octet = 0;

//citesc fisierul caracter cu caracter

while (octet != -1) {

octet = sursa.read();

System.out.print((char)octet);

}

sursa.close();

}

public static void main(String args[]) {

try {

citesteFisier();

}

catch (FileNotFoundException e) {

System.out.println("Fisierul nu a fost gasit !");

System.out.println("Exceptie: " + e.getMessage());

System.exit(1);

}

catch (IOException e) {

System.out.println("Eroare de intrare/iesire");

System.out.println("Exceptie: " + e.getMessage());

System.exit(2);

}

}

}

Observati ca, n acest caz, nu mai putem diferentia exceptiile provocate de citirea din fisier si de nchiderea fisierului ambele fiind de tipul IOException. Aruncarea unei exceptii se poate face si implicit prin instructiunea throw ce are formatul: throw obiect_de_tip_Exceptie .

Exemple:

throw new IOException();

if (index >= vector.length)

throw new ArrayIndexOutOfBoundsException();

catch(Exception e) {

System.out.println("A aparut o exceptie);

throw e;

}

Aceasta instructune este folosita mai ales la aruncarea exceptiilor proprii care, evident, nu sunt detectate de catre mediul de executie. (vezi "Crearea propriilor exceptii")

Ierarhia claselor ce descriu exceptii Radacina claselor ce descriu exceptii este clasa Thowable iar cele mai importante subclase ale sale sunt Error, Exception si RuntimeException, care sunt la rndul lor superclase pentru o serie ntreaga de tipuri de exceptii.

Clasa Error Erorile (obiecte de tip Error) sunt cazuri speciale de exceptii generate de functionarea anormala a echipamentului hard pe care ruleaza un program Java si sunt invizibile programatorilor. Un program Java nu trebuie sa trateze aparitia acestor erori si este improbabil ca o metoda Java sa provoace asemenea erori. Clasa Exception Obiectele de acest tip sunt exceptiile standard care trebuie tratate de catre programele Java. In Java, tratarea exceptiilor nu este o optiune ci o constrngere. Exceptiile care pot "scapa" netratate sunt ncadrate n subclasa RuntimeException si se numesc exceptii la executie.

In general metodele care pot fi apelate pentru un obiect exceptie sunt definite n clasa Throwable si sunt publice, astfel nct pot fi apelate pentru orice tip de exceptie. Cele mai uzuale sunt:

String getMessage( ) tipareste detaliul unei exceptii

void printStackTrace( ) tipareste informatii despre localizarea exceptiei

String toString( ) metoda din clasa Object, da reprezentarea ca sir de caractere a exceptiei

Exceptii la executie (RuntimeException)In general tratarea exceptiilor este obligatorie n Java. De la acest principu se sustrag nsa asa numitele exceptii la executie sau, cu alte cuvinte, exceptiile care pot proveni strict din vina programatorului si nu generate de o cauza externa. Aceste exceptii au o superclasa comuna si anume RuntimeException si n acesata categorie sunt incluse:

operatii aritmetice (mpartire la zero) accesarea membrilor unui obiect ce are valoarea null

operatii cu elementele unui vector (accesare unui index din afara domeniului,etc)

Aceste exceptii pot aparea uriunde n program si pot fi extrem de numeroare iar ncercarea de "prindere" a lor ar fi extrem de anevoioasa. Din acest motiv compilatorul permite ca aceste exceptii sa ramna netratate, tratarea lor nefiind nsa ilegala.

int v[] = new int[10];

try {

v[10] = 0;

} catch (ArrayIndexOutOfBoundsException e) {

System.out.println("Atentie la indecsi!");

e.printStackTrace();

}//legal

Crearea propriilor exceptii Adeseori poate aparea necesitatea crearii unor exceptii proprii pentru a pune n evidenta cazuri speciale de erori provocate de clasele unei librarii, cazuri care nu au fost prevazute n ierarhia exceptiilor standard Java. O exceptie proprie trebuie sa se ncadreze n ierarhia exceptiilor Java, cu alte cuvinte clasa care o implementeaza trebuie sa fie subclasa a unei clase deja existente n aceasta ierarhie, preferabil una apropiata ca semnificatie sau superclasa Exception.

class MyException extends Exception {

public MyException() {}

public MyException(String msg) {

super(msg);

//apeleaza constructorul superclasei Exception

}

}

Un exemplu de folosire a exceptiei nou create:

public class TestMyException {

public static void f() throws MyException {

System.out.println("Exceptie in f()");

throw new MyException();

}

public static void g() throws MyException {

System.out.println("Exceptie in g()");

throw new MyException("aruncata din g()");

}

public static void main(String[] args) {

try {

f();

} catch(MyException e) {e.printStackTrace();}

try {

g();

} catch(MyException e) {e.printStackTrace();}

}

}

Fraza cheie este extends Exception care specifica faptul ca noua clasa MyEception este subclasa a clasei Exception si deci implementeaza obiecte ce reprezinta exceptii. In general codul adaugat claselor pentru exceptii proprii este nesemnificativ: unul sau doi constructori care afiseaza un mesaj de eroare la iesirea standard. Rularea programului de mai sus va produce urmatorul rezultat:

Exceptie in f()MyException()

at TestMyException.f(TestMyException.java:12)

at TestMyException.main(TestMyException.java:20)

Exceptie in g()

MyException(): aruncata din g

at TestMyException.g(TestMyException.java:16)

at TestMyException.main(TestMyException.java:23)

Procesul de creare a unei noi exceptii poate fi dus mai departe prin adaugarea unor noi metode clasei ce descrie acea exceptie, nsa aceasta dezvoltare nu si are rostul n majoritatea cazurilor. In general, exceptiile proprii sunt descrise de clase foarte simple chiar fara nici un cod n ele, cum ar fi:

class SimpleException extends Exception { }Aceasta clasa se bazeaza pe constructorul implicit creat de compilator nsa nu are constructorul SimpleException(String), care n practica nici nu este prea des folosit.

Curs 3 Fluxuri (Intrari / Iesiri)

Ce sunt fluxurile ?

Clasificarea fluxurilor

Ierarhia claselor pentru lucrul cu fluxuri

Fluxuri de caractere

Fluxuri de octeti

Metode comune fluxurilor

Folosirea fluxurilor

Fluxuri pentru citirea/scrierea efectiva a datelor

Fluxuri pentru procesarea datelor

Crearea unui flux

Fluxuri pentru lucrul cu fisiere (fluxuri de tip "File")

Conectarea fluxurilor de caractere(fluxuri de tip "Pipe")

Citirea si scrierea cu zona tampon

Concatenarea fisierelor (clasa SequenceInputStream)

Fluxuri pentru filtrarea datelor

Clasele DataInputStream si DataOutputStream

Scrierea propriilor fluxuri de filtrare

Fluxuri standard de intrare/iesire (citirea de la tastatura/scrierea pe ecran)

Analiza lexicala pe fluxuri (clasa StreamTokenizer)

Alte clase pentru lucrul cu fisiere

Clasa RandomAccesFile (fisiere cu acces direct)

Clasa File

Interfata FilenameFilter (filtrarea fisierelor dintr-un director)

TopCe sunt fluxurile?

Adeseori programele necesita citirea unor informatii care se gasesc pe o sursa externa sau trimiterea unor informatii catre o destinatie externa. Informatia se poate gasi oriunde : ntr-un fisier pe disc, n retea, n memorie sau n alt program si poate fi de orice tip: date primitive, obiecte, imagini, sunete, etc. Pentru a aduce informatii dintr-un mediu extern, un progam Java trebui sa deschida un canal de comunicatie (flux) catre sursa informatiilor (fisier, memorie, socket,etc) si sa citeasca serial informatiile respective:

Similar, un program poate trimite informatii catre o destinatie externa deaschiznd un canal de comunicatie (flux) catre acea destinatie si scriind serial informatiile respective:

Indiferent de tipul informatiilor, citirea/scrierea informatiilor de pe/catre un mediu extern respecta urmatorii algoritmi:

CitireaScrierea

deschide canal comunicatie

while (mai sunt informatii) {

citeste informatie

}

inchide canal comunicati;deschide canal comunicatie

while (mai sunt informatii) {

scrie informatie

}

inchide canal comunicati;

Pentru a generaliza, att sursa externa a unor informatii ct si destinatia lor sunt vazute ca fiind niste procese care produc, respectiv consuma informatii:

Definitii:

Un flux este un canal de comunicatie unidirectional ntre doua procese.

Un proces care descrie o sursa externa de date se numeste proces producator.

Un proces care descrie o destinatie externa pentru date se numeste proces consumator.

Un flux care citeste date se numeste flux de intrare.

Un flux care scrie date se numeste flux de iesire.

Observatii:

Fluxurile sunt canale de comunicatie seriale pe 8 sau 16 biti.

Fluxurile sunt unidirectionale, de la producator la consumator

Fiecare flux are un singur proces producator si un singur proces consumator

Intre doua procese pot exista oricte fluxuri, orice proces putnd fi att producator si consumator n acelasi timp, dar pe fluxuri diferite

Consumatorul si producatorul nu comunica direct printr-o interfata de flux ci prin intermediul codului Java de tratare a fluxurilor

Clasele si intefetele standard pentru lucu cu fluxuri se gasesc n pachetul java.io. Deci orice program care necesita operatii de intrare/iesire trebuie sa contina instructiunea de import a pachetului java.io: import java.io.*;

Clasificarea fluxurilor

Exista trei tipuri de clasificare a fluxurilor:

1. Dupa "directia" canalului de comunicatie deschis fluxurile se mpart n:

fluxuri de intrare (pentru citirea datelor)

fluxuri de iesire (pentru scrierea datelor)

2. Dupa tipul de date pe care opereaza:

fluxuri de octeti (comunicare seriala se realizeaza pe 8 biti)

fluxuri de caractere (comunicare seriala se realizeaza pe 16 biti)

3. Dupa actiunea lor:

fluxuri primare de citire/scriere a datelor (se ocupa efectiv cu citirea/scrierea datelor)

fluxuri pentru procesarea datelor

Ierarhia claselor pentru lucrul cu fluxuri

Fluxuri de caractere

Clasele radacina pentru ierarhia claselor ce se ocupa cu fluxurile de caractere sunt Reader (pentru fluxuri de intrare) si Writer (pentru fluxuri de iesire). Acestea sunt superclase abstracte pentru clase ce implementeaza fluxuri specializate pentru citirea/scrierea datelor pe 16 biti. Ierarhia claselor pentru fluxuri de intrare pe caractere:

Ierarhia claselor pentru fluxuri de iesire pe caractere:

Au fost puse n evidenta (colorate cu gri) fluxurile care intra n categoria fluxurilor pentru procesarea datelor.

Fluxuri de octeti

Clasele radacina pentru ierarhia claselor ce se ocupa cu fluxurile de octeti sunt InputStream (pentru fluxuri de intrare) si OutputStream (pentru fluxuri de iesire). Acestea sunt superclase abstracte pentru clase ce implementeaza fluxuri specializate pentru citirea/scrierea datelor pe 8 biti. Ierarhia claselor pentru fluxuri de intrare pe octeti:

Ierarhia claselor pentru fluxuri de iesire pe octeti:

Au fost puse n evidenta (colorate cu gri) fluxurile care intra n categoria fluxurilor pentru procesarea datelor.

Atentie: Pentru majoritatea programelor scrierea si citirea datelor se vor face prin intermediul fluxurilor de caractere deoarece acestea permit manipularea caracterelor Unicode (16-biti), n timp ce fluxurile de octeti permit doar lucrul pe 8 biti - caractere ASCII.

Metode comune fluxurilor

Superclasele abstracte Reader si InputStream definesc metode similare pentru citirea datelor.

ReaderInputStream

int read()

int read(char buf[])

int read(char buf[], int offset,int length)int read()

int read(byte buf[])

int read(byte buf[], int offset,int length)

De asemenea ambele clase pun la dispozitie metode pentru marcarea unei locatii ntr-un flux, saltul peste un numar de pozitii, resetarea pozitiei curente, etc.

Superclasele abstracte Writer si OutputStream sunt de asemenea paralele, definind metode similare pentru scrierea datelor.

WriterOutputStream

int write()

int write(char buf[])

int write(char buf[], int offset,int length)int write()

int write(byte buf[])

int write(byte buf[], int offset,int length)

Inchiderea oricarui flux se realizeaza prin metoda close. In cazul n care aceasta nu este apelata explicit fluxul va fi automat nchis de catre colectorul de gunoaie atunci cnd nu va mai exista nici o referinta la el. Metodele referitoare la fluxuri pot genera exceptii de tipul IOException.

Folosirea fluxurilor

Asa cum am vazut fluxurile pot fi mpartite n functie de activitatea lor, n fluxuri care se ocupa efectiv cu citirea/scrierea datelor si fluxuri pentru procesarea datelor. In continuare vom vedea care sunt cele mai importante clase din cele doua categorii si la ce folosesc acestea:

Fluxuri pentru citirea/scrierea efectiva a datelor

Clasele ce descriu fluxuri pentru citirea/scrierea efectiva a datelor pot fi mpartite n functie de tipul sursei datelor astfel:

Tip sursa Fluxuri caractere Fluxuri octeti

Memorie CharArrayReader, CharArrayWriter ByteArrayInputStream, ByteArrayOutputStream

Aceste fluxuri folosesc pentru scrierea/citirea informatiilor n memorie si sunt create pe un vector existent deja. Cu alte cuvinte permit tratarea vectorilor ca sursa/destinatie pentru crearea unor fluxuri de intrare/iesire.

StringReader, StringWriter StringBufferInputStream

Permit tratarea sirurilor de caractere aflate n memorie ca sursa/destinatie pentru crearea unor fluxuri de intrare/iesire. StringReader si StringWriter sunt folosite cu obiecte de tip String iar StringBufferInputStream cu obiecte de tip StringBuffer.

Pipe PipedReader, PipedWriter PipedInputStream, PipedOutputStream

Implementeaza componentele de intrare/iesire ale unei conducte de date (pipe). Pipe-urile sunt folosite pentru a canaliza iesirea unui program sau fir de executie catre intrarea altui program sau fir de executie

Fisier FileReader, FileWriter FileInputStream, FileOutputStream

Numite si fluxuri fisier, acestea sunt folosite pentru citirea datelor dintr-un fisier, respectiv scrierea datelor ntr-un fisier (vezi "Fluxuri pentru lucrul cu fisiere").

Fluxuri pentru procesarea datelor

Clasele ce descriu fluxuri pentru procesarea datelor pot fi mpartite n functie de tipul de procesare pe care l efectueaza:

Tip procesare Fluxuri caractere Fluxuri octeti

"Bufferizare" BufferedReader,BufferedWriter BufferedInputStream,BufferedOutputStream

Sunt folosite pentru a introduce un buffer n procesul de scriere/citire a informatiilor, reducnd astfel numarul de accese la dispozitivul ce reprezinta sursa originala de date. Sunt mult mai eficiente dect fluxurile fara buffer si din acest motiv se recomanda folosirea lor ori de cte ori eset posibil (vezi "Citirea si scrierea cu zona tampon").

Filtrare FilterReader, FilterWriter FilterInputStream, FilterOutputStream

Sunt clase abstracte ce definesc o interfata pentru fluxuri care filtreaza automat datele citite sau scrise (vezi "Fluxuri pentru filtrare").

Conversie octeti-caractere InputStreamReader, OutputStreamWriter

Formeaza o punte de legatura ntre fluxurile de caractere si fluxurile de octeti. Un flux InputStreamReader citeste octeti dintr-un flux InputStream ai i converteate la caractere folosind codificarea standard a caracterelor sau o codificare specificata de program. Similar, un flux OutputStreamWriter converteste caractere n octeti si trimite rezutatul catre un flux de tipul OutputStream.

Concatenare SequenceInputStream

Concateneaza mai multe fluxuri de intrare ntr-unul singur (vezi "Concatenarea fisierelor").

Serializare ObjectInputStream, ObjectOutputStream

Folosite pentru serializarea obiectelor (vezi "Serializarea obiectelor").

Conversie tipuri de date DataInputStream, DataOutputStream

Folosite la scrierea/citirea datelor de tip primitiv ntr-un format independent de masina pe care se lucreaza (vezi "Folosirea claselor DataInputStream si DataOutputStream").

Numarare LineNumberReader LineNumberInputStream

Numara liniile citite de la un flux de intrare.

Citire n avans PushbackReader PushbackInputStream

Fluxuri de intrare care au un buffer de 1-caracter(octet) n care este citit n avans si caracterul (octetul) care urmeaza celui curent citit.

Afisare PrintWriter PrintStream

Metode convenabile pentru afisarea informatiilor.

Crearea unui flux

Orice flux este un obiect al clasei ce implementeaza fluxul respectiv. Crearea unui flux se realizeaza asadar similar cu crearea obiectelor prin instructiunea new().

Exemple:

//crearea unui flux de intrare pe caractere

FileReader in = new FileReader("fisier_intrare.txt");

//crearea unui flux de iesire pe caractere

FileWriter out = new FileWriter("fisier_iesire.txt");

//crearea unui flux de intrare pe octeti

FileInputStream in = new FileInputStream("fisier_intrare.txt");

//crearea unui flux de iesire pe octeti

FileOutputStrem out = new FileOutputStream("fisier_iesire.txt");

Asadar, crearea unui flux primitiv de date care scrie/citeste informatii de la un dispozitiv extern are formatul general:

FluxPrimitiv numeFlux = new FluxPrimitiv( dispozitiv extern )

Fluxurile de procesare nu pot exista de sine statatoare ci se suprapun pe un flux primitiv de citire/scriere a datelor. Din acest motiv constructorii claselor pentru fluxurile de procesare nu primesc ca argument un dispozitiv extern de memorare a datelor ci o referinta la un flux primitiv responsabil cu citirea/scrierea efectiva a datelor:

Exemple:

//crearea unui flux de intrare printr-un buffer

BufferedReader in = new BufferedReader(new FileReader("fisier.in"));

//echivalent cu

FileReader fr = new FileReader("fisier.in");

BufferedReader in = new BufferedReader(fr);

//crearea unui flux de iesire printr-un buffer

BufferedWriter out = new BufferedWriter(new FileWriter("fisier.out")));

//echivalent cu

FileWriter fo = new FileWriter("fisier.out");

BufferedWriter out = new BufferedWriter(fo);

Asadar, crearea unui flux pentru procesarea datelor are formatul general:

FluxProcesare numeFlux = new FluxProcesare( referintaFluxPrimitiv )

In general, fluxurile pot fi grupate n succesiuni orict de lungi:

DataInputStream in =

new DataInputStream(

new BufferedInputStream(

new FileInputStream("fisier.in")));

Fluxuri pentru lucrul cu fisiere (fluxuri de tip "File")

Fluxurile pentru lucrul cu fisiere sunt cele mai usor de nteles. Clasele care implementeaza aceste fluxuri sunt urmatoarele:

FileReader, FileWriter

- caractere

FileInputStream, FileOutputStream - octeti

Constructorii acestor clase accepta ca argument un obiect care sa specifice un anume fisier. Acesta poate fi un sir de caractere, on obiect de tip File sau un obiect de tip FileDesciptor (vezi "Clasa File"). Constructorii clasei FileReader:

public FileReader( StringfileName ) throws FileNotFoundException

public FileReader( Filefile ) throws FileNotFoundException

public FileReader( FileDescriptorfd )

Constructorii clasei FileWriter:

public FileWriter( StringfileName ) throws IOException

public FileWriter( Filefile ) throws IOException

public FileWriter( FileDescriptorfd )

public FileWriter( StringfileName, booleanappend ) throws IOException

Cei mai uzuali constructori sunt cei care primesc ca argument numele fisierului. Acestia pot provoca exceptii de tipul FileNotFoundException n cazul n care fisierul cu numele specificat nu exista. Din acest motiv orice creare a unui flux de acest tip trebuie facuta ntr-un bloc try sau metoda n care sunt create fluxurile respective trebuie sa arunce exceptiile de tipul FileNotFoundException sau de tipul superclasei IOException.

Exemplu: un program care copie con]inutul unui fisier n alt fisier:

import java.io.*;

public class Copy {

public static void main(String[] args) throws IOException {

FileReader in = new FileReader("in.txt");

FileWriter out = new FileWriter("out.txt");

int c;

while ((c = in.read()) != -1)

out.write(c);

in.close();

out.close();

}

}

Obs: metoda main arunca exceptii IOException care este superclasa pentru FileNotFoundException. Aceste exceptii nu vor "prinse" dect de interpretor si va fi afisat un mesaj de eroare la aparitia lor.

Citirea si scrierea cu zona tampon

Clasele pentru citirea/scrierea cu zona tampon sunt:

BufferedReader, BufferedWriter

- caractere

BufferedInputStream, BufferedOutputStream- octeti

Sunt folosite pentru a introduce un buffer n procesul de scriere/citire a informatiilor, reducnd astfel numarul de accese la dispozitivul ce reprezinta sursa originala de date. Sunt mult mai eficiente dect fluxurile fara buffer si din acest motiv se recomanda folosirea lor ori de cte ori este posibil. Clasele BufferedReader si BufferedInputStream citesc n avans date si le memoreaza ntr-o zona tampon (buffer). Atunci cnd se executa o operatie read(), octetul citit va fi preluat din buffer. In cazul n care buffer-ul este gol citirea se face direct din flux si, odata cu citirea octetului, vor fi memorati n buffer si octetii care i urmeaza. Similar, se lucreaza si cu clasele BufferedWriter si BufferedOutputStream. Fluxurile de citire/scriere cu buffer sunt fluxuri de procesare si sunt folosite prin suprapunere cu alte fluxuri.

Exemplu:

BufferedOutputStream out = new BufferedOutputStream(

new FileOutputStream("out.dat"), 1024)

Constructorii acestor clase sunt urmatorii: BufferedReader BufferedReader( Reader in )

BufferedReader( Reader in, intdim_buffer )

BufferedWriter BufferedWriter( Writer out )

BufferedWriter( Writer out, intdim_buffer )

BufferedInputStream BufferedInputStream( InputStream in )

BufferedInputStream( InputStream in, int dim_buffer )

BufferedOutputStream BufferedOutputStream( OutputStreamout )

BufferedOutputStream( OutputStreamout, int dim_buffer )

In cazul constructorilor n care dimensiunea buffer-ului nu este specificata, aceasta primeste valoarea implicita de 512 octeti.

Metodele acestor clase sunt cele uzuale de tipul read si write (vezi "Metode comune fluxurilor"). Pe lnga acestea, clasele pentru scriere prin buffer mai au si metoda flush care goleste explicit zona tampon chiar daca aceasta nu este plina.

Exemplu:

BufferedWriter out = new BufferedWriter(

new FileWriter("out.dat"), 1024)

//am creat un flux cu buffer de 1024 octeti

for(int i=0; i in fisier nu s-a scris nimic

out.flush();

//bufferul este golit -> datele se scriu n fisier

Metoda readLine

BufferedReader br = new BufferedReader(new FileReader("in"))

String input;

while ((input = br.readLine()) != null) {

. . .

//readLine metoda specifica care citeste o linie

}

Metoda readLine permite citirea unui flux linie cu linie.

Concatenarea fisierelor

Clasa SequenceInputStream permite unei aplicatii sa combine serial mai multe fluxuri de intrare astfel nct acestea sa apara ca un singur flux de intrare. Citirea datelor dintr-un astfel de flux se face astfel: se citeste din primul flux de intrare specificat pna cnd se ajunge la sfrsitul acestuia, dupa care primul flux de intrare este nchis si se deschide automat urmatorul flux de intrare din care se vor citi n continuare datele, dupa care procesul se repeta pna la terminarea tuturor fluxurilor de intrare. Constructorii acestei clase sunt:

SequenceInputStream( Enumeratione ) Construieste un flux secvential dintr-o multime de fluxuri de intrare. Fiecare obiect n enumerarea primita ca parametru trebuie sa fie de tipul InputStream.

SequenceInputStream( InputStreams1, InputStreams2 ) Construieste un flux de intrare care combina doua fluxuri s1 si s2. Primul flux citit va fi s1.

Exemplul cel mai elocvent de folosirea a acestei clase este concatenarea a doua fisiere:

//Concatenarea a 2 fisiere ale caror nume sunt primite la linia de comanda

//Rezultatul concatenarii este afisat pe ecran

import java.io.*;

public class Concatenare1 {

public static void main(String args[]) throws IOException {

FileInputStream f1 = new FileInputStream(args[0]);

FileInputStream f2 = new FileInputStream(args[1]);

SequenceInputStream s = new SequenceInputStream(f1, f2);

int c;

while ((c = s.read()) != -1)

System.out.write(c);

s.close();

//f1 si f2 sunt nchise automat

}//main

}//class

Pentru concatenarea mai multor fisiere exista doua variante

folosirea unei enumerari - primul constructor

concatenarea pe rnd a acestora folosind al 2-lea constructor; concatenarea a 3 fisiere va construi un flux de intrare astfel:

o FileInputStream f1 = new FileInputStream(args[0]);

o FileInputStream f2 = new FileInputStream(args[1]);

o FileInputStream f3 = new FileInputStream(args[2]);

o SequenceInputStream s = new SequenceInputStream(

o

f1, new SequenceInputStream(f2, f3));

Fluxuri pentru filtrarea datelor

Un flux de filtrare se ataseaza altui flux pentru a filtra datele care sunt citite/scrise de catre acel flux. Clasele pentru fluxuri de filtrare au ca superclase clasele abstracte FilterInputStream (pentru filtrarea fluxurilor de intrare) si FilterOutputStream (pentru filtrarea fluxurilor de iesire). Clasele pentru filtrarea datelor sunt:

DataInputStream, DataOutputStream

BufferedInputStream, BufferedOutputStream

LineNumberInputStream

PushbackInputStream

PrintStream (flux de iesire)

Observati ca toate aceste clase descriu fluxuri de octeti. Filtrarea datelor nu trebuie vazuta ca o metoda de a elimina anumiti octeti dintr-un flux ci de transforma acesti octeti n date care sa poata fi interpretate sub alta forma. Asa cum am vazut la citirea/scrierea cu zona tampon clasele de filtrare BufferedInputStream si BufferedOutputStream grupeaza datele unui flux ntr-un buffer, urmnd ca citirea/scrierea sa se faca prin intermediu acelui buffer (vezi "Citirea si scrierea cu zona tampon"). Asadar fluxurile de filtrare nu elimina date citite sau scrise de un anumit flux, ci introduc o noua modalitate de manipulare a lor. Din acest motiv fluxurile de filtrare vor contine anumite metode specializate pentru citirea/scrierea datelor, altele dect cele comune tuturor fluxurilor (metode de tip read/write). Folosirea fluxurilor de filtrare se face prin atasarea lor de un flux care se ocupa efectiv de citirea/scrierea datelor:

FluxFiltrare numeFlux = new FluxFiltrare ( referintaAltFlux )

Cele mai importante clase din aceasta categorie sunt DataInputStream si DataOutputStream

Clasele DataInputStream si DataOutputStream

Aceste clase ofera metode prin care un flux nu mai este vazut ca o nsiruire de octeti, ci ca o sursa de date primitive. Prin urmare vor furniza metode pentru citirea si scrierea datelor la nivel de tip de data si nu la nivel de octet. Constructorii si metodele cele mai importante (altele dect read/write) sunt date n tabelul de mai jos :

DataInputStreamDataOuputStream

//ConstructorDataInputStream(InputStream in) //ConstructorDataOutputStream(OutputStream out)

readBoolean( )

readByte( )

readChar( )

readDouble( )

readFloat( )

readInt( )

readLong( )

readShort( )

readUnsignedByte( )

readUnsignedShort( )

String readUTF( )writeBoolean( boolean v )

writeByte( int v )

writeChar( int v )

writeDouble( double v )

writeFloat( float v )

writeInt( int v )

writeLong( long v )

writeShort( int v )

writeBytes( String s )

writeChars( String s )

writeUTF( String str )

Aceste metode au denumirile generice de readXXX si writeXXX specificate de interfetele DataInput si DataOutput. Pot provoca exceptii de tipul IOException.

Atentie: Un fisier n care au fost scrise informatii folosind metode writeXXX nu va putea fi citit dect prin metode readXXX.

Fluxuri standard de intrare/iesire

Mergnd pe linia introdusa de sistemul de operare UNIX orice program Java are :

o intrare standard

o iesire standard

o iesire standard pentru erori

In general intrarea standard este tastatura iar iesirea standard este ecranul. Intrarea si iesirea standard sunt de fapt niste obiecte pre-create ce descriu fluxuri de date pentru citirea respectiv scrierea la dispozitivele standard ale sistemului. Aceste obiecte sunt definite publice n clasa System si sunt:

VariabilaSemnificatieTip flux

System.in flux standard de intrare InputStream

System.out flux standard de iesire PrintStream

System.err flux standard pentru afisarea erorilor PrintStream

Afisarea informatiilor pe ecran

Am vazut deja numeroase exemple de folosire a fluxului standard de iesire, el fiind folosit la afisarea oricaror rezultate pe ecran (n modul consola):System.out.println("mesaj"). Fluxul standard pentru afisarea erorilor se foloseste similar:

catch (IOException e) {

System.err.println("Eroare de intrare/iesire!")

}

Fluxurile de iesire pot fi folosite asadar fara probleme deoarece tipul lor este PrintStream, clasa primitiva pentru scrierea efectiva a datelor. In schimb fluxul standard de intrare System.out este de tip InputStream care este o clasa abstracta, deci pentru a-l putea utiliza va trebui sa-l folosim mpreuna cu un flux de procesare a datelor sau cu orice alt flux ce permite citirea efectiva a datelor.

Citirea datelor de la tastatura

Uzual vom dori sa folosim metoda readLine pentru citirea datelor de la tastatura si din acest motiv vom folosi intrarea standard mpreuna cu o clasa de procesare care implementeaza metoda readLine. Exemplul tipic este:

BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));

System.out.print("Introduceti o linie:");

String linie = stdin.readLine()

System.out.println(linie);

Exemplu: un program care afiseaza liniile introduse de la tastatura

import java.io.*;

public class Echo {

public static void main(String[] args) {

BufferedReader stdin = new BufferedReader(

new InputStreamReader(System.in));

String s;

try {

while((s = stdin.readLine()).length() != 0)

System.out.println(s);

//Programul se opreste cu o linie vida

} catch(IOException e) {e.printStackTrace();}

}

}

Observatie: Metoda readLine poate provoca exceptii de tipul IOException.

Redirectarea intrarii/iesirii standard

Incepnd cu Java 1.1 au fost introduse n clasa System metode statice pentru a redirecta fluxurile de intrare si iesire standard. Aceste metode sunt:

setIn(InputStream)- redirectare intrare

setOut(PrintStream)- redirectare iesire

setErr(PrintStream)- redirectare erori

Redirectarea iesirii este utila n special atunci cnd sunt afisate foarte multe date pe ecran si acestea se deruleaza mai repede dect putem citi. Putem redirecta afisarea catre un fisier pe care sa-l citim dupa executia programului. Secventa clasica de redirectare a iesirii este:

PrintStream out = new PrintStream(new BufferedOutputStream(

new FileOutputStream("rezultate.out")));

System.setOut (out);

Redirectarea erorilor ntr-un fisier poate fi de asemenea utila.

PrintStream err = new PrintStream(new BufferedOutputStream(

new FileOutputStream("erori.err")));

System.setErr (err);

Redirectarea intrarii poate fi utila pentru un program consola care primeste niste valori de intrare. Pentru a nu le scrie de la tastatura de fiecare data n timpul testarii programului ele pot fi puse ntr-un fisier, redirectnd intrarea standard. In momentul cnd testarea programului a luat sfrsit redirectarea poate fi eliminata, datele fiind cerute din nou de la tastatura.

Exemplu de folosire a redire