Professional Documents
Culture Documents
Programare Obiect Orientat A2: Iolu Mihai - S Tefan
Programare Obiect Orientat A2: Iolu Mihai - S Tefan
ORIENTATĂ 2
19 noiembrie 2008
ii
Cuprins
1 Introducere 1
1.1 Mediul de programare Java . . . . . . . . . . . . . . . . . . . 3
iii
iv CUPRINS
Introducere
1
2 CAPITOLUL 1. INTRODUCERE
• Limbajul Java este un limbaj relativ simplu. După cum vom vedea ı̂n
cele ce urmează ı̂n Java nu avem numeroase construcţii care existau ı̂n
C/C++ precum: pointeri, structuri, fişiere header, directive de prepro-
cesor, supraı̂ncărcare de operatori, moştenire virtuală etc.
• Limbajul Java este obiect orientat. În ziua de azi, majoritatea limba-
jelor care există pe piaţă şi au o foarte largă răspândire au la bază
principiile programării obiect orientate (vezi C++, C#, Visual Basic).
• Java este un limbaj robust. Cei care au dezvoltat acest limbaj au avut
ca scop foarte important ı̂ncercarea de a evita pe cât posibil apariţia
erorilor, prin depistarea acestora cât mai rapidă sau prin uşurarea mod-
ului de a programa. Un astfel de exemplu este lipsa pointerilor care face
ca programarea să fie mai uşoară şi practic face imposibilă coruperea
memoriei care se putea produce destul de uşor ı̂n C/C++.
• Java este portabil. Aceasta ı̂nseamnă că dacă scriem un cod pe plat-
formă Windows, exact acelaşi cod fără modificări va putea rula şi pe o
platformă Linux de exemplu. Nu acelaşi lucru se ı̂ntâmpla, de exemplu,
ı̂n cazul C++. Tipul int putea avea dimensiuni diferite ı̂n funcţie de
platforma ţintă.
• Java este neutru din punct de vedere arhitectural. În momentul ı̂n care
se compilează codul Java, rezultatul produs este un bytecode. Acesta
este rulat cu ajutorul unei maşini virtuale Java (JVM - Java Virtual
Machine). Din cauza acestui mod de gândire este posibil ca acelaşi
cod să ruleze pe orice sistem de operare, doar implementarea maşinii
virtuale diferind. Un mecanism asemănător a fost dezvoltat şi de către
cei de la Microsoft pentru platforma .NET, care are la bază tot o maşină
virtuală.
Voi ı̂ncerca ı̂n cele ce urmează să prezint un curs ı̂n care accentul se va
pune mai ales pe partea practică a limbajului şi pe facilităţile oferite de
acesta pentru programarea obiect orientată. Din cauza timpului limitat, nu
vom prezenta facilităţile pentru lucrul cu interfeţe grafice, pentru programare
web sau pentru realizarea aplicaţiilor pe mobil. Acestea sunt subiecte de mare
interes care pot fi studiate ı̂ntr-un curs viitor.
Totuşi trebuie precizat ı̂ncă de la ı̂nceput că se foloseşte mediul Java
pentru a realiza mai multe tipuri de aplicaţii:
1.1. MEDIUL DE PROGRAMARE JAVA 3
• Aplicaţii consolă (ı̂n linie de comandă): se pot rula din linie de co-
mandă, neavând o interfaţă grafică
• Aplicaţii grafice (folosesc librarii AWT sau Swing)
• Applet-uri - sunt un anumit tip de aplicaţii grafice care pot fi introduse
ı̂n pagini web, facând ca o pagină web să aibă un aspect mult mai
dinamic asemănător aplicaţiilor desktop
• Pagini web dinamice - sunt create cu ajutorul unor tehnologii care s-au
dezvoltat peste Java şi care adaugă funcţionalităţi noi limbajului (vezi
JSP, Servlets, Spring etc.)
• Aplicaţii pe mobil (se realizează cu ajutorul unei distribuţii Java numită
J2ME - Java 2 Microedition).
Accentul ı̂n continuare se va pune mai ales pe primul tip de aplicaţie,
doar tangenţial se vor atinge celelalte tipuri.
Trebuie spus că majoritatea ideilor prezentate vis-a-vis de programarea
obiect orientată sunt aplicabile şi ı̂n cazul altor limbaje obiect orientate pre-
cum C++ (deja studiat) şi C# (care va fi studiat ı̂n semestrul următor).
Voi ı̂ncerca să prezint ı̂n continuare următoarele lucruri:
• Structurile de programare fundamentale ı̂n Java
• Lucrul cu obiecte şi clase, moştenirea şi lucrul cu interfeţe
• Tratarea excepţiilor
• Stream-uri Java
• Programarea generică şi colecţiile
• Lucrul cu baze de date
• Annotations
• Java Reflection
Pentru a putea compila şi rula aplicaţii Java este bine să ı̂ncepem prin a
instala JDK -ul (Java Development Kit).
Acesta poate fi descărcat gratuit la următoarea adresă web:
http://java.sun.com/javase/downloads/index.jsp.
În acest moment se poate alege ı̂ntre o versiune beta care este mai nouă
sau o versiune stabilă.
Este recomandabil, de asemenea, să se downloadeze şi documentaţia pen-
tru JDK care se găseşte ı̂n secţiunea Java SE 6 Documentation.
După ce se instalează JDK (procesul este foarte uşor, motiv pentru care
nici nu va fi descris) se poate dezarhiva directorul docs şi, eventual, se poate
copia ı̂n directorul ı̂n care a fost salvat JDK.
CAPITOLUL 2
Pentru a rula acest program din linie de comandă se parcurg mai mulţi
paşi:
5
6CAPITOLUL 2. STRUCTURI FUNDAMENTALE DE PROGRAMARE ÎN JAVA
Observaţii:
• Numele fişierului trebuie să fie chiar Welcome.java deoarece ı̂n program
avem o clasă publică numită Welcome.
• În directorul ı̂n care am instalat Java există mai multe directoare pre-
cum: bin, include, lib etc. O semnificaţie deosebit o are directorul bin
ı̂n care sunt poziţionate mai multe fişiere executabile precum java.exe
şi javac.exe. Este necesar pasul 3 deoarece aceste fişiere executabile nu
sunt implicit ı̂n calea sistemului de operare. De altfel, la paşii 4 şi 5,
compilarea respectiv rularea programelor se face folosind aceste fişiere
executabile.
În Java tot codul care va fi scris trebuie să fie introdus ı̂n cadrul unor
clase, motiv pentru care şi aici am scris codul ı̂n cadrul unei clase. Această
clasă are plasată ı̂naintea definiţiei sale cuvntul cheie public ceea ce ı̂nseamnă
că ea poate fi accesată de oriunde. Vom vorbi mai multe despre indicatorii
de acces atunci când vom prezenta modul ı̂n care se lucrează cu clase.
În interiorul acestei clase avem definită o metodă main() care este publică
şi statică. Această metodă este mai specială, ea fiind ı̂ntotdeauna publică şi
statică. Ce o face mai specială este faptul că ea constituie punctul de intrare
ı̂n program. Aceasta ı̂nseamnă că atunci când rulăm o aplicaţie, execuţia
acesteia ı̂ncepe cu această metodă.
2.1. PRIMUL PROGRAM JAVA 7
• În momentul ı̂n care se scrie cod Java, acesta trebuie aliniat core-
spunzător. Compilatorul nu va ”plânge” dacă nu se respectă această
regulă, dar aceasta indică un programator care de multe ori nu ı̂nţelege
ceea ce programează.
8CAPITOLUL 2. STRUCTURI FUNDAMENTALE DE PROGRAMARE ÎN JAVA
Acesta este un program foarte simplu care are ca efect afişarea unui mesaj
de ı̂ntâmpinare pentru fiecare nume transmis din linie de comandă.
Astfel, vom rula programul cu urmatoarea comandă:
Hello radu!
Hello aurel!
Hello ana maria!
System.exit(0);
}
double a=Double.parseDouble(args[0]);
double b=Double.parseDouble(args[1]);
double c=Double.parseDouble(args[2]);
double delta=b*b-4*a*c;
if(delta<0)
System.out.println("Ecuatia nu are solutii reale");
else
if(delta==0)
System.out.println("Solutie unica: "+ (-b)/(2*a));
else
{
double x1=(-b-Math.sqrt(delta))/(2*a);
double x2=(-b+Math.sqrt(delta))/(2*a);
System.out.println("x1="+x1);
System.out.println("x2="+x2);
}
}
}
Observaţii:
• Metoda statică parseDouble() a clasei Double are rolul de a transforma
un şir de caractere ı̂ntr-un număr de tip double. În mod asemănător
există Integer.parseInt() şi Float.parseFloat().
2.3 Comentarii
În Java se pot realiza trei tipuri de comentarii care sunt toate prezentate
ı̂n exemplul următor. Această aplicaţie calculează suma cifrelor unui număr
ı̂ntreg:
/**
*
* @author mihai
10CAPITOLUL 2. STRUCTURI FUNDAMENTALE DE PROGRAMARE ÎN JAVA
* @version 1.0
*/
public class ExempluComentariu
{
/**
* @param n
* numarul pentru care se calculeaza suma cifrelor.
* Trebuie sa fie un numar mai mare decat 0.
* @return suma cifrelor numarului
*/
public static int sumaCif(int n)
{
/*
* Metoda sumaCif este o metoda recursiva
* care calculeaza suma cifrelor unui numar
*/
if(n<9)
return n;
return n%10+sumaCif(n/10); //aici are loc apelul recursiv
}
• Comentarii pe mai multe linii, care sunt cuprinse ı̂ntre ”/*”’ şi ”*/”’
Primele două moduri de lucru sunt identice cu ceea ce ı̂ntâlnim ı̂n C/C++,
motiv pentru care ne vom axa ı̂n principal pe a treia modalitate de generare
a documentaţiei.
Comentariile care ajută la generarea documentaţiei sunt folosite pentru
a genera nişte fişiere HTML care descriu clasa pe care am scris-o. Aceste
fişiere vor arăta exact la fel ca documentaţia furnizată ı̂mpreună cu mediul
JDK.
Generarea acestor fişiere se poate face utilizând utilitarul javadoc astfel:
javadoc ExempluComentariu.java
Vom remarca apariţia ı̂n directorul ı̂n care avem fişierul a mai multor fişiere
HTML. Dacă deschidem fişierul ExempluComentariu.html vom putea vedea
documentaţia.
Un rol foarte important ı̂n cadrul documentaţiei ı̂l au tag-urile predefinite,
cu ajutorul cărora se pot specifica anumite informaţii mai speciale:
• @name - reprezintă numele celui care a scris codul pentru această clasă
• Există şi alte tag-uri care sunt foarte utile. Cel mai bine se poate studia
modul ı̂n care sunt ele folosite dacă examinăm codul sursă Java prezent
ı̂n fişierul src.zip aflat ı̂n directorul ı̂n care este instalat Java.
• tipul caracter
• tipul boolean
Un tip de date mai deosebit este tipul char. În Java variabilele de tip
char se reprezintă ı̂ntre caractere ”. La fel ca şi ı̂n C/C++ există caractere
mai speciale care sunt tratate folosind secvenţe escape: \b, \t, \n, \r, \’, \”,
\\.
În Java tipul boolean are două valori true şi false. Nu se mai respectă
convenţia din C/C++ care spunea că valoarea 0 ı̂nseamnă false şi valoare
diferită de 0 reprezintă true.
2.5 Operatori
Un operator utilizează unul sau mai mulţi operanzi pentru a produce o
nouă valoare. Un operator poate, de asemenea, să schimbe valoarea unui
operand.
14CAPITOLUL 2. STRUCTURI FUNDAMENTALE DE PROGRAMARE ÎN JAVA
• Operatorul de atribuire =
• Operatorii aritmetici:
• Operatori logici:
• Operatorul de cast
• Operatorul instanceof
if (conditie)
secventa1
[else
secventa2]
switch(expresie)
{
case val1: secventa1
[break;]
case val2: secventa2
[break;]
....
case valN: secventaN
[break;]
[default:
secventa]
}
while (conditie)
instructiune
Ideea acestei instrucţiuni este că, att̂a timp cât este adevaărată condiţia,
se execută instrucţiunea cuprinsă ı̂n ciclul while.
Instrucţiunea do-while are următoarea formă:
do
instructiune
while (conditie);
Ideea acestei instrucţiuni este că se tot execută instrucţiunea (care poate
fi simplă sau compusă) atâta timp cât este adevărată condiţia.
Cea de-a treia instrucţiune repetitivă, care de asemenea exista şi ı̂n lim-
bajul C/C++ este instrucţiunea for :
16CAPITOLUL 2. STRUCTURI FUNDAMENTALE DE PROGRAMARE ÎN JAVA
nume=new tip[dimensiune];
2.8. VECTORI ŞI MATRICI 17
După cum se poate observa, ı̂n acest al doilea caz declararea şi alocarea
de memorie se fac ı̂n aceeaşi instrucţiune.
Observaţii:
• Orice tablou are o dată membră length care returnează lungimea tabloului
respectiv, ca ı̂n exemplul următor:
Pentru a accesa elementele unui tablou vom folosi, la fel ca şi ı̂n C/C++
numele tabloului şi numărul elementului ı̂n cadrul tabloului:
nume[indice];
Tipul tablou este un tip referinţă ceea ce ı̂nseamnă că variabila cu ajutorul
căreia am declarat tabloul, de fapt reţine adresa zonei efective de memorie
unde se reţin elementele tabloului.
Acesta este motivul pentru care dacă vom transmite ca parametru un
tablou la o metodă modificările efectuate asupra elementelor acestuia se vor
păstra.
18CAPITOLUL 2. STRUCTURI FUNDAMENTALE DE PROGRAMARE ÎN JAVA
Probleme rezolvate
Problema 1: Se citeşte de la tastatură un şir de numere ı̂ntregi. Să se
sorteze crescător elementele şirului şi să se afişeze şirul astfel obţinut.
}
}
Observaţii:
• Se putea realiza afişarea şirului şi ı̂n alt mod, folosind capabilităţile
oferite de JDK 1.6:
for(int x:a)
System.out.println(x+" ");
• Pentru lucrul cu şiruri avem clasa Arrays care are definite câteva
metode foarte utile. Astfel, puteam sorta elementele şirului folosind
următorul cod:
Arrays.sort(a);
În Java există o metodă prin care putem copia elemente dintr-un şir in
alt şir elegant şi eficient, folosind metoda arraycopy a clasei System. Antetul
acestei metode este următorul:
20CAPITOLUL 2. STRUCTURI FUNDAMENTALE DE PROGRAMARE ÎN JAVA
2.8.2 Matrici
În Java, o matrice este reţinută sub forma unui şir de şiruri. O matrice
se poate declara şi iniţializa ı̂n trei feluri.
Cea mai simplă variantă este să declarăm matricea şi să alocăm memorie
pentru ea ca ı̂n exemplul următor:
int[][] a=new int[nrLinii][nrColoane];
Matricile se pot şi iniţializa direct. Prezentăm modul ı̂n care se poate
iniţializa o matrice cu 2 linii şi 3 coloane:
int[][] a= {{2, 3, 4},
{1, 4, 2}};
Totuşi, ı̂n Java o matrice poate avea un număr variabil de elemente pe
fiecare linie:
int[][] a= {{2, 3},
{1, 4, 2, 5}};
În acest caz, numărul de linii ale matricii este a.length iar numărul de
coloane ale liniei i este a[i].length.
Dacă dorim să citim o astfel de matrice de la tastatură putem proceda
astfel:
2.8. VECTORI ŞI MATRICI 21
Cel mai bine se va vedea cum se lucrează cu astfel de matrici ı̂n exemplele
următoare.
Problema 1 Se citeşte de la tastatură o matrice a, pătratică de ordin n.
Să se verifice dacă toate elementele de pe diagonala principală sunt mai mari
decât suma elementelor de pe liniile corespunzătoare lor.
import java.util.*;
public class VerificareMatrice
{
public static void main(String args[])
{
Scanner s=new Scanner(System.in);
System.out.print("n=");
int n=s.nextInt();
int[][] a=new int[n][n];
for(int i=0;i<a.length;i++)
for(int j=0;j<a.length;j++)
{
System.out.print("a["+i+"]["+j+"]=");
a[i][j]=s.nextInt();
}
if(verificare(a))
System.out.println("Se respecta conditia");
else
System.out.println("Nu se respecta conditia");
}
{
int s=0;
for(int j=0;j<a[i].length;j++)
s+=a[i][j];
if(2*a[i][i]<=s)
return false;
}
return true;
}
}
Problema 2 Se citeşte o matrice a cu n linii şi număr variabil de elemente
pe fiecare linie. Să se calculeze şi să se afişeze minimul dintre maximele de
pe fiecare linie.
int max=a[0];
for(int i=1;i<a.length;i++)
if(a[i]>max)
max=a[i];
return max;
}
String nume="Adi";
System.out.println(nume.length());
if (s1.equals(s2))
...
• int length()
• String toLowerCase()
• String toUpperCase()
• String trim()
2.9. ŞIRURI DE CARACTERE 25
import java.util.*;
public class StringPalindrom
{
static boolean palindrom(String s)
{
for(int i=0;i<=(s.length()-2)/2;i++)
if(s.charAt(i)!=s.charAt(s.length()-i-1))
return false;
return true;
}
import java.util.*;
public class SortareStringuri
{
public static void main(String args[])
26CAPITOLUL 2. STRUCTURI FUNDAMENTALE DE PROGRAMARE ÎN JAVA
{
Scanner s=new Scanner(System.in);
System.out.print("n=");
int n=s.nextInt();
s.nextLine();
String[] str=new String[n];
for(int i=0;i<str.length;i++)
{
System.out.print("str["+i+"]=");
str[i]=s.nextLine();
}
sortare(str);
afisare(str);
}
Observaţie importantă:
Clasa String este immutable ceea ce ı̂nseamnă că niciodată o variabilă
de tip String nu se poate modifica. Se poate doar ca ea să reţină o altă
referinţă. Dacă vrem să putem modifica un şir de caractere folosim clasa
StringBuf f er.
2.9. ŞIRURI DE CARACTERE 27
return s2;
28CAPITOLUL 2. STRUCTURI FUNDAMENTALE DE PROGRAMARE ÎN JAVA
Problema s-ar mai fi putut rezolva şi ı̂n alt mod, şi anume prin folosirea
metodei append() a clasei StringBuffer:
import java.util.*;
public class AfisareOperanzi
{
public static void main(String args[])
{
Scanner s=new Scanner(System.in);
System.out.print("Expresia matematica: ");
String expr=s.nextLine();
2.9. ŞIRURI DE CARACTERE 29
import java.util.regex.Pattern;
import java.util.*;
Reprezentare Semnificaţie
[abc] a, b sau c
[ˆabc] orice caracter cu excepţia lui a, b sau c
[a-zA-Z] caractere ı̂ntre a-z şi A-Z
[a-d[m-p]] caractere ı̂ntre a-d şi m-p
[a-z&&[def]] d, e sau f (intersecţie)
[a-z&&[ˆbc]] de la a la z, cu excepţia lui b şi c
[a-z&&[ˆm-p]] de la a la z, cu excepţia lui m-p
30CAPITOLUL 2. STRUCTURI FUNDAMENTALE DE PROGRAMARE ÎN JAVA
if(Pattern.matches("([\\d&&[^0]]\\d)2,4", str))
System.out.println("Respecta");
else
System.out.println("Nu respecta");
Reprezentare Semnificaţie
X? X, odată sau deloc
X* X, de zero sau mai multe ori
X+ X, odată sau de mai multe ori
X{n} X, exact de n ori
X{n,} X, de cel puţin n ori
X{n,m} X, de cel puţin n ori dar nu mai mult de m ori
CAPITOLUL 3
31
32 CAPITOLUL 3. CONCEPTE ŞI PRINCIPII ÎN POO
• Totul este un obiect (Un obiect este o variabilă mai deosebită care are
atât stare cât şi comportament)
• Un program este alcătuit dintr-un set de obiecte care-şi spun unul altuia
ce să facă.
• Fiecare obiect are propria sa memorie alcătuită eventual din alte obiecte.
• Toate obiectele de acelaşi tip pot primi aceleaşi mesaje (chiar şi ı̂n
contextul moştenirii).
După cum putem uşor observa toate aceste lucruri sunt valabile şi acum
ı̂n contextul limbajului Java.
• cei care creează clasa - aceştia sunt cei care cunosc toate detaliile despre
clasa respectivă, fiind cei care realizează crearea unui nou tip de date
• cei care sunt doar clienţi ai clasei - aceştia sunt acei programatori care
doar folosesc clasa respectivă nefiind interesaţi de modul ı̂n care clasa
este implementată efectiv, ci doar de serviciile pe care aceasta le oferă.
Acestea sunt specificate prin interfaţ a clasei respective.
Putem practic afirma că interfaţa unei clase reprezintă un contract ı̂ntre
prima categorie de programatori şi ceea de-a doua, ı̂n sensul ı̂n care cre-
atorii clasei le oferă o asigurare celor care folosesc acea clasă ca metodele din
interfaţă nu se vor modifica ı̂n viitor chiar dacă implementările vor fi altele.
Moştenirea se referă la anumite clase care partajează anumite informaţii
precum şi comportament. Subclasele sunt versiuni mai specializate ale unor
clase, care moştenesc atribute şi comportament de la clasele lor părinte şi işi
introduc propriile lor atribute şi metode.
Fiecare subclasă ı̂şi poate altera caracteristicile moştenite.
34 CAPITOLUL 3. CONCEPTE ŞI PRINCIPII ÎN POO
În unele limbaje, nu şi ı̂n Java, putem avea moştenire multiplă. Aceasta
ı̂nseamnă să moşteneşti atribute şi comportament din mai multe clase de
bază.
Polimorfismul permite programatorului să trateze metodele claselor
derivate ca şi pe metodele claselor lor părinte. Mai exact, polimorfismul
reprezintă capabilitatea obiectelor care aparţin unor tipuri de date diferite
să răspundă la apeluri de metode cu acelaşi nume, fiecare corespunzătoare
unui comportament specific tipului lor.
1. Între componentele unui modul (clasă, pachet, etc.) trebuie să existe
o coeziune ı̂naltă. Aceasta ı̂nseamnă că nu este bine, spre exemplu, ca
ı̂ntr-o clasă să avem mai multe informaţii şi metode ı̂ntre care nu există
legături strânse. Există metrici specializate pentru măsurarea acestei
caracteristici.
2. Între diversele părţi componente ale unei aplicaţii trebuie să existe o
cuplare slabă. Aceasta ı̂nseamnă că atunci când modificăm un modul,
modificările asupra lui să nu genereze modificări ı̂n cascadă asupra altor
module.
În final vom spune câteva cuvinte despre un alt principiu foarte important
referitor la programarea obiect orientată şi anume principiul numit Inversion
of Control (IoC). Acesta este un principiu abstract care descrie un anumit
aspect referitor la designul sistemelor soft ı̂n care fluxul evenimentelor este
invers faţă de cursul normal ı̂ntr-o aplicaţie tradiţională.
El a fost asemănat cu principiul numit ”Hollywood Principle”: don’t call
us, we’ll call you.
Se poate spune că IoC este un mod de a scrie software ı̂n care un cod
generic şi reutilizabil poate fi aplicat la diverse probleme.
Există două moduri de lucru care au la bază principiul IoC:
În limbajul Java pentru a crea până şi cea mai simplă aplicaţie este nevoie
să creăm măcar o clasă.
După cum vom vedea ı̂n continuare o aplicaţie Java este alcătuită, ı̂n
principiu, din mai multe clase care colaborează ı̂ntre ele. De obicei există o
clasă care conţine şi o metoda main() şi care constituie punctul de intrare ı̂n
aplicaţie, clasă care foloseşte alte clase la rularea aplicaţiei.
Se poate ı̂ntâmpla ca o aplicaţie să aibă mai multe metode main(),
situaţie ı̂n care, la rularea programului putem specifica care este clasa a
cărei metodă se va executa.
//definire constructori
37
38 CAPITOLUL 4. OBIECTE ŞI CLASE
class Stiva
{
public final static int MAX=100; //atribut static
private int nrElemente; //atribut nestatic
Dacă nu iniţializăm ı̂n mod explicit un atribut el are nişte valori implicite:
0 pentru tipurile ı̂ntregi, 0.0 pentru tipurile reale, boolean pentru caractere
şi null pentru obiecte.
Modificatorii aplicabili atributelor ı̂n Java sunt: public, private, pro-
tected, final, static, transient şi volatile.
class Complex
{
public double re;
public double im;
class UseComplex
{
public static void main(String args[])
{
Complex c=new Complex(3,4);
c.re=6; //se poate folosi atributul din afara clasei
}
}
Modificatorul protected specifică faptul că un atribut este vizibil atât ı̂n
clasele derivate din clasa ı̂n care este declarat cât şi ı̂n toate clasele care fac
parte din acelaşi pachet cu clasa ı̂n care a fost declarat atributul.
Dacă nu este folosit nici un modificator de vizibilitate se presupune im-
plicit că avem vizibilitate la nivel de pachet (package). Acest lucru ı̂nseamnă
că acel atribut este accesibil doar ı̂n interiorul pachetului ı̂n care este definită
clasa respectivă.
class Test
{
final int a=100;
final String s="test";
final int[] b={2, 4, 5};
void metoda()
{
//genereaza eroare deoarece nu se poate modifica un atribut de
//un tip primitiv declarat constant
a=1000;
În mod implicit, dacă modificatorul static nu este folosit asupra unui
atribut, acel atribut poate avea valori diferite pentru obiecte diferite. Astfel,
dacă avem definită o clasă P ersoana care are un atribut nume, acest atribut
va avea valori diferite pentru fiecare obiect de tipul P ersoana.
Modificatorul static aplicat unui atribut ı̂nseamnă că pentru acel atribut
se alocă memorie o singură dată indiferent câte obiecte de tipul clasei avem.
Spunem despre acel atribut că este un atribut al clasei, practic acea valoare
unică nu ţine de obiecte ci de clasă.
Prezentăm şi un exemplu din care se poate vedea modul ı̂n care se pot
folosi atributele statice şi nestatice. Remarcaţi faptul că un atribut static
poate fi accesat şi prin intermediul numelui clasei, mai degrabă decât prin
numele unui obiect.
class Test
{
public static int atributStatic;
public int atributNestatic;
}
t1.atributStatic=1;
t2.atributStatic=2;
System.out.println(t1.atributStatic+" "+Test.atributStatic);
//afiseaza valorile 2 si 2
}
}
42 CAPITOLUL 4. OBIECTE ŞI CLASE
Ca o primă observaţie importantă, trebuie spus că o clasă poate avea mai
multe metode cu acelaşi nume. Totuşi este evident nevoie ca metodele să
difere măcar prin tipul sau numărul parametrilor transmişi la metodă.
Modificatorii aplicabili metodelor ı̂n Java sunt: public, private, protected,
final, static, abstract, native şi synchronized.
Nu vom mai prezenta ı̂n cele ce urmează modificatorii de vizibilitate
public, private şi protected deoarece ei au aceeaşi semnificaţie ca şi ı̂n cazul
declarării atributelor.
Modificatorul f inal aplicat unei metode specifică faptul că acea metodă
nu mai poate fi redefinită ı̂ntr-o clasă derivată. Mai multe despre acest lucru
ı̂n capitolul dedicat moştenirii.
Modificatorul abstract aplicat unei metode specifică faptul că acea metodă
există pentru tipul respectiv, dar nu se cunoaşte ı̂ncă implementarea ei. Ea
va fi implementată ı̂ntr-o clasă derivată.
Modificatorul synchronized aplicat unei metode specifică faptul că apelul
asupra acelei metode trebuie sincronizat. Acest lucru se foloseşte ı̂n cazul
aplicaţiilor care utilizează fire de execuţie.
Vom prezenta ı̂n subsecţiunea următoare rolul modificatorului static asupra
metodelor.
Nu vom prezenta deocamdată facilităţile care ţin de tratarea excepţiilor.
4.3. DECLARAREA METODELOR UNEI CLASE 43
class Mathematica
{
private Mathematica()
{
}
Observatii:
public Complex()
{
this(0);
}
//alte metode...
Observaţii:
class Test
{
static int max=10;
static
{
max=100;
System.out.println("In initializatorul static");
}
}
public Complex()
{
this(0);
}
return re+"+"+im+"*i";
}
}
import java.util.*;
public class TestComplex
{
public static void main(String args[])
{
Scanner s=new Scanner(System.in);
System.out.print("n=");
int n=s.nextInt();
for(int i=0;i<c.length;i++)
{
System.out.print("partea reala: ");
double re=s.nextDouble();
System.out.print("partea imaginara: ");
double im=s.nextDouble();
c[i]=new Complex(re, im);
}
sortare(c);
afisare(c);
public Stack()
{
this(10);
// implicit se creeaza o stiva care are exact
// 10 elemente
}
{
int[] newElemente=new int[2*elemente.length];
System.arraycopy(elemente, 0, newElemente, 0, elemente.length);
elemente=newElemente;
}
import java.util.Scanner;
if(joc.ghiceste(c))
System.out.println("BRAVO!!! Ai ghicit !");
else
System.out.println("GRESEALA!!! Mai ai "+joc.incercariRamase()+" ince
}
if(joc.isWinner())
System.out.println("AI CASTIGAT!!!");
else
System.out.println("AI PIERDUT!!!");
}
}
4.6. PROBLEME REZOLVATE 53
După cum se poate uşor observa, metodele şi constructorii necesari pentru
această clasă sunt:
Spanzuratoare
---------------------------
public Spanzuratoare()
Având interfaţa clasei, adică metodele publice ale clasei, putem scrie co-
dul pentru aceasta:
import java.util.*;
public class Spanzuratoare
{
private String[] cuvinteDeAles={"abecedar", "calculator", "masina"};
private char[] cuvantCurent;
private char[] cuvantAles;
private int nrGreseli;
private String ghicite;
public Spanzuratoare()
{
Random r=new Random();
cuvantAles=cuvinteDeAles[r.nextInt(cuvinteDeAles.length)].toCharArray();
cuvantCurent=cuvantAles.clone();
char c1=cuvantAles[0];
char c2=cuvantAles[cuvantCurent.length-1];
cuvantCurent[0]=c1;
cuvantCurent[cuvantCurent.length-1]=c2;
for(int i=0;i<cuvantAles.length;i++)
if(cuvantAles[i]==c1 || cuvantAles[i]==c2)
cuvantCurent[i]=cuvantAles[i];
else
54 CAPITOLUL 4. OBIECTE ŞI CLASE
cuvantCurent[i]=’_’;
ghicite=""+c1+c2;
}
MyStringTokenizer
---------------------------
public MyStringTokenizer(String str, String sep)
public MyStringTokenizer(String str)
public MyStringTokenizer(String s)
{
this(s," \t\n\r\f");
}
import com.mihai.datastructures.*;
q.push(3);
q.push(5);
58 CAPITOLUL 4. OBIECTE ŞI CLASE
q.push(7);
try
{
System.out.println("In varful cozii: "+q.top());
q.pop();
System.out.println("Continutul cozii: "+q);
}
catch(EmptyQueueException e)
{
System.out.println("Coada goala !!!");
}
}
}
După cum se poate uşor observa vom avea nevoie de două clase: Queue
şi EmptyQueueException.
Clasa Queue are următoarea interfaţă:
Queue
---------------------------
public Queue()
package com.mihai.datastructures;
----------------------------------------
package com.mihai.datastructures;
class Node {
private int value;
4.6. PROBLEME REZOLVATE 59
Node(int value)
{
this.value=value;
this.next=null; //putea lipsi
}
int getValue() {
return value;
}
Node getNext()
{
return next;
}
----------------------------------------
package com.mihai.datastructures;
public class Queue {
private Node first, last;
sb.append("]");
return sb.toString();
}
}
for(int i=0;i<3;i++)
heap.add(r.nextInt(10));
System.out.println(heap);
while(!heap.isEmpty())
System.out.print(heap.extractMin()+" ");*/
for(int i=0;i<5;i++)
heap.add(r.nextInt(10));
System.out.println(heap);
HeapMin
---------------------------
public HeapMin(int[] a)
public HeapMin()
public HeapMin()
{
elements=new int[3];
}
public HeapMin(int[] a)
{
elements=new int[a.length+5];
System.arraycopy(a, 0, elements, 1, a.length);
count=a.length;
construiesteHeap();
}
else
newSize=elements.length*4/3;
int[] newElements=new int[newSize];
System.arraycopy(elements, 1, newElements, 1, elements.length-1);
elements=newElements;
}
3. Să se creeze o clasă Punct care conţine coordonatele unui punct ı̂n plan.
Se citesc de la tastatură n puncte ı̂n plan. Să se rezolve următoarele
subprobleme:
• tratarea ı̂n mod uniform a unor obiecte care sunt de tipuri diferite
(ı̂ncurajează polimorfismul)
Modul de lucru ı̂n Java seamănă oarecum cu ceea ce cunoaştem deja din
limbajul C++, numai că ı̂n Java sintaxa este mult mai uşoară. În plus,
ı̂n Java nu există conceptul de moştenire multiplă (ı̂n Java o clasă poate fi
derivată dintr-o singură altă clasă).
Pentru a indica faptul că o clasă este derivată din altă clasă folosim
cuvântul cheie extends, ca ı̂n exemplul următor:
Clasa din care se derivează se numeşte clasă de bază (sau clasă părinte,
superclasă) iar clasa care este derivată din ea se numeşte clasă derivată (sau
clasă copil sau subclasă).
67
68 CAPITOLUL 5. MOŞTENIREA ÎN JAVA
O subclasă descrie obiecte care au aceleaşi proprietăţi ca şi cele ale su-
perclasei şi care, eventual, au şi alte atribute sau metode.
Atributele şi metodele din superclasă sunt accesibile şi ı̂n subclasă, cu
excepţia atributelor şi metodelor care sunt declarate private ı̂n superclasă.
De multe ori, atributele private din superclasă pot fi accesate prin intermediul
unor metode din superclasă.
Reamintim că ı̂n limbajul Java există modificatorul de vizibilitate pro-
tected care are următoarea semnificaţie: acele atribute sau metode cu această
vizibilitate sunt văzute oriunde ı̂n subclase precum şi ı̂n toate clasele din pa-
chetul curent.
După cum ştim şi din C++, atunci când avem moştenire, trebuie să putem
apela din constructorul unei clase constructorul clasei de bază. Acest lucru
se poate face cu ajutorul cuvântului cheie super. Apelul constructorului din
clasa de bază trebuie să fie prima instrucţiune din constructor.
Un astfel de exemplu este prezentat ı̂n continuare:
care-l are, iar pentru fiecare student se cunoaşte dacă este bursier sau nu. Să
se sorteze crescător şirul de persoane după nume şi să se afişeze şirul obţinut.
În primul rând definim o clasă Persoană astfel:
public class Persoana
{
private String nume;
private String prenume;
System.out.print("n=");
int n=Integer.parseInt(s.nextLine());
for(int i=0;i<n;i++)
{
System.out.print("Numele: ");
String nume=s.nextLine();
System.out.print("Prenumele: ");
String prenume=s.nextLine();
72 CAPITOLUL 5. MOŞTENIREA ÎN JAVA
sort(p);
afisare(p);
}
Trebuie remarcat că ı̂n metoda de afişare, atunci când se parcurge şirul
de obiecte de tipul Persoana, ı̂n funcţie de tipul obiectelor, Profesor sau
Student se apelează metoda toString() corectă.
Acest mecanism poartă numele de polimorfism.
Soluţia acestei probleme poate fi prezentată sub forma diagramei UML
din figura 5.1.
5.3 Polimorfismul
Polimorfismul se referă la faptul că atunci când se apelează o metodă
asupra unui obiect care la prima vedere pare de un tip mai generic, de fapt
se apelează metoda corespunzătoare clasei efective a obiectului.
Acest lucru este posibil deoarece ı̂n Java se foloseşte apelul dinamic al
metodelor.
Atunci când se apelează o metodă asupra unui obiect, se parcurg următorii
paşi:
• În lista de metode ale clasei se identifică acele metode care se potrivesc
ca nume cu metoda apelată.
74 CAPITOLUL 5. MOŞTENIREA ÎN JAVA
• Dacă avem de-a face cu o metodă privată, statică, finală sau un con-
structor, atunci compilatorul ştie exact care este metoda care trebuie
apelată. În acest caz, avem de-a face cu legarea statică la momentul
rulării.
• Dacă nu suntem ı̂n una din aceste situaţii, atunci metoda apelată se
alege la momentul rulării de către maşina virtuală Java. În primul rând
se caută o metodă ı̂n clasa curentă, după care ı̂n clasa ei de bază, şi
tot aşa până când se găseşte o clasă ı̂n care metoda este implementată.
De fapt, ı̂n realitate lucrurile nu se ı̂ntâmplă chiar aşa, ci mai degrabă,
pentru fiecare clasă se construieşte câte un tabel cu metodele care se
pot apela din clasa respectivă şi doar se face o căutare ı̂n acea tabelă.
PROFESOR
getNume() => Persoana.getNume()
setNume() => Persoana.setNume()
getPrenume() => Persoana.getPrenume()
setPrenume() => Persoana.setPrenume()
getGradDidactic() => Profesor.getGradDidactic()
setGradDidactic() => Profesor.setGradDidactic()
toString() => Profesor.toString()
Şi ı̂n Java, la fel ca ı̂n toate limbajele moderne de programare este supor-
tată operaţia de cast.
Putem avea nevoie de operaţia de ea, de exemplu, atunci când avem un
şir de obiecte de un tip mai generic şi vrem să folosim anumite facilităţi par-
ticulare ale unui obiect care este de un tip mai specializat. Iată un exemplu:
for(int i=0;i<p.length;i++)
if(p[i] instanceof Profesor)
{
Profesor prof=(Profesor)p[i];
prof.setGradDidactic("profesor universitar");
}
for(int i=0;i<n;i++)
{
System.out.print("Tipul figurii? (c/d)");
if(s.nextLine().charAt(0)==’c’)
{
System.out.print("Culoarea: ");
String culoare=s.nextLine();
System.out.print("Raza: ");
double raza=Integer.parseInt(s.nextLine());
f[i]=new Cerc(culoare, raza);
}
else
{
System.out.print("Culoarea: ");
String culoare=s.nextLine();
System.out.print("Lungime: ");
double lungime=Integer.parseInt(s.nextLine());
System.out.print("Latime: ");
double latime=Integer.parseInt(s.nextLine());
f[i]=new Dreptunghi(culoare, lungime, latime);
}
}
sortare(f);
afisare(f);
}
figuri geometrice. Totuşi, nu puteam să le definesc ı̂n cazul clasei Figura
deoarece nu ştiu cum sunt implementate. Din acest motiv, metodele au fost
declarate ca abstracte.
Faptul că toate obiectele sunt derivate din clasa Object ı̂nseamnă că
următorul cod este perfect valabil:
String s="test";
Object o=s;
s=(String)o;
class Complex
{
// alte atribute si metode
if (getClass() != otherObject.getClass())
return false;
84 CAPITOLUL 5. MOŞTENIREA ÎN JAVA
class Persoana
{
private String nume, prenume;
//alte atribute si metode
public int hashCode()
{
return 3*nume.hashCode()+5*prenume.hashCode();
}
}
Observaţie: Dacă două obiecte prin folosirea metodei equals() sunt egale,
atunci şi metodele lor hashCode() trebuie să returneze aceleaşi valori.
să facem nimic, pentru că aceasta este implementarea implicită ı̂n Java. Dacă
totuşı̂ vrem să putem
Dacă obiectul iniţial are numai câmpuri de tipuri primitive atunci este
suficient să copiem câmpurile din vechiul obiect ı̂n cel nou. La fel se ı̂ntâmplă
şi ı̂n cazul câmpurilor de tip String, deoarece această clasă este immutable.
Prezentăm un exemplu ı̂n cele ce urmează:
class Complex implements Cloneable
{
private double re, im;
În cele ce urmează prezentăm un astfel de exemplu. Clasa Queue este cea
prezentată anterior, ı̂n plus adăugând doar faptul că ea poate reţine obiecte
de orice tip.
package com.mihai.datastructures;
package com.mihai.datastructures;
class Node {
private Object value;
private Node next;
Node(Object value)
{
this.value=value;
this.next=null; //putea lipsi
}
Object getValue() {
return value;
}
Node getNext()
{
return next;
}
this.next=next;
}
}
package com.mihai.datastructures;
public class Queue {
private Node first, last;
package com.mihai.main;
import java.util.StringTokenizer;
import com.mihai.datastructures.*;
q.push("mihai");
q.push("ioana");
q.push("ana maria");
try
{
System.out.println("In varful cozii: "+q.top());
String str=(String)q.pop();
System.out.println("Continutul cozii: "+q);
}
catch(EmptyQueueException e)
{
System.out.println("Coada goala !!!");
}
5.9. UTILIZAREA COLECŢIILOR GENERICE CU TEMPLATE-URI 89
}
}
Practic, dacă ne gândim mai bine o să observăm că aceste colecţii nu
oferă siguranţă ı̂n utilizare şi pot produce erori care nu pot fi depistate la
momentul rulării.
Din acest motiv, s-au introdus colecţiile care folosesc template-uri, care
sunt prezentate pe scurt ı̂n secţiunea următoare.
import java.util.*;
public class TestLista {
lista.add("test1");
lista.add("test2");
lista.add("test3");
for(String s:lista)
90 CAPITOLUL 5. MOŞTENIREA ÎN JAVA
System.out.println(s);
System.out.println("-----------------------------");
Iterator<String> iterator=lista.iterator();
while(iterator.hasNext())
{
String str=iterator.next();
System.out.println(str);
}
lista.remove("test2");
lista.add("test4");
System.out.println("-----------------------------");
for(int i=0;i<lista.size();i++)
{
String str=lista.get(i);
System.out.println(str);
}
System.out.println("-----------------------------");
}
Se poate testa faptul că ı̂n noua listă nu se mai pot introduce elemente
de alte tipuri decât cel declarat. Se poate observa, de asemenea, că nu mai
este nevoie de operaţii de cast pentru a obţine un anumit obiect din colecţie.
Deocamdată nu intrăm ı̂n mai multe amănunte relativ la folosirea colecţiilor
ı̂n Java, urmând a reveni ı̂n capitolul special dedicat colecţiilor Java.
lista.add(3);
lista.add(4);
for(int x:lista)
System.out.print(x+" ");
Acest cod este posibil deoarece atunci când este apelată metoda add()
parametrul transmis este automat convertit la un obiect de tip Integer (box-
ing).
Exact opusul se ı̂ntâmplă atunci când realizăm afişarea, ı̂n acest caz
putând afirma că avem de-a face cu conceptul de unboxing.
Cel mai bine se poate vedea acest lucru pe următorul cod:
int x = intObject;
//echivalent cu apelul int x = intObject.getValue();
Observaţie: La fel ca şi clasa String, clasa Integer este o clasă immutable
(nu ı̂şi poate schimba niciodată valoarea).
92 CAPITOLUL 5. MOŞTENIREA ÎN JAVA
CAPITOLUL 6
Unul din cele mai importante lucruri pentru orice programator care foloseşte
programarea obiect orientată este ı̂nţelegerea mecanismului care stă la baza
lucrului cu excepţii.
Evident, ar fi de dorit ca ı̂n practică să nu apară excepţii, dar după cum
fiecare dintre noi a observat, acest lucru este imposibil. Astfel, este important
să le tratăm corespunzător.
Mecanismul de lucru cu excepţii ı̂n Java seamănă foarte mult cu mecan-
ismul pe care deja ı̂l cunoaştem din C/C++.
În momentul ı̂n care apare o excepţie, cel care utilizează programul sau
programatorul trebuie să fie ı̂nştiinţat pentru a o putea trata corespunzător.
Astfel, aplicaţia trebuie să fie capabilă să facă unul din următoarele două
lucruri:
• Să termine aplicaţia ı̂ntr-un mod civilizat (cu salvarea eventual a lu-
crurilor care trebuie salvate, ı̂nchiderea conexiunilor care se folosesc ı̂n
aplicaţie)
• Să permită utilizatorului să se ı̂ntoarcă la o stare din care să poată
continua aplicaţia (de exemplu, dacă o citire nu reuşeşte, trebuie să mi
se permită să reiau acest pas sau să tratez altfel această situaţie).
Teoretic, chiar dacă acest lucru este mult mai complicat şi mai puţin ele-
gant, se pot ı̂ncerca şi alte abordări ale tratării excepţiilor decât cele folosite
ı̂n limbajul Java. Astfel, se poate de exemplu, ca fiecare metodă să returneze
un anumit cod, care să ne spună dacă metoda a reuşit sau nu.
93
94 CAPITOLUL 6. TRATAREA EXCEPŢIILOR ÎN JAVA
Cele mai importante clase care sunt folosite ı̂n Java pentru tratarea
excepţiilor sunt Throwable, Exception, Error şi RuntimeException. Ele
se află ı̂n relaţiile prezentate ı̂n figura 6.1.
Toate excepţiile sunt derivate din clasa Throwable care se comportă pen-
tru excepţii in modul ı̂n care Object se comportă pentru toate clasele. Avem
două tipuri derivate din Throwable şi anume Error (erori care apar din cauza
maşinii virtuale şi care ı̂n principiu sunt foarte greu de tratat; tot ceea ce
se poate face este să ı̂ncheiem programul ı̂ntr-un mod elegant) şi Exception
(erori care apar din cauze care nu ţin de implementarea maşinii virtuale).
Un caz mai deosebit de erori sunt cele de tipul RuntimeException care
sunt erorile unchecked de tipul Exception. Aceste erori sunt acele erori care
nu ţin de maşina virtuală ci mai degrabă de programator şi care dacă ar trebui
tratate de fiecare dată ar ı̂ngreuna foarte mult scrierea codului. Astfel,ar fi
greu dacă de fiecare dată când folosim un şir sau o matrice am verifica dacă
nu cumva se foloseşte un element din afara matricii.
Totuşi, ı̂n aceste situaţii se poate trata o eroare de acest tip.
Erorile de tip Exception care nu sunt de tipul RuntimeException sunt
erori care trebuie neapărat tratate ı̂ntr-un fel de programator (checked er-
rors).
Noua clasă pentru tratarea unei excepţii poate fi derivată şi dintr-o altă
clasă de tipul excepţie.
Dacă dorim crearea unei excepţii unchecked derivăm clasa excepţie din
RuntimeException sau dintr-o clasă derivată din aceasta:
96 CAPITOLUL 6. TRATAREA EXCEPŢIILOR ÎN JAVA
try {
instr1;
instr2;
....
}
catch(Exception1 e){
//tratare exceptie 1
}
catch(Exception2 e){
//tratare exceptie 2
}
finally{
//aici am cod care se executa mereu
//indiferent daca a aparut exceptie sau nu
}
Dacă ı̂ntre instrucţiunile din blocul try nu apare nici o eroare atunci se
execută toate aceste instrucţiuni după care se execută toate instrucţiunile
din blocul finally.
Dacă la un moment dat apare eroare când se execută o instrucţiune ı̂n
blocul try atunci execuţia instrucţiunilor din acel bloc se opreşte şi se execută
instrucţiunile doar din primul catch care prinde eroarea potrivită, după care
se execută instrucţiunile din blocul finally.
După cum se poate observa se poate să avem mai multe blocuri catch
dar un singur bloc finally. Trebuie remarcat, de asemenea, că blocul finally
poate lipsi.
În momentul ı̂n care scriem cod ı̂n care la un moment dat poate apărea o
excepţie de tipul ”checked” putem să rezolvăm problema ı̂n unul din următoarele
moduri:
• Dacă nu ştim cum să tratăm eroarea şi vrem ca ea să fie tratată ı̂n
altă parte, putem să aruncăm eroarea mai departe. Pentru acest lucru
trebuie să specificăm că acea metodă poate arunca eroarea respectiva
ı̂n clauza throws.
• Dacă nu ştim cum să tratăm eroarea dar vrem să aruncăm mai departe
o altă eroare, putem folosi un bloc try-catch ı̂n care pe ramura de catch
corespunzătoare excepţiei să aruncăm o altă excepţie.
• Nu toate excepţiile trebuie tratate ı̂n blocuri catch. În unele situaţii
este de dorit să se arunce mai departe acea excepţie.
6.6 Exemple