Sunteți pe pagina 1din 343

Programare Java

Sl. dr. ing. Raul Robu


www.aut.upt.ro/~rraul

2020-2021, Semestrul 1
1. Organizarea activităţii

1.1 Ce vom studia?


 Aspecte generale legate de limbajul Java
 Constante, variabile în limbajul Java, clase înfăşurătoare, tablouri, şiruri de caractere, operatorii limbajului, instrucţiuni
(for, while, if, …)

 Aspecte legate de orientarea pe obiecte


 Clase, obiecte, constructori, modificatori de acces, moştenirea, conversii de tip, supraîncărcarea metodelor,
redefinirea metodelor, variabile şi metode statice, clase abstracte şi interfeţe, încapsularea, etc

 Colecţii de obiecte
 Interfeţe (Collection, Set, List, etc), implementări (HashSet, ArrayList, etc) şi algoritmi (pentru ordonarea colecţiilor,
dereminarea valorii minime, căutarea unui element)

 Tratarea excepţiilor
 Mecanismul de emitere-captare a excepţiilor, Instrucţiunea throw, clauza throws, care tipuri de excepţie vor apărea
într-o clauză throws, blocurile try-catch, secvenţa finally, excepţii predefinite ale limbajului Java

 Interfeţe grafice (Swing)


 Lucrul cu diverse componente (JFrame, JLabel, JTextField, JPanel, JButton, JComboBox, JCheckBox, JRadioButton,
JList, JMenu, JTabbedPane, JToolBar)
 Amplasarea controalelor în cadrul containerelor (în poziţii absolute sau cu ajutorul gestionarilor de componente
BorderLayout, FlowLayout, GridLayout, BoxLayout, etc)
 Lucru cu evenimente

 Java Database Connectivity (JDBC) si MySQL


 Creearea unei baze de date MySQL, conectarea din Java la MySQL, preluarea datelor, execuţia unor comenzi SQL
select, insert, update, delete, actualizări programatice
2
 Servleturi
 Aplicații web în Java pe partea de server, structura unui servlet, ciclul de viată, clasa HttpServlet, crearea unui servlet
simplu, testarea funcţionării serverului web Apache Tomcat, rularea proiectului Web, adnotaţia @WebServlet, fişierul
web.xml, lucru cu sesiuni, lucru cu obiecte de tip ServletContext, servleturi şi JDBC

 Procesarea documentelor XML


 SAX – Simple API for XML Parsing, DOM – Document Object Model

 Dezvoltarea aplicaţiilor Android


 Componentele unei aplicaţii Android, accesarea resurselor aplicaţiei, lucrul cu evenimente, notificări, harta și baza de
date locală

 Caracteristicile Java 8
 Metode implicite în interfețe, interfețe funcționale, expresii Lambda, referințe la metode, stream API, date time
API

 Fire de execuție
 Crearea firelor de executie, sincronizarea acestora, modelul producator consumator, etc

1.2 Aplicaţii laborator


 Aplicaţii consolă:

3
 Aplicaţii cu interfaţă grafică:

 Aplicaţii cu baze de date:

 Servleturi:

4
1.3 Instrumentele software necesare

 JDK (Java Development Kit) for Windows


http://www.oracle.com/technetwork/java/javase/downloads/index.html

 Eclipse IDE for Java EE Developers


www.eclipse.org/downloads

 MySQL
http://dev.mysql.com/downloads/installer/

 Apache Tomcat
http://tomcat.apache.org/

 Android Studio
https://developer.android.com/studio/index.html

5
1.4 Orarul activităţilor online

1.5 Prezenţa (curs, laborator)

1.6 Evaluarea

 Laborator – sustinere orală a temelor

 Examenul – grilă online

 Nota finală = (nota laborator + 2 * notă examen) / 3


2. Istoricul şi caracteristicile limbajului Java
Istoric
 Limbajul Java a fost generat în cadrul proiectului Green al companiei Sun Microsystems, proiect
lansat în 1991 şi condus de James Gosling

 Prima implementare JDK (Java Development Kit) a fost lansată în 1995 (versiunile alpha şi beta) şi
urmărea principiul "Write Once, Run Anywhere“ . JDK 1.0 a fost lansată în 1996

 Sun Microsystems a fost preluată de Oracle Corporation în 2009

 Versiuni de Java:
 Java SE (Java Standard Edition)
 Java EE (Java Enterprise Edition)
 Java ME (Java Micro Edition)

Caracteristici
 Traducere în executabil în 2 faze:
 Compilarea: text Java -> cod de maşină virtuală Java (“byte code” sau cod intermediar)
 Interpretarea: cod de maşină virtuală Java - > cod de maşină de execuţie efectivă, fizică

7
 Are un mare grad de independenţă de platformă

 Este orientat pe obiecte

 Prezintă câteva simplificări în raport cu C++:


 nu admite moştenirea multiplă,
 nu operează cu pointeri, ci cu referinţe, asupra cărora se pot efectua doar operaţii de
atribuire, nu şi operaţii aritmetice,
 Efectuează automat dezalocarea memoriei ocupate de obiecte care nu mai sunt folosite cu
ajutorul unei componente numite “Garbage collector”

 Este concurent

 Este distribuit - Programele Java pot îngloba, alături de obiecte locale, obiecte aflate la distanţă,
în mod normal în reţea, inclusiv în Internet. În Java, sunt respectate protocoalele de reţea FTP,
HTTP, etc

 Este dinamic şi robust. În Java, alocarea memoriei este prin excelenţă dinamică –adică: nu se
face la compilare / linkeditare, ci în execuţie- şi orice alocare este precedată de verificări.

 Este sigur. Înainte ca interpretorul Java să execute codul intermediar, acesta este supus unor
verificări de tipul: nedepăşirea stivei, etc.

8
3. JDK – Java Development Kit
 compilatorul Java: javac

 interpretorul de cod intermediar: java (este specific pentru fiecare maşină ţintă)

 un depanator: jdb

 un generator de fişiere header pentru integrarea unor funţii scrise în C: javah

 un vizualizator pentru aplicaţiile Java conţinute în pagini HTML: appletviewer

 un generator de documentaţie: javadoc

 un dezasamblor pentru fişiere .class: javap

 un utilitar pentru generarea de arhive: jar

 etc
9
4. Pachete Java
 java.lang - conţine clasele de bază (clasa Object, clasele înfăşurătoare, clase pentru tratarea
excepţiilor, clase pentru lucru cu fire de execuţie, etc)

 java.io - conţine clase pentru intrări / ieşiri generice şi accesarea fişierelor

 java.math - conţine clase pentru operaţii matematice

 java.net - conţine clase pentru accesarea reţelei

 java.util – conţine clasele necesare pentru lucrul cu colecţii de obiecte şi alte clase cu rol utilitar

 javax.swing - conţine clase suport pentru dezvoltarea interfeţelor grafice

 java.rmi - conţine clase suport pentru apelul unor metode ale unor obiecte aflate la distanţă

 java.security - conţine clase suport pentru autentificare, criptare, etc.

 java.sql - conţine clase suport pentru lucrul cu baze de date

 javax.servlet – conţine clasele suport pentru lucrul cu servleturi

 javax.xml – conţine clasele suport pentru procesarea documentelor XML

 Ş.a
10
5. Medii integrate de dezvoltare pentru Java
 Eclipse (proiectat iniţial de IBM, dar aflat acum în stadiu de open source)

 IntelliJ (dezvoltat de JetBrains)

 NetBeans (iniţial dezvoltat de Charles University in Prague)

 Sun Java Studio (oferit de Sun Microsystems)

 Visual Age for Java (oferit de IBM)

 JBuilder (oferit de Borland)

 Probabil că, la ora actuală, cel mai popular dintre toate acestea este Eclipse.

La dezvoltarea aplicaţiilor practice vom folosi Eclipse 2019-09


pentru dezvoltatori de aplicații Java EE

11
2. Elementele de bază ale
limbajului Java

Sl. dr. ing. Raul Robu


www.aut.upt.ro/~rraul

2020-2021, Semestrul 1
2.1 Alfabetul Java
 Unicode - 32 768 caractere codificate pe 2 octeţi
 Primele 256 coincid cu cele 256 ale standardului ASCII
 Afişare caractere cu \uxxxx, unde xxxx=valoare hexa
 System.out.print('\u0061'); //afişează a pe ecran

2.2 Cuvintele rezervate ale limbajului Java

2
2.3 Identificatorii în limbajul Java
 secvenţe de caractere Unicode de lungime nelimitată, dintre care primul este literă,
iar celelalte sunt litere sau cifre

2.4 Constantele în limbajul Java


 constante numerice
 constante întregi
 constante reale
 constante booleene
 constante caracter
 constante şir

 Constante întregi
 Octale – precedate de cifra 0, formate din cifre de la 0 la 7
 Zecimale – prima cifra diferită de 0, iar restul cifre de la 0 la 9
 Hexazecimale – precedate de grupul de caractere 0x, formate din cifre de la 0 la 9 şi/sau litere de la ‘a’ la ‘f’
 Dacă înşiruirile respective nu sunt urmate de niciun sufix, atunci constantele în cauză se reprezintă intern pe
4 octeţi. Dacă înşiruirile respective sunt urmate de sufixul ”L”, atunci constantele în cauză se reprezintă
intern pe 8 octeţi. În ambele cazuri, reprezentarea internă este ”complement de doi”.

3
 Exemple de constante întregi
023 notaţie octală, reprezentând constanta 19, exprimată intern pe 4 octeţi
23 notaţie zecimală, reprezentând constanta 23, exprimată intern pe 4 octeţi
0x15 notaţie hexazecimală, reprezentând constanta 21, exprimată intern pe 4 octeţi
0xffffffff notaţie hexazecimală, reprezentând constanta -1, exprimată intern pe 4 octeţi
023L notaţie octală, reprezentând constanta 19, exprimată intern pe 8 octeţi
23L notaţie zecimală, reprezentând constanta 23, exprimată intern pe 8 octeţi
0x15L notaţie hexazecimală, reprezentând constanta 21, exprimată intern pe 8 octeţi
0xffffffffL notaţie hexazecimală, reprezentând constanta -1, exprimată intern pe 8 octeţi

 Exemple de constante reale

1.4f – dacă se pune sufixul f variabila de reprezintă pe 4 octeţi


1. - dacă se pune sufixul d sau nu se pune nici un sufix variabila se reprezintă pe 8 octeţi, deci
trebuie declarată de tip double
.723
1.2e15f
1.e-7
-27e+12
-43e-6d
4
float f=2.3; //GRESIT! Eroare de compilare, pentru ca se incearca atribuirea unei valori
// double unei variabile float

float f=2.3f; //CORECT


float f=(float)2.3; //CORECT

 Constante caracter
 System.out.println('A');
 System.out.println('\u0041'); //secventa escape
 System.out.println(‘\t’);//secventa escape speciala carcaterul t reprezintă tab, alte caractere posibile sunt:
 b - pentru backspace
 n - pentru line feed
 f - pentru form feed
 r - pentru carriage return
 “ - pentru double quote
 ‘ - pentru single quote
 \ - pentru backslash

 Constantele şir
- secvenţe de caractere Unicode delimitate de ghilimele
- Exemplu:
 System.out.println(”Automatica si Calculatoare”);
 System.out.println(”Vechiul nume: Universitatea \”Politehnica\” din Timisoara”);

 Constante booleene {true,false} 5


2.5 Variabile în limbajul Java
byte
2.5.1 Tipuri de variabile în Java
short

intregi
int

long
aritmetice

float

reale

double
primitive logice boolean

Variabile caracter char

Adrese de obiecte
referinţă

Adrese de tablouri

6
2.5.2 Spaţiul ocupat şi domeniul de valori al variabilelor

7
2.5.3 Clasele înfăşurătoare în Java
 Clasele înfășuratoare (wrapper classes) sunt clase asociate tipurilor primitive.

 Clasele înfăsurătoare sunt: Byte, Short, Integer, Long, Float, Double, Boolean, Character

 Obiectul clasei înfășurătoare conține o valoare a tipului primitiv asociat și dispune de metode care
permit diverse prelucrări asupra valorii primitive asociate

 În continuare se exemplifică pentru clasa înfășurătoare Integer:

 Instanțierea:

Integer x=new Integer(3);


Integer y=new Integer(3);
//SAU
Integer x=3; //autoboxing
int z=3;
Integer y=z; //autoboxing
int w=x; //unboxing

8
 Conversia automată pe care compilatorul Java o face între tipurile primitive și
obiectele corespunzătoare claselor lor înfășurătoare se numește autoboxing

 Conversia unui obiect al clasei înfășurătoare la tipul primitiv asociat se numește


unboxing

 În continuare se exemplifică o parte din metodele care pot fi utilizate în operații cu


obiecte Integer

System.out.println( x.compareTo(y) ); // afiseaza 0. Returneaza 1 cand x>y si -1 cand x<y


System.out.println( x.equals(y) ); // afiseaza true

System.out.println( Integer.toString(15) ); // afiseaza 15. Conversie din intreg in String

System.out.println( Integer.toBinaryString(15) ); // afiseaza 1111. returneaza un String cu


//reprezentarea binara a intregului transmis ca si parametru

Integer intVar2;
intVar2=Integer.valueOf("123"); //conversie din String in Integer

int intVar3;
intVar3=Integer.parseInt("123"); //conversie din String in int

9
2.5.4 Tablourile în Java
 Crearea şi iniţializarea tablourilor
 int[] alfa=new int[5];
 alfa[0]=100;
 alfa[1]=200;
 ….
 alfa[4]=500;

SAU

 int[] alfa={100,200,300,400,500};

 în Java se verifică depășirea capacității tablourilor. Încercarea de a accesa un


element din afara spațiului alocat va genera excepția
ArrayIndexOutOfBoundsException

 Lungimea unui tablou  alfa.length

10
 Copierea tablourilor
 Copierea tablourilor se poate realiza cu ajutorul metodei statice arraycopy() din clasa
System sau cu ajutorul metodei statice copyOf() din clasa Arrays
public static void arraycopy (Object referinţăTablouSursă,
int indiceÎnSursă,
Object referinţăTablouDestinaţie,
int indiceÎnDestinaţie,
int numărElementeDeCopiat)

package exemplu;

public class MainApp2 {


public static void main(String[] args) {
int []a= {1,2,3};
int []b= {4,5,6};

b=a;//b indica spre aceeasi referinta ca si a. Nu se face copiere de elemente

a[0]=99;
System.out.println("a[0]="+a[0]+" b[0]="+b[0]);//Output a[0]=99 b[0]=99

11
int []c= {1,2,3};
int []d=new int[c.length];
System.arraycopy(c, 0, d, 0, c.length);
//valorile din zona de memorie a lui c se copiaza in zona de memorie a lui d
c[0]=99;
System.out.println("c[0]="+c[0]+" d[0]="+d[0]);
}
}

 Ordonarea tablourilor

 Ordonarea tablourilor de tipuri primitive se poate realiza cu ajutorul metodei statice


sort() din clasa Arrays, care utilizează pentru ordonare algoritmul Quicksort

 Metoda poate fi utilizată și pentru ordonarea vectorilor de obiecte în ordine


crescătoare naturală. Dacă obiectele nu au o ordine crescătoare naturală trebuie
implementat un comparator

12
package exemplu;

import java.util.Arrays;

public class MainApp3 {


public static void afis(int []v) {
for(int i=0;i<v.length;i++)
System.out.print(v[i]+" ");
System.out.println();
}
public static void main(String[] args) {
int []a= {5,2,1,7,9,3,0};
afis(a);
Arrays.sort(a);
afis(a);

int poz=Arrays.binarySearch(a, 5);//doar ptr tablou ordonat


System.out.println(poz>=0?"Gasit pe pozitia "+poz:"Nu se gaseste!");
int []b=Arrays.copyOf(a, a.length);
afis(b);
}
}
13
 Tablouri bidimensionale
 Tablourile de tablouri (tablouri bidimensionale) pot avea număr diferit de elemente pe
fiecare rând

String[][] persoane = { { "Dl. ", "Dna. ", "Dsra. " }, { "Popescu", "Georgescu" } };

for (int i = 0; i < persoane.length; i++) { //accesam randurile


for (int j = 0; j < persoane[i].length; j++){ //accesam elementele de pe randuri
System.out.print(persoane[i][j] + " ");
}
System.out.println();
}

14
2.5.5 Şirurile de caractere în Java
 Clasa String
 Crearea și inițializarea String-urilor
String sir; //sau: String sir=”hello!”;
sir=”hello!”;

//sau:
String sir=new String(”hello!”);

//sau:
char[] helloArray={'h','e','l','l','o','!'};
String sir=new String(helloArray);
System.out.println(sir); // afişează: “hello!”
 Lungimea unui String
System.out.println(sir.length()); // afişează:6

 Concatenarea şirurilor
 cu operatorul +
 cu metoda concat() a clasei String

String s1=“afara”;
String s2=“ploua”;

System.out.println(s1+” ”+s2); // afişează: afara ploua


System.out.println(s1.concat(“ ”).concat(s2)); // afişează: afara ploua 15
 Convertirea şirurilor în numere
 Fiecare clasă înfăşurătoare dispune de o metodă ValueOf care converteşte un şir într-un
obiect din clasa respectivă
 Ficare clasă înfăşurătoare dispune de o metodă parse…, care converteşte un şir într-o
variabilă de tipul elementar corespunzător clasei înfăşurătoare în cauză (vezi subcapitolul
2.5.3).

 Convertirea numerelor în şiruri


 Fiecare clasă înfăşurătoare dispune de o metodă numită toString(), care converteşte tipul
primitiv corespunzător într-un şir

int i=3;
double d=7;
String s1=Integer.toString(i);
String s2=Double.toString(d);

 Obţinerea caracterului dintr-o anumită poziţie a unui şir


String alfa="Luna tu stapana marii, pe a lumii bolta luneci";
char a=alfa.charAt(9); // variabila a va lua valoarea “t”

 Utilizarea unui delimitator pentru extragerea de subșiruri


String alfa=“Exemplu cu split";
String[] s=alfa.split(“ ”);
System.out.println(s[0]); //afiseaza Exemplu 16
 O alternativă la utilizarea metodei split() este utilizarea clasei
StringTokenizer care permite specificarea delimitatorului ori la
instanțierea obiectului, ori ca și parametru a metodei nextToken()
StringTokenizer st = new StringTokenizer("Exemplu cu StringTokenizer"," ");
while (st.hasMoreTokens()) {
System.out.println(st.nextToken());
}

 Obţinerea de subşiruri din şiruri de date


String alfa="Luna tu stapana marii, pe a lumii bolta luneci ";
System.out.println(alfa.substring(8,15)); //Afişează stapana
System.out.println(alfa.substring(8)); //Afişează stapana marii, pe a lumii bolta luneci

 Generarea şirului de minuscule, respectiv a şirului de majuscule


corespunzătoare unui şir dat
System.out.println(alfa.toUpperCase());
System.out.println(alfa.toLowerCase());

17
 Căutarea unui caracter într-un şir dat
System.out.println(alfa.indexOf('a')); //Afiseaza 3
System.out.println(alfa.indexOf('a',4)); //Afiseaza 10
System.out.println(alfa.lastIndexOf('a')); //Afiseaza 38
System.out.println(alfa.lastIndexOf('a',37)); //Afiseaza 26

 Căutarea unui subşir într-un şir dat


System.out.println(alfa.indexOf("un")); //Afiseaza 1
System.out.println(alfa.indexOf("un",2)); //Afiseaza 41
System.out.println(alfa.lastIndexOf("un")); //Afiseaza 41
System.out.println(alfa.lastIndexOf("un",40)); //Afiseaza 1
System.out.println(alfa.contains("un")); //Afiseaza true

 Înlocuirea unui caracter într-un şir dat


System.out.println(alfa.replace ('a‘, ‘A‘)); //Afisează: LunA tu stApAnA mArii, pe A lumii boltA luneci

 Înlocuirea unui subşir într-un şir dat


System.out.println(alfa.replaceFirst (“un”, “UN”)); //LUNa tu stapana marii, pe a lumii bolta luneci
System.out.println(alfa.replaceAll (“un”, “UN”)); //LUNa tu stapana marii, pe a lumii bolta lUNeci

18
 Compararea şirurilor şi porţiunilor de şiruri
System.out.println(alfa.startsWith("Luna")); //true
System.out.println(alfa.startsWith("tu",5)); //true
System.out.println(alfa.endsWith("Luneci")); //false

String beta=“abcd”;

System.out.println(beta.compareTo(“abce”)); //-1
System.out.println(beta.compareToIgnoreCase("ABCD")); //0
System.out.println(beta.equals("ABCD")); //false
System.out.println(beta.equalsIgnoreCase("ABCD")); //true
System.out.println(beta.regionMatches(1, "aaaBCd", 3, 2));//false
System.out.println(beta.regionMatches(true,1, "aaaBCd", 3, 2));//true

Eliminarea spațiilor de la începutul și finalul șirului se poate realiza cu


funcția trim()

Valorile șirului inițial nu pot fi modificate. Funcții precum (trim(),


toUpperCase(), toLowerCase(), replace(), etc) returnează un String nou
cu valorile modificate, lasândul-l pe cel vechi nemodificat. Stringul este
imuabil (engl. immutable) 19
 Clasa StringBuffer

 Obiectele StringBuffer permit modificarea șirului de caractere. De exemplu


modificarea unui character se poate realiza cu ajutorul metodei setCharAt().
Stergerea unui character cu ajutorul metodei deleteCharAt()
String s="abc";
StringBuffer sb=new StringBuffer(s);
sb.setCharAt(1, 'Z');
System.out.println(sb); //output: aZc
sb.deleteCharAt(0);
System.out.println(sb); //output: Zc

 Clasa StringBuffer dispune de metode pentru:


 Adăugarea unui tip primitiv la un obiect StringBuffer – metoda append()
 Stergerea unei bucati dintr-un sir de caractere – metoda delete()
 Inserarea unui subsir intr-un sir – metoda insert()
 Inversarea literelor sirului de caractere – metoda reverse()
 Etc
 Metodele publice ale clasei StringBuffer sunt sincronizate, ceea ce inseamna ca sunt
sigure in aplicatii cu fire de executie, dar costul pentru acest lucru este performanta.
 Clasa StringBuilder dispune de metode similare cu StringBuffer, doar ca metodele
sale nu sunt sincronizate si aceasta aduce un plus de viteza de executie.
 StringBuilder este recomandat in aplicatii fara fire de executie, iar StringBuffer in
20
aplicatii cu fire de executie
2.6 Operatorii limbajului Java

21
22
Operatorii limbajului Java, în ordinea puterii de precedenţă

 Pentru stabilirea ordinii dorite de realizare a operatiilor in expresii, se


recomanda utilizarea parantezelor rotunde

23
2.7 Instrucţiile limbajului Java
 Instrucţia if-then-else
if(expresieLogica){ //secvenţa de instrucţii dintre aceste acolade se execută doar dacă expresieLogica are valoarea true

}
else{ //secvenţa de instrucţii dintre aceste acolade se execută doar dacă expresieLogica are valoarea false
...
}

 Instrucţia switch
class SwitchDemo{
public static void main(String[] args){
int month=8;
switch(month){ //se va executa doar linia de instrucţii pt. care parametrul etichetei caseeste egal cu valoarea variabilei month
case 1: System.out.println("January"); break;
case 2: System.out.println("February"); break;
case 3: System.out.println("March"); break;
case 4: System.out.println("April"); break;
case 5: System.out.println("May"); break;
case 6: System.out.println("June"); break;
case 7: System.out.println("July"); break;
case 8: System.out.println("August"); break;
case 9: System.out.println("September"); break;
case 10: System.out.println("October"); break;
case 11: System.out.println("November"); break;
case 12: System.out.println("December"); break;
default: System.out.println("Invalid month.");break;
}
}
}
24
 Instrucţia while
while(expresieLogica){ // verifică dacă expresieLogica=true şi execută instrucţiile dintre
// acolade cât timp această condiţie este îndeplinită
...
}

 Instrucţia do..while
do{ // execută instrucţiile dintre acolade şi apoi verifică dacă expresieLogica=true;
// în caz afirmativ reia totul, altfel treci la instrucţia următoare
...
} while(expresieLogica);

 Instrucţia for
class ForDemo{ class EnhancedForDemo{
public static void main(String[] args){ public static void main(String[] args){
for(int i=1; i<11; i++){ int[] numbers={1,2,3,4,5,6,7,8,9,10};
System.out.println(“Contorul este: " + i); for(int item:numbers){
} System.out.println("Count is: " + item);
} }
} }
}
25
 Instrucţia break
class BreakDemo{
public static void main(String[] args){
int[] arrayOfInts={32,87,3,589,12,1076,2000,8,622,127};
int searchfor=12;
int i;
boolean foundIt=false;
for(i=0;i<arrayOfInts.length;i++){
if(arrayOfInts[i]==searchfor){
foundIt=true;
break;
}
}
if(foundIt){
System.out.println("Found "+searchfor+" at index "+i);
}
else{
System.out.println(searchfor+" not in the array");
}
}
}

26
 Instrucţia continue
class ContinueDemo{
public static void main(String[] args){
String searchMe="Un vultur sta pe pisc c-un pix in plisc";
int max=searchMe.length();
int numPs=0;

for(int i=0;i<max;i++){
if(searchMe.charAt(i)!='p')
continue;
numPs++;
}
System.out.println("Found "+numPs+" p's in the string.");
}
}

 Instrucţia return
 În metodele de tip void se foloseşte return;
 În metodele de alt tip decât void se foloseşte return expresie, unde unde expresie trebuie să
fie în consonanţă cu tipul folosit la declararea metodei

27
3. Orientarea pe obiecte

Sl. dr. ing. Raul Robu


www.aut.upt.ro/~rraul

2020-2021, Semestrul 1
3.1 Noţiuni generale

 Un program este format din una sau mai multe clase.

 O clasă reprezintă descrierea unei mulţimi de obiecte care au aceeaşi structură şi


acelaşi comportament. Ca urmare, într-o clasă vom găsi definiţiile datelor şi ale
operaţiilor ce caracterizează obiectele clasei respective

 Declararea unei clase este similară cu declararea unui nou tip de date

 Majoritatea conceptelor legate de orientarea pe obiecte sunt similare cu cele din


C++:
 Încapsularea – accesul la variabilele membre ale unui obiect se poate realiza doar cu
ajutorul metodelor obiectului
 Supraîncărcarea – mai multe metode pot avea acelaşi nume dar o signatură diferită
(numărul şi tipul parametrilor diferă de la o metodă la alta)
 Moştenirea - constă în extinderea comportamentului unei clase existente prin definirea
unei clase noi, care moşteneşte conţinutul primei clase, adăugând la acesta elementele
specifice ei
 Legarea dinamică - asocierea unui apel de metodă cu funcţia ce trebuie apelată se
numeşte "legare" ("Binding"). Legarea se poate face la compilare (“legare timpurie” sau
statică) sau la execuţie ("legare târzie" sau "legare dinamică"). În Java pentru metodele
finale sau statice legarea este timpurie iar pentru celelate legarea este dinamică

 Clasele formează o ierarhie, care are la rădăcină clasa Object


 Orice construcţie are un tip stabilit la momentul compilării 2
 Nu există tipuri implicite pentru metode şi nici posibilitatea ca la apelul unei
metode să fie omişi anumiţi parametri (“strong typed”)

 Polimorfismul este abilitatea unui obiect de a lua mai multe forme. Una din cele
mai frecvente utilizări ale polimorfismului în POO este când o referinţă a unei clase
de bază este utilizată pentru a referi un obiect al clasei derivate

 O funcţie sau operator este supraîncărcart dacă execută operaţii diferite în


contexte diferite (de exemplu operatorul + care poate fi adunare sau concatenare
de şiruri)

 O clasă poate fi extinsă prin redefinirea unor metode şi / sau adăugarea unor
metode noi

 O metodă care redefineşte o alta din clasa de bază are acelaşi nume, acelaşi tip
de rezultat sau o subclasa a acestuia şi aceeaşi listă de parametri

 În cazul în care o clasă derivată are variabile membru cu acelaşi nume ca şi clasa
de bază nu este vorba de o redefinire, cei doi parametri coexista si pot si referiţi
distinct

 Final – blochează redefinirea

3
 Moştenirea
 Permisă doar moştenirea simplă, spre deosebire de C++
 Clasa moştenită – superclasă sau supraclasă sau clasă de bază
 Clasa care realizează extinderea- subclasă sau clasă derivată

 Clasa reprezintă un tipar după care se creează un obiect prin instanţiere

 Un constructor este o metodă care acelaşi nume cu clasa şi este apelată implicit la
crearea obiectului

 Un obiect reprezintă încapsularea unor date asupra cărora nu se pot executa


decât anumite operaţii numite metode

 Signatura unei metode – reprezintă tipul returnat de metoda, numele acesteia şi


lista sa de parametri

 Interfaţă unui obiect –modul în care un obiect este cunoscut din exterior

 Rădăcina ierarhiei de clase, clasa Object conţine o serie de metode finale care
vor fi moştenite de orice clasă definită în Java

4
 Unele dintre metodele clasei Object nu sunt scrise în Java, deoarece implementarea
lor depinde de maşina pe care se face execuţia; despre aceste metode se spune că
sunt native

 În momentul în care o clasă este necesară (pentru că se instanţiază un obiect de tipul


respectiv sau pentru că este apelată o metodă statică din clasa respectivă) aceasta se
va încărca în maşina virtuală. Încărcarea este realizată de către un obiect numit „class
loader".

 Definiţia clasei Object este conţinută în biblioteca java.lang şi va fi încărcată de pe


maşina pe care se execută programul care o utilizează.
 O parte din metodele clasei Object sunt prezentate în continuare:

 public boolean equals (Object obj)


 public final Class getClass ()
 public String toString ()
 protected Object clone() throws CloneNotSupportedException
 public final void notify () throws IllegallMonitorStateException
 public final void notifvAll () throws IllegalMonitorStateException
 public final void wait (long timeout) throws InterruptedException
 public final void wait() throws InterruptedException

5
3.2 Moştenirea
 Extinderea comportamentului unei clase existente prin definirea unei clase noi, care
moşteneşte conţinutul primei clase, adăugând la acesta elementele specifice ei

 Clasa moştenită se numeşte clasă de bază, supraclasă sau superclasă, iar clasa care
realizează extinderea se numeşte subclasă, clasă derivată, sau clasă descendentă.
class nume_subclasa extends nume_supraclasa
{
//conţinut specific subclasei
}
class Poligon {
protected double[ ] laturi;
public Poligon(int n) { laturi = new double[n] }
public double perimetru( ) {
double s=0;
for(int i=0;i<laturi.length;i++) s+=laturi[i];
return s;
}
}
class Dreptunghi extends Poligon {
public Dreptunghi(double L, double h){
super(4);
laturi[0] = laturi[2] = L;
laturi[1] =laturi[3] = h;
}
public double aria( ){
return laturi[0]*laturi[1];
}
} 6
Clase derivate din clasa Object
în mod explicit:
class Chitara extends Object{
public String culoare;
public int nr_corzi;
}
în mod implicit:
class Chitara {
public String culoare;
public int nr_corzi;
}

Chitara c; //variabilă iniţializată cu null



c=new Chitara();

sau
Chitara c=new Chitara();

Accesul la câmpurile obiectului creat


c.culoare=“neagra”;
c.nr_corzi=12;
7
3.3 Constructori
 Metode speciale apelate la crearea obiectelor

 Au acelaşi nume ca şi numele claselor în care se găsesc şi nu returnează nimic

 Un constructor nu poate fi apelat din alte metode, cu excepţia altui constructor

 În mod implicit prima instrucţiune dintr-un constructor este apelul constructorului fără
parametri ai supercasei. Singura abatere de la această regulă are loc atunci când
prima instrucţiune din constructor este un apel explicit la alt constructor al clasei sau
la un constructor al supraclasei.

 Dacă într-o clasă nu se defineşte nici un constructor, se consideră ca există un


constructor fără argumente (numit şi constructor implicit) al cărui corp conţine numai
un apel al constructorului implicit al supraclasei

 Dacă într-o clasă se defineşte cel puţin un constructor, constructorul implicit fără
argumente nu mai este furnizat automat. Constructorii nu pot avea atributele abstract,
native, static, synchronized sau final

8
class Chitara {
private String culoare;
private int nr_corzi;
public Chitara(){
culoare=“Neagra”;
nr_corzi=6;
}
public Chitara(String culoare,int nr_corzi){
this.culoare=culoare;
this.nr_corzi=nr_corzi;
}
}
SAU:
class Chitara {
private String culoare;
private int nr_corzi;
public Chitara(String culoare,int nr_corzi){
this.culoare=culoare;
this.nr_corzi=nr_corzi;
}
public Chitara(){
this (“Neagra”,6);
}
}

 Apelul constructorului supraclasei se poate face în felul următor:


super(lista_de_parametrii);
9
3.4 Modificatori de acces
 Principiul încapsulării presupune ca variabilele membre ale claselor să fie ascunse şi modificarea
lor să se poată face numai cu ajutorul metodelor definite în cadrul claselor respective, de
exemplu:
class Chitara {
private String culoare;
private int nr_corzi;

public Chitara(String culoare,int nr_corzi){


this.culoare=culoare;
this.nr_corzi=nr_corzi;
}
public Chitara() {
this (“Neagra”,6);
}

public String getCuloare () {


return culoare;
}
public void setCuloare (String culoare) {
this.culoare=culoare;
}
//….
}
 În Eclipse metodele de tip get set pot fi generate alegând opţiunea de meniu Source şi apoi
Generate Getters and Setters… atunci când cursorul se află în interiorul clasei de interes 10
 public
 se poate aplica constructorilor, metodelor, variabilelor membre și
claselor
 o clasă publică poate fi importată într-un alt pachet decât cel în care
este declarată și utilizată acolo
 o clasă care nu are atributul public este vizibilă doar la nivelul
pachetului în care se află

 private
 se poate aplica constructorilor, variabilelor membre sau metodelor
 restrânge nivelul de vizibilitate al variabilelor membre sau a metodelor
care au acest atribut doar la nivelul clasei

 protected
 se poate aplica constructorilor, variabilelor membre sau metodelor
 metodele si variabilele membre protected sunt accesibile din metodele
clasei si din metodele subclaselor
 un obiect al unei clase care conține variabile si metode protected nu
dispune de acces la acestea din afara pachetului (accesul este permis
doar daca obiectul se gaseste în același pachet cu clasa)

11
package exemplu2;

class Test {
protected String camp_protected;
}
class MainApp {
public static void main(String []args) {
Test t=new Test();
t.camp_protected="acces permis";
}
}

 Modificatorul de acces implicit


 Când nu se specifică un modificator de acces, accesul este limitat la
nivelul pachetului. Variabilele membre sau metodele pentru care nu se
specifică un modificator de acces sunt accesibile din alte clase ale
aceluiași pachet
 In exemplul de mai jos clasa Test și clasa MainApp sunt în același
pachet (exemplu2)

12
package exemplu2;

class Test {
String camp;
}

class MainApp {
public static void main(String []args) {
Test t=new Test();
t.camp="Acces permis";
}
}

 Pentru ca o clasă să poată fi utilizată într-un alt pachet decât cel în


care este definită ea trebuie importată. În continuare, clasa OClasa din
pachetul exemplu3, importa clasa Test pentru a putea declara obiecte
de tip Test. Accesul la variabilele membre implicite nu este permis în
acest caz

13
package exemplu3;

import exemplu2.Test;

class OClasa{
void o_metoda() {
Test t=new Test();
//t.camp="Acces interzis";
}
}

14
3.5 Distrugerea obiectelor

 Nu cade în sarcina programatorului, ci în sarcina unei componente a maşinii virtuale


numita Garbage Collector

 Dacă spre un anumit obiect nu mai există nici o referinţă externă, în nici o funcţie
activă, acel obiect devine candidat la eliminarea din memorie

 Înainte de a elimina un obiect din memorie este apelată metoda finalize


protected void finalize() throws Throwable;

 În metoda finalize pot fi scrise instrucţiuni care să reactiveze obiectul, sau poate fi
afişat un mesaj care precizează că obiectul urmează să fie distrus

 La crearea unui obiect se apelează o ierarhie de constructori începând cu


constructorul clasei Object, iar la distrugerea lui se apelează un lanţ de metode
finalize

 Dacă există un cod care trebuie să se execute când un obiect nu mai este util atunci
acest cod trebuie scris într-o metodă care trebuie apelată în mod explicit pentru că nu
există garanţia că obiectul va fi distrus până se încheie programul 15
3.6 Conversii de tip
class Vapor{
private String denumire;
private int nr_membrii_echipaj;

public Vapor(String denumire, int nr_membrii_echipaj) {


this.denumire = denumire;
this.nr_membrii_echipaj = nr_membrii_echipaj;
}
@Override
public String toString() {
return denumire + ", "+ nr_membrii_echipaj;
}
}
class VasDeCroaziera extends Vapor{
private int nr_restaurante;
private int nr_piscine;
VasDeCroaziera(String denumire,int nr_membrii_echipaj,int nr_restaurante,int nr_piscine){
super(denumire,nr_membrii_echipaj);
this.nr_restaurante=nr_restaurante;
this.nr_piscine=nr_piscine;
}
@Override
public String toString() {
return super.toString()+", "+ nr_restaurante+ ", " + nr_piscine;
}
} 16
class MainApp {
public static void main(String[] args) {
Vapor v=new Vapor("Pescarus",10);
System.out.println(v);

VasDeCroaziera c=new VasDeCroaziera("Harmony of the Seas", 2100, 20, 23);


System.out.println(c);

v=c;
System.out.println(v.toString());

c=(VasDeCroaziera)v;
System.out.println(c.toString());
}
}

 În cazul în care un obiect primeşte o referinţă la un obiect al subclasei, nu este


necesară o conversie explicită prin cast
17
 Conversia prin operaţie de cast de la un obiect al superclasei la un obiect al subclasei
este posibilă doar dacă în momentul execuţiei supraclasa referă un obiect al
subclasei

 După ce un obiect al supraclasei a obţinut ca valoare o referinţă la un obiect al


subclasei, în cazul în care în clasa de bază există o metoda redefinită de clasa
derivata, atunci cea din clasa derivată va fi cea apelată

 Utilizarea mecanismului de conversie pe bază de cast se poate face numai între o


supraclasa şi subclasele sale (sau subclasele subclaselor sale)
 În general deoarece Object este rădăcina ierarhiei se poate executa ceva precum în
secvenţa următoare
Object o;
Ceva c=new Ceva();

o=c;

c=(Ceva) o;

 Dacă obiectul referit nu este de tipul corespunzător se va genera o excepţie


ClassCastException

 Pentru a verifica corectitudinea castului se recomandă verificarea apartenenţei


obiectului la clasa respectivă cu ajutorul operatorului instanceof 18
Floare f=new Floare(); //se considera clasa Iris derivate din clasa Floare
if (f instanceof Iris)
System.out.println("DA");
else
System.out.println("NU");

Iris c=new Iris();


if (c instanceof Floare)
System.out.println("DA");
else
System.out.println("NU");

3.7 Redefinirea metodelor


 dacă într-o clasă derivată se defineşte o metodă cu acelaşi nume şi signatură cu o
metodă din clasa de bază, spunem că metoda din clasa derivată o redefineşte pe cea
din clasa de bază (overriding)

 Din subclasă pot fi apelate ambele metode:


 exemplu() – apelează metoda din clasa derivată
 super.exemplu() –apelează metoda din clasa de bază 19
class Floare {
String nume;
Floare(){
nume=“Iris";
}
void afisareNume(){
System.out.println("Executie din supraclasa "+nume);
}
}

class FloareExtinsa extends Floare


{
String nume;
void setNume(String nume){
this.nume=nume;
}
void afisareNume(){
System.out.println("Executie din subclasa "+nume);
}
void afis(){
super.afisareNume();
afisareNume();
System.out.println("Nume curent="+this.nume+"\nNume din supraclasa="+super.nume);
}
}
class Test{
public static void main(String args[]){
FloareExtinsa f=new FloareExtinsa();
f.setNume("Garoafa");
f.afis();
}
}
20
3.8 Clase imbricate
 O clasă poate fi decarată în interiorul altei clase. Din clasa interioară se pot accesa
variabilele membre ale clasei exterioare chiar private fiind acestea

 Fișierul ClasaExterioara.java

package clase_imbricate;

public class ClasaExterioara {//Outer class


private int a=3;

class ClasaInterioara{//Inner class


private int b=4;
public void afisare(){
System.out.println("a="+a+", b="+b);
}
}
}

21
 Declararea obiectelor clasei interioare se face cu ajutorul obiectelor clasei exterioare,
precum in exemplul de mai jos

 Fișierul MainApp.java
package clase_imbricate;

public class MainApp {


public static void main(String[] args) {
ClasaExterioara o1=new ClasaExterioara();
ClasaExterioara.ClasaInterioara o2=o1.new ClasaInterioara();
o2.afisare();
}
}

22
3.9 Variabile şi metode statice. Secvenţe de cod statice.
Clase interioare statice
 Variabilele statice ale unei clase sunt variabilele globale ale clasei, adică variabile a căror valoare
poate să fie referită de orice obiect din clasa respectivă

 Pentru o variabilă membru statică se alocă memorie o singură dată indiferent de numărul de
obiecte de acel tip

 Variabilele şi metodele declarate static se consideră că aparţin clasei nu obiectelor clasei


respective, de aceea ele se apelează de obicei utilizând numele clasei:
NumeClasa.nume_variabila_statica
NumeClasa.nume_metodă_statica()

 O metodă statică poate să refere doar variabile sau metode statice, dar poate să fie apelată de
orice metoda a clasei

 Metodele statice sunt similare funcţiilor obişnuite din limbajul C

Class Aplicatie{
static final int versiune=3;
static int numarObiecte;

}
23
 versiune este similară unei constante întregi şi globale din limbajul C
 numarObiecte poate fi actualizată de orice obiect creat pentru a contoriza numărul total de obiecte
de tip Aplicatie
 exemplul de mai jos arată că pentru o variabilă membru statică se alocă memorie o singură dată
indiferent de numărul de obiecte de acel tip
public class DespreStatic {
int x;
static int y;

DespreStatic(){
x=0;
y=0;
}
}

class Test{
public static void main(String args[]){
DespreStatic t1=new DespreStatic();
DespreStatic t2=new DespreStatic();
t1.x=10;
t1.y=10;

t2.x=20;
t2.y=20;
System.out.println("t1.x="+t1.x+" t1.y="+t1.y);
System.out.println("t2.x="+t2.x+" t2.y="+t2.y);

System.out.println(); //sau mai bine


System.out.println("t1.x="+t1.x+“DespreStatic.y="+DespreStatic.y);
System.out.println("t2.x="+t2.x+“DespreStatic.y="+DespreStatic.y);
} 24
}
Secvenţe de cod statice
static{
secventa de cod
}
 Secvenţe de cod static se pot declara numai în afara metodelor
 Aceste secvenţe se execută în momentul în care se referă clasa care le conţine
class Floare {
protected int numarPetale;
protected String nume;
Floare(int numarPetale,String nume){
this.numarPetale =numarPetale;
this.nume=nume;
}
public void afiseaza(){
System.out.println(nume+" are "+numarPetale+ " petale ");
}
static{
System.out.println(“Zona statica");
}
}

class Exemplu{
static{
Floare g=new Floare(7,“Laleaua");
g.afiseaza();
}
public static void main(String args[]){}
} 25
Clase interioare statice
 Clasele interioare pot fi statice. În acest caz instantierea unui obiect al clasei
interioare nu mai necesita un obiect al clasei exterioare (vezi exemplul următor). Din
clasa interioara pot fi accesati membrii statici ai clasei exterioare

 Fișierul Outer.java

package clase_imbricate;

public class Outer {


static class Static_Inner {
public void display() {
System.out.println("Metoda display() din clasa interioara");
}
}

public static void main(String args[]) {


Outer.Static_Inner inner = new Outer.Static_Inner();
inner.display();
}
}

26
3.10 Clase abstracte şi interfeţe
 O clasă abstractă este o supraclasă pentru o ierarhie de clase

 O clasă abstractă conţine câmpuri şi metode normale (pentru care se specifică o


implementare dar şi modele de metode (metode abstracte) care urmează să fie
implementate în mod obligatoriu de clasele normale care extind clasa abstractă)

 O interfaţă este doar o colecţie de constante şi metode abstracte care urmează să fie
implementate

Clase abstracte
abstract class Aplicatie extends Floare{
abstract void prelucrare();
}

 O metodă finală nu mai poate să fie redefinită

 O metodă cu atributul abstract trebuie trebuie să fie redefinită pentru a fi utilizată


printr-o metodă care nu are acelaşi atribut

 O clasă abstractă poate să fie extinsă de o altă clasă abstractă sau de o clasă 27
normală
 O metodă abstractă se află în mod obligatoriu într-o clasă abstractă sau într-o
interfaţă

 Deoarece metodele abstracte reprezintă numai modele de metode nu se pot face


instanţieri de obiecte pentru clasele abstracte

Aplicatie a=new Aplicatie(); //eroare la compilare


Aplicatie a; //Ok

 O clasă trebuie declarată abstractă dacă:


 Conţine cel puţin o metodă abstractă
 Moşteneşte o metodă abstractă de la supraclasa sa şi nu o implementează
 Reprezintă o implementare a unei interfeţe pentru care există cel puţin o metodă
care nu este implementată
 Dacă prin extinderea clasei Aplicatie se defineşte o clasă care nu mai este abstractă
se poate face şi o instanţiere

class AplicatieImplementata extends Aplicatie{


void prelucrare(){}
}
Aplicatie a=new AplicatieImplementata(); //ok

28
Interfeţe
 Variabilele de tip interfaţă pot fi utilizate ca argumente ale metodelor şi pot fi obţinute
ca rezultat, se pot crea ierarhii de interfeţe similare ierarhiilor de clase

 Metodele care apar într-o interfaţă sunt în mod implicit publice

 Variabilele declarate sunt de fapt constante (chiar dacă nu se specifică atributele


corespunzătoare: public, static, final compilatorul asumă aceste atribute în mod
implicit)

interface Exemplu{
int versiune=3;
void prelucrare1();
void prelucrare2(Floare f);
}

 O clasă care implementează interfaţa trebuie să specifice codul corespunzător


metodelor, din interfaţă dar poate să declare variabile şi metode care nu apar în
interfaţă

29
class Implementare implements Exemplu{

void prelucrare1(){

}
void prelucrare2(Floare f){
...
}
void altaPrelucrare(){ //metoda care nu se gaseste in interfata
...
}
}

 Variabile referinţă la o interfaţă – valoarea unei astfel de variabile poate fi o referinţă


la un obiect al unei clase care implementează interfaţa sau a unei clase care extinde
o clasă ce implementează interfaţa
Exemplu e;
….
Implementare i1=new Implementare();

e=i1; //ok

30
 O interfaţă poate fi extensia unei alte interfeţe, adică poate să adauge noi modele de
metode la o interfaţă care există

 Implementarea unei interfeţe poate fi o clasă abstractă

 O clasă poate implementa mai multe interfeţe


public class RandomAccesFile implements DataOutput, DataInput{

}

interface Executabila{
public void executie();
}
public class Exemplu{

void prelucrare(Executabila e){
e.executie();
}
}
class Implementare implements Executabila{
public void executie(){
System.out.println(“s-a executat metoda executie din Implementare”)
}
}
31
….
new Exemplu().prelucrare(new Implementare());//se va apela metoda Implementare.executie()

 Exemplu de utilizare clase abstracte şi interfeţe


abstract class A{ class Test{
abstract void x(String n); public static void main(String args[]){
} A a=new Aimplem();
interface B{ B b=new Bimplem();
void y(String n);
} ABimplem ab=new ABimplem();
class Aimplem extends A{ ab.z(a,b);
void x(String n){ }
System.out.println(" din Aimplem "+n); }
}
}
class Bimplem implements B{
public void y(String n){
System.out.println(" din Bimplem "+n);
}
}
class ABimplem extends A implements B{
void x(String n){
System.out.println(" din ABimplem "+n);
}
public void y(String n){
System.out.println(" din ABimplem "+n);
}
void z(A a,B b){
a.x("AAA");
b.y("BBB");
x("CCC");
y("DDD");
}
} 32
3.11 Clase anonime
 Clasele anonime permit:
 scrierea comasată a codului
 declararea unei clase și instanțierea ei în același timp

 O clasă anonimă este o expresie. Sintaxa unei clase anonime este asemănătoare cu
apelul unui constructor însoțit de definiția unei clase

 Expresia unei clase anonime conține următoarele:


 Operatorul new
 Numele unei interfețe de implementat sau a unei clase de extins
 Parantezele care contin argumentele unui constructor
 Corpul declarării unei clase
 Fișierul Ordonare.java
package clase_anonime;

public interface Ordonare {


public void metoda();
}
33
 Fișierul MainApp.java:

package clase_anonime;

public class MainApp {


public static void main(){
Ordonare t=new Ordonare(){
public void metoda(){
System.out.println("Ordoneaza prin HeapSort");
}
};
t.metoda();
}
}

34
3.12 Clase generice
 O clasă generică este o clasă care are parametri generici, enumerați între
parantezele unghiulare care însoțesc definiția clasei.

 Valorile din interiorul parantezelor ungiulare reprezintă tipuri de date care vor fi
stabilite când se declară un obiect de tipul clasei respective.

 Operatorul <> este cunoscut sub denumirea operatorul diamond


package exemplu;
class Persoana<T,S>{
T nume;
S varsta;
public Persoana(T nume, S varsta) {
this.nume = nume;
this.varsta = varsta;
}
public String toString() {
return nume + ", " + varsta;
}
}

35
class MainApp {
public static void main(String[] args) {
String nume1="Popescu Ion";
int varsta1=23;
Persoana<String, Integer> p1=new Persoana<String, Integer>(nume1, varsta1);
System.out.println(p1);

StringBuilder nume2=new StringBuilder("Popescu Ion");


byte varsta2=23;
Persoana<StringBuilder,Byte> p2=new Persoana<StringBuilder,Byte>(nume2, varsta2);
System.out.println(p2);
}
}

36
3.13 Metode generice
 Metodele generice sunt metode care pot fi apelate cu argumente de diferite tipuri

 Metodele generice au operatorul diamond înaintea tipului returnat. În interiorul


acestuia se enumeră tipurile generice separate prin virgule

package exemplu;

public class MainApp2 {


public static <T> void afiseazaVector(T[] vector ) {
for(T v : vector) {
System.out.print(v+" ");
}
System.out.println();
}

public static void main(String[] args) {


Integer[] v1 = { 1, 2, 3, 4, 5 };
Character [] c={'t','e','s','t'};

afiseazaVector(v1);
afiseazaVector(c);
}
} 37
3.13 Serializarea obiectelor
 Serializarea obiectelor presupune descompunerea acestora într-o înșiruire de octeți
și salvarea lor într-un flux de date. Procesul opus, de recompunere a obiectelor din
șirul de octeți se numește deserializare

 Pot să fie serializate obiectele oricărei clase care implementează intefața Serilizable

 O clasă care implementează interfața Serilizable poate avea variabile membre


obiecte ale altor clase, care la rândul lor trebuie să implementeze interfata Serilizable
pentru a putea fi salvate

 Câmpurile ale căror valoare nu se dorește să fie serializată trebuie să fie precedate
de cuvântul cheie transient

 Serializarea / deserializarea obiectelor se poate realiza cu ajutorul claselor


ObjectOutputStream / ObjectInputStream iar a tipurilor primitive cu ajutorul claselor
DataOutputStream / DataInputStream

 Metoda writeObject() din clasa ObjectOutputStream realizeaza serializarea, iar


metoda readObject() din clasa ObjectInputStream() realizeaza deserializarea 38
 Exemplul de mai jos serializează un tablou de persoane in fișierul pers.bin

 Fișierul Persoana.java:
package exemplu;

import java.io.Serializable;

class Persoana implements Serializable{


private String nume;
private transient int varsta;
private Adresa adresa;

public Persoana(String nume, int varsta,Adresa adresa) {


this.nume = nume;
this.varsta = varsta;
this.adresa=adresa;
}
@Override
public String toString() {
return nume + " " + varsta+" "+adresa;
}
}

39
 Fișierul Adresa.java:
package exemplu;

import java.io.Serializable;

class Adresa implements Serializable{


private String localitate;
private String strada;
private int nr;

public Adresa(String localitate, String strada, int nr) {


this.localitate = localitate;
this.strada = strada;
this.nr = nr;
}
@Override
public String toString() {
return localitate + ", " + strada + ", " + nr;
}
}

40
 Fișierul MainApp.java:

package exemplu;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class MainApp {


static void scrie(Object o, String fis) {
try {
FileOutputStream f = new FileOutputStream(fis);
ObjectOutputStream oos = new ObjectOutputStream(f);
oos.writeObject(o);
oos.close();
f.close();
}
catch (IOException e) {
e.printStackTrace();
}
}

41
static Object citeste(String fis) {
try {
FileInputStream f = new FileInputStream(fis);
ObjectInputStream ois = new ObjectInputStream(f);
Object o=ois.readObject();
ois.close();
f.close();
return o;
}
catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}

public static void main(String[]args) {


Persoana[] pers=new Persoana[2];
pers[0]=new Persoana("Ion",20, new Adresa("Timisoara","Parvan",1));
pers[1]=new Persoana("Ana",21,new Adresa("Timisoara","Republicii",1));
scrie(pers,"pers.bin");

Persoana []q;
q=(Persoana[])citeste("pers.bin");
for(Persoana p:q)
System.out.println(p);
}
} 42
 Câmpul varsta fiind precedat de cuvântul cheie transient nu va fi serializat, își va
pierde valoarea

 Pentru ca obiectele clasei Persoana să poată să fie serializate este necesar ca


aceasta să implementeze interfața Serilizable. Clasa Persoana are un câmp de tip
Adresa. Pentru ca acel câmp să poată fi serializat este necesar ca și clasa Adresa să
implementeze interfața Serilizable. În caz contrar se va produce
NotSerializableException

43
4. Colecţii de obiecte

Sl. dr. ing. Raul Robu


www.aut.upt.ro/~rraul

2020-2021, Semestrul 1
4.1 Noţiuni generale
 “The Collections Framework “ constituie o arhitectură unificată pentru reprezentarea şi
manipularea colecţiilor, permiţând manipularea acestora independent de detaliile implementării
lor.

 Avantajele colecţiilor de obiecte:


 reduc efortul de programare şi cresc performanţă – prin faptul că furnizează structuri de
date şi algoritmi performanţi care altfel ar trebui scrişi de programator
 cresc gradul de reutilizare a codului – prin faptul că o furnizează o interfaţă standard pentru
colecţii şi algoritmi pentru a le manipula

 Framework-ul este bazat pe interfeţele de colecţii din figura de mai jos. El include implementări
ale acestor interfeţe şi algoritmi pentru a le manipula

Interfeţe
Collection Map

List Set Queue SortedMap

SortedSet

2
 Collection – un grup de obiecte. Este utilizată când se doreşte maximul de generalitate.

 List, Set şi Queue extind interfaţa Collection


 Set - nu permite elemente duplicat, poate să fie sau nu ordonată. Elementele nu pot fi accesate
prin indice

 List – Colecţie de asemenea cunoscută ca secvenţă. Elementele se regăsesc în ordinea


adăugării. Duplicatele sunt de obicei permise. Permite accesul la elemente cu ajutorul unui indice

 Map – o punere în corespondenţă cheilor şi valorilor (keys to values). Fiecare cheie corespunde
unei valori

 SortedSet – un set a cărui elemente sunt automat ordonate, fie în ordine naturală fie printr-un
obiect de tip Comparator specificat când instanţa de tip SortedSet este creată. Extinde interfaţa
Set

 SortedMap – un Map a cărui elemente sunt ordonate automat prin cheie fie în ordine naturală fie
printr-un comparator transmis instanţei SortedMap atunci când aceasta este creată

Implementări

 Clase care implementează interfeţele menţionate sunt:

 HashSet - implementarea printr-un tabel de dispersie (HashTable) a interfeţei Set (cea mai bună
3
implementare a interfeţei Set)
 TreeSet – implementarea printr-un arbore roşu – negru (Red-black tree) a interfeţei SortedSet.
Elementele sunt ordonate pe baza valorii lor

 LinkedHashSet – implementarea interfeţei Set cu ajutorul unui tabel de dispersie şi a unei liste
înlănţuite (Linked list). O implementare care face inserare în ordinea adăugării şi care rulează
aproape la fel de rapid ca un HashSet

 ArrayList – o implementare a interfeţei List printr-un vector care îşi ajustează dimensiunea (cea
mai bună implementare a interfeţei List).

 LinkedList – o implementare a interfeţei List cu listă dublu înlănţuită. Poate furniza o performanţă
mai buna decât implementările cu ArrayList dacă elementele sunt inserate sau şterse frecvent

 HashMap – Implementarea interfeţei Map cu ajutorul unui tabel de dispersie (în mod esenţial un
tabel de dispersie nesincronizat care suportă null pentru chei şi valori (cea mai bună
implementare a interfeţei Map)

 TreeMap - implementarea printr-un arbore roşu - negru a interfeţei SortedMap .

 LinkedHashMap – implementarea cu tabel de dispersie şi listă înlănţuită a interfeţei Map. O


implementare bazată pe inserare ordonată care lucrează aproape la fel de repede ca un
HashMap

4
Algoritmi

 Algoritmii sunt furnizaţi de clasa Collections, sub forma unor metode statice, a căror prim
argument este colecţia de elemente asupra căreia se va aplică operaţia. Majoritatea algoritmilor
operează asupra unor instanţe List.

 Algoritmii permit ordonarea ordonarea colecţiilor, căutarea unui element, determinarea valorii
maxime, a celei minime, etc

4.2 Interfeţe
 În general trebuie să avem în minte interfeţele, nu modul în care acestea sunt implementate

Interfaţa Collection

 O colecţie este formată dintr-un grup de obiecte. Interfaţa Collection este folosită pentru
transmiterea colecţiilor de obiecte în situaţii în care se doreşte maximul de generalitate
 Interfaţa are metode precum:
 size() – returnează numărul de elemente care fac parte din colecţie ()
 isEmpty () - verifică dacă colecţia e goală ()
 contains ()- verifică dacă un anumit obiect este în colecţie ()
 add(), remove() - adaugă / elimină un obiect din colecţie ()
 iterator() - furnizează un iterator pentru colecţie (), etc
 Metoda de adăugare este definită destul de general încât să poată fi folosită in colecţii de
elemente care permit duplicatele respectiv în cele care nu permit acest lucru. Metodele add ()şi 5
remove() returnează true dacă adăugarea / ştergerea a reuşit şi false daca nu
 Parcurgerea colecţiei
 Parcurgerea cu for-each

for (Persoana p : colectie_persoane)


System.out.println(p);

 Parcurgerea cu iterator
java.util.Iterator<Persoana> it=pers.iterator();
while (it.hasNext()){
p=it.next();
System.out.println(p.toString());
}

 Se utilizează iterator în loc de for-each în situaţia în care dorim să ştergem un element.


Construcţia for-each ascunde iterator-ul şi deci nu poate fi apelată metoda remove()

 Bulk Operations
 Realizează o operaţie asupra întregii colecţii. De exemplu:
 containsAll() — returnează true dacă colecţia ţintă conţine toate elementele din colecţia
specificată
 addAll() - adaugă toate elementele din colecţia specificată la colecţia ţintă
 removeAll() – şterge din colecţia ţintă toate elementele care sunt de asemenea conţinute în
colecţia specificată
 retainAll() – şterge din colecţia ţintă toate elementele care nu sunt conţinute în colecţia
specificată. Reţine în colecţia ţintă doar elementele care sunt şi în colecţia specificată
 clear() –şterge toate elementele din colecţie
6
Interfaţa List

 O listă este o colecţie în care elementele se găsesc în ordinea adăugării. Listele pot conţine
duplicate. Pe lângă operaţiile moştenite de la interfaţa Collection, interfaţa List include
următoarele operaţii:
 Acces poziţional – manipulează obiectele pe baza poziţiei lor numerice în listă
 Căutare – caută după un anumit obiect şi îi returnează poziţia în listă
 Vizualizarea unui domeniu – permite realizarea unor operaţii asupra elementelor care se află
într-un domeniu al listei
package exemplu;
import java.util.*;

class Persoana{
private String nume;
private int varsta;

public Persoana(String nume,int varsta){


this.nume=nume;
this.varsta =varsta;
}
public Persoana(String nume){
this.nume=nume;
}

public String getNume(){ return nume; }


public int getVarsta(){ return varsta; }
public String toString(){
return nume+" "+varsta;
}
} 7
class MainApp1 {
public static void main(String[] args) {
List<Persoana> pers = new ArrayList<Persoana>();
pers.add(new Persoana("Maria",30));
pers.add(new Persoana("Ela",20));
pers.add(new Persoana("Oana",18));
pers.add(new Persoana("Denisa",28));

System.out.println("Dimensiunea initiala a listei="+pers.size()+" elemente");


System.out.println("----Afisare1---");
System.out.println(pers);

System.out.println("----Afisare2---");
for (int i=0;i<pers.size();i++)
System.out.println(pers.get(i).toString() );

System.out.println("----Afisare3---");
for (Persoana p:pers)
System.out.println(p);

System.out.println("----Afisare4---");
Iterator<Persoana> it=pers.iterator();
while (it.hasNext()){
Persoana p=it.next();
System.out.println(p.toString());
}

System.out.println("---Stergere---");
pers.remove(0);//stergerea primului element
System.out.println(pers);

System.out.println("----Ordonare varsta---");
Collections.sort(pers, new ComparaVarsta()); 8
System.out.println(pers);
System.out.println("----Ordonare nume---");
Collections.sort(pers, new ComparaNume());
System.out.println(pers);

System.out.println("----Cautare in colectie ordonata---");


String s="Denisa";
int x=Collections.binarySearch(pers, new Persoana(s),new ComparaNume());
if (x>=0)
System.out.println(s+" gasita pe pozitia "+x);
else
System.out.println(s+" nu a fost gasita");

System.out.println("----Colectie neordonata---");
Collections.shuffle(pers);
System.out.println(pers);

System.out.println("----Cautare in colectie neordonata---");


boolean gasit=false;
for (int i=0;i<pers.size();i++)
if (pers.get(i).getNume().equals(s)){
System.out.println(s+" gasita pe pozitia "+i);
gasit=true; break;
}
if (!gasit)
System.out.println(s+" nu a fost gasita" );

System.out.println("---Setarea unui element---");


pers.set(2,new Persoana("Roxana",50));
System.out.println(pers);

9
System.out.println("---Stergerea unei bucati din lista---");
pers.subList(0, 2).clear();
System.out.println(pers);
}
}

class ComparaNume implements Comparator{


@Override
public int compare(Object o1, Object o2) {
return (((Persoana)o1).getNume().compareTo(((Persoana) o2).getNume()));
}
}

class ComparaVarsta implements Comparator<Persoana>{


@Override
public int compare(Persoana p1, Persoana p2) {
if (p1.getVarsta()>p2.getVarsta()) return 1;
else
if (p1.getVarsta()<p2.getVarsta()) return -1;
else return 0;
}
}

10
Interfaţa Queue

 O coadă este o colecţie pentru păstrarea elementelor înainte de procesare. Pe lângă operaţiile de
bază ale interfeţei Collections cozile dispun de operaţii de inserare, ştergere şi inspectare
adiţionale
 Cozile în mod tipic ordonează elementele în stil FIFO (first-in-first-out). Printre excepţii sunt cozile
prioritare care ordonează elementele pe baza valorii lor
 Indiferent de stilul de ordonare, elementul care se va şterge atunci când se apelează metoda
remove() este cel din capul cozii.
package exemplu;
import java.util.*;

class Persoana2 implements Comparable<Persoana2>{


private String nume;
private int varsta;

public Persoana2(String nume,int varsta){


this.nume=nume;
this.varsta =varsta;
}
public Persoana2(String nume){
this.nume=nume;
}
public String getNume(){
return nume;
}
public int getVarsta(){
return varsta;
} 11
public String toString(){
return nume+" "+varsta;
}
@Override
public int compareTo(Persoana2 o) {
return this.nume.compareToIgnoreCase(o.nume);
}
}

class MainApp2{
public static void main(String []args){
Queue<Persoana2> pers = new LinkedList<Persoana2>();
//Queue<Persoana2> pers = new PriorityQueue<Persoana2>();

pers.add(new Persoana2("Bogdan",30));
pers.add(new Persoana2("Vlad",20));
pers.add(new Persoana2("George",18));
pers.add(new Persoana2("Dorel",28));

System.out.println("Dimensiunea initiala a cozii="+pers.size()+" elemente");


System.out.println("----Coada initiala---");
System.out.println(pers);

System.out.println("---Stergere primul element din coada---");


pers.remove();
for (Persoana2 pe:pers)
System.out.println(pe.toString() );

//ordonare doar pentru coada neordonata de tip LinkedList


System.out.println("---Ordonare---");
Collections.sort((List<Persoana2>) pers); 12
java.util.Iterator<Persoana2> it=pers.iterator();
while (it.hasNext()){
Persoana2 p=it.next();
System.out.println(p.toString());
}

for (Persoana2 pe:pers)


if (pe.getNume().equals("Vlad") )
System.out.println("Vlad Gasit!");

System.out.println("Elementul din varful cozii "+pers.element());


}
}

Interfaţa Map

 Un Map este un obiect care pune în corespondenţă cheile către anumite valori. Un Map nu poate
să conţină duplicate de chei. Fiecare cheie corespunde unei singure valori
 Interfaţa Map are trei implementări HashMap, TreeMap şi LinkedHashMap
 Operaţiile de bază cu Map sunt put, get, containsKey, containsValue, size şi isEmpty
 Metodele de explorare a colecţiei permit Map-ului să fie vizualizat în aceste 3 moduri:
 keySet — setul de chei conţinut în Map
 values — colecţia de valori conţinute în Map. Această colecţie nu este de tip Set pentru că
diferite chei pot să corespundă aceleiaşi valori
 entrySet — Set-ul de perechi key-value conţinut în Map.
 Aceste vederi reprezintă singurul mijloc de a explora un Map
for (KeyType key : m.keySet())
System.out.println(key); 13
package exemplu;
import java.util.*;
import java.util.Map.Entry;

class MainApp3{
public static void main(String []args){
Map<String,Persoana> mp=new HashMap<String,Persoana>();

mp.put("unu",new Persoana("Maria",20) );
mp.put("doi",new Persoana("Ileana",10) );
mp.put("trei",new Persoana("Oana",20) );
mp.put("patru",new Persoana("Denisa",30));
mp.put("unu",new Persoana("Maia",30));

Set s=mp.entrySet();

Iterator it=s.iterator();

while(it.hasNext())
{
Entry m =(Entry)it.next();

String key=(String)m.getKey();
Persoana value=(Persoana)m.getValue();
System.out.println("Key :"+key+" Value :"+value.toString());

if (key.equalsIgnoreCase("trei"))
it.remove();
}
System.out.println(mp);
}
} 14
Interfaţa Set

 Un set este o colecţie de elemente care nu poate conţine duplicate

 O interfaţă Set conţine doar metodele moştenite de la interfaţa Collection şi adaugă restricţia că
duplicatele sunt interzise

 Java oferă 3 implementări ale interfeţei Set: HashSet, TreeSet şi LinkedHashSet (vezi
subcapitolul 4.1 - Implementări)

 Presupunând că avem o colecţie col şi vrem să creem o altă colecţie care să conţină aceleaşi
elemente dar toate duplicatele eliminate, putem scrie următorul cod:

Collection<Type> faraDuplicate = new HashSet<Type>(col);

 Un exemplu de eliminare a duplicatelor dar care păstrează ordinea elementelor din colecţia în
urma eliminării este:

Collection<Type> faraDuplicate = new LinkedHashSet<Type>(col);

15
 Exemplu care subliniază că într-un Set duplicatele sunt eliminate şi că elementele sunt ordonate
diferit în funcţie de implementarea aleasă

import java.util.*;

public class SetExample {


public static void main(String[] args) {
Set<String> s=new HashSet<String>(); HashSet
// Set<String> s=new TreeSet<String>();
// Set<String> s=new LinkedHashSet<String>();

s.add("b");
s.add("a");
s.add("b");
TreeSet
s.add("c");

Iterator it=s.iterator();

while(it.hasNext()){
String value=(String)it.next();
System.out.println("Value :"+value); LinkedHashSet
}
}
}

16
 În situatia în care se utilizează o implementare a interfeței Set cu table de dispersie (HashTable)
cum ar fi HashSet sau LinkedHashSet pe o celecție de obiecte în care obiectele nu au o ordine
naturală, duplicatele nu sunt automat eliminate. Vezi exemplul urmator:
package exemplu;

import java.util.HashSet;
import java.util.Set;
import java.util.LinkedHashSet;

public class MainApp4 {


public static void main(String[] args) {
Set<Persoana> pers = new HashSet<Persoana>();//sau new LinkedHashSet<Persoana>();
pers.add(new Persoana("Maria",30));
pers.add(new Persoana("Ela",20));
pers.add(new Persoana("Maria",30));
pers.add(new Persoana("Denisa",28));
System.out.println(pers);
}
}

 Acest lucru se întamplă pentru ca nu fost redefinite metodele equals() și hashCode() din clasa
Object și se folosesc implementările implicite ale acestora

 Implementarea implicită a metodei equals() din clasa Object compară referinta obiectului curent
cu cea a obiectului transmis ca și parametru 17
 Tot din acest motiv codul de mai jos afiseaza mesajul Diferite, pentru că referințele celor două
obiecte sunt diferite, chiar daca ele au același continut.
package exemplu;

public class MainApp5 {


public static void main(String[] args) {
Persoana p1 = new Persoana("Maria", 20);
Persoana p2 = new Persoana("Maria", 20);
if(p1.equals(p2))
System.out.println("Egale");
else
System.out.println("Diferite");
}
}

 Pentru ca metoda equals() să compare câmpurilor obiectului current cu câmpurile obiectului dat
ca și parametru, ea trebuie redefinită în clasa Persoana și specificată o implementare
corespunzătoare

 Metoda hashCode() returneaza valoarea hash a obiectului care este folosită ca și cheie în
tabelele de dispersie

 Pentru două obiecte care sunt egale potrivit metodei equals() metoda hashCode() trebuie
să returneze acceași valoare hash

 În Eclipse pot fi generate ambele metode din optiunea de meniu Source > Generate hashCode()
18
and equals()...
 După ce se genereaza metodele hashCode() și equals() în clasa Persoana duplicatele nu vor mai
fi premise

 În cazul în care se utilizează pentru interfața Set o implementare prin clasa TreeSet, va trebui
specificat un comparator. Acesta va fi folosit pentru ordonare si pentru eliminarea duplicatelor
(vezi exemplul următor)

Interfaţa SortedSet

 Un obiect SotedSet este un set care menţine elementele în ordine crescătoare, sortate pe baze
ordonării naturale sau în acord cu un obiect de tip Comparator furnizat SortedSet-ului în momentul
creării. Pe lângă operatorii oferiţi de interfaţa Set, interfaţa SortedSet mai aduce operatori pentru:
 Vizualizarea unui domeniu de valori
 Determinarea primului şi ultimului element din SortedSet
 Returnează comparatorul utilizat pentru a ordona setul

package exemplu;
import java.util.*;

class MainApp6{
public static void main(String []args){
SortedSet<Persoana2> pers = new TreeSet<Persoana2>();
pers.add(new Persoana2("Bogdan",30));
pers.add(new Persoana2("Vlad",20));
pers.add(new Persoana2("George",18));
pers.add(new Persoana2("Bogdan",30));
pers.add(new Persoana2("Dorel",28)); 19
System.out.println("Dimensiunea initiala a SortedSet-ului="+pers.size());
System.out.println("---Setul initial ordonat dupa nume---");

java.util.Iterator<Persoana2> it=pers.iterator();
while (it.hasNext()){
Persoana2 p=it.next();
System.out.println(p.toString());
}

System.out.println("---Stergere---");
pers.remove(new Persoana2("Bogdan"));

for (Persoana2 pe:pers)


System.out.println(pe.toString() );

System.out.println("---Cautare---");
for (Persoana2 pe:pers)
if (pe.getNume().equals("George") )
System.out.println("Gasit!");

System.out.println("---Primul---");
System.out.println(pers.first());
}
}

20
4.3 Clasa Vector

 Clasa Vector implementează o mulţime crescătoare de obiecte. La fel ca şi în cazul vectorilor de


lungime fixă, elementele unui obiect de tip Vector pot fi accesate cu ajutorul unui indice.
Dimensiunea obiectului Vector poate să crească sau să scadă în funcţie de elementele adăugate
sau şterse din acesta

 Capacitatea unui Vector (capacity) este întotdeauna cel puţin la fel de mare ca şi dimensiunea
vectorului (size). Capacitatea unui vector se poate modifica în funcţie de conţinutul acestuia. (De
exemplu, capacitatea iniţială este 10 şi rămâne atât până se adaugă primele 10 obiecte. Înainte
să se adauge elementul al 11-lea se produce o realocare şi capacitatea vectorului se dublează,
ajungând la 20).

 Clasa Vector este sinconizată, ceea ce înseamnă că metodele care modifică structura vectorului
cum ar fi add (), remove () sunt sincronizate şi pot fi folosite cu încredere în aplicaţiile cu fire de
execuţie (sunt thread safe). ArrayList nu este thread safe deci nu este recomandata în fire de
execuţie. Pe de alta parte ArrayList este mai rapidă, deci recomandată în programele fără fire de
execuţie

 Clasa Vector face parte din Java Collections Framework

21
package exemplu;
import java.util.*;

class MainApp7{
public static void main(String []args){
Vector pers=new Vector();
pers.add(new Persoana("Maria",30));
pers.add(new Persoana("Ela",20));
pers.add(new Persoana("Oana",18));
pers.add(new Persoana("Denisa",28));

System.out.println("Capacitate vector="+pers.capacity());
System.out.println("Numar elemente in vector="+pers.size());

System.out.println("---Vectorul initial---");
for (int i=0;i<pers.size();i++)
System.out.println(pers.elementAt(i).toString());

System.out.println("---Stergere---");
pers.removeElementAt(0);
Iterator<Persoana> it=pers.iterator();
while(it.hasNext()){
Persoana p=it.next();
System.out.println(p.toString());
}

System.out.println("---Ordonare---");
Collections.sort(pers, new ComparaNume());
System.out.println(pers);
for (int i=0;i<pers.size();i++)
if (((Persoana)pers.elementAt(i)).getNume().equals("Denisa") )
System.out.println("Gasit!");
} 22
}
4.4 Clasa Properties

 Clasa Properties este utilizată la colecții de obiecte formate din perechi chei-valoare la care atât
cheia cât și valoarea sunt striguri.

 Proprietățile pot să fie salvate într-un stream sau încărcate dintr-un stream cu ajutorul metodelor
load() și store()

package exemplu;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;

public class MainApp8 {


public static void main(String[] args) throws IOException {
Properties judete = new Properties();
judete.setProperty("TM", "Timis");
judete.setProperty("AR", "Arad");
judete.setProperty("HD", "Hunedoara");

System.out.println(judete);
23
Set jud = judete.keySet();
Iterator itr = jud.iterator();

while (itr.hasNext()) {
String s = (String) itr.next();
System.out.println("Judetul " + judete.getProperty(s) + " are codul “ + s + ".");
}
scrie(judete,"judete.properties");
System.out.println("citire din fisier");
Properties jud_in = new Properties();
citeste(jud_in, "judete.properties");
}

public static void scrie(Properties p, String fis) {


FileWriter writer;
try {
writer = new FileWriter(fis);
p.store(writer,"Lista judetelor");
writer.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

24
public static void citeste(Properties p, String fis) {
FileReader reader;
try {
reader = new FileReader(fis);
p.load(reader);
p.list(System.out);
reader.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}

 Introducerea elementelor în colecție se poate face cu ajutorul metodelor setProperty() sau put()

 Setul de chei poate fi obținut cu ajutorul metodei keySet()

 Valoarea unei chei se poate obține cu ajutorul metodei getProperty()

 Metoda list() permite afișarea colecției în streamul dat ca și parametru de intrare, care poate fi
asociat monitorului, unui fișier, etc

 Metoda remove() permite eliminarea unui element din colecție


25
4.4 Vectori de dimensiune fixă
 Un vector este un container care conţine un număr fix de valori de un singur tip. Lungimea
vectorului este stabilită când acesta este creat. După creare lungime rămâne fixă (vezi
subcapitolul 2.5.5)

 Exemplul precedent rezolvat cu tablouri de lungime fixă


package exemplu;

import java.util.Arrays;
import java.util.Comparator;

class MainApp9{
static void afis(Persoana []pers,int n){
for (int i=0;i<n;i++)
System.out.println(pers[i].toString());
}

public static void main(String []args){


Persoana []pers=new Persoana[10];
int n=0;
pers[n++]=new Persoana("Maria",30);
pers[n++]=new Persoana("Ela",20);
pers[n++]=new Persoana("Oana",18);
pers[n++]=new Persoana("Denisa",28);

System.out.println("pers.length="+pers.length +"; n="+n);

System.out.println("---Vectorul initial---");
afis(pers,n); 26
System.out.println("---Stergerea primului element---");
for (int i=0;i<n-1;i++)
pers[i]=pers[i+1];
n--;
afis(pers,n);

Arrays.sort(pers,0,n, new Comparator<Persoana>() {


@Override
public int compare(Persoana p1, Persoana p2) {
return p1.getVarsta()-p2.getVarsta();
}
});
System.out.println("---Ordonare crescatoare dupa varsta---");
afis(pers,n);
}
}

 Ordonarea s-a făcut cu ajutorul metodei statice sort din clasa Arrays care primește ca și parametri
de intrare vectorul de ordonat, indexul de start (inclusiv), indexul de stop(exclusiv) și comparatorul
care va fi utilizat pentru ordonarea colecției care în cazul de față s-a transmis folosind o clasă
anonimă

27
5. Tratarea excepţiilor

Sl. dr. ing. Raul Robu


www.aut.upt.ro/~rraul

2020-2021, Semestrul 1
5. Tratarea excepţiilor
 O excepţie este un eveniment care nu permite continuarea normală a execuţiei
programului

 Exemple de excepţii
 Încercarea de a accesa un element aflat dincolo de limitele unui vector
(IndexOutOfBoundsException)
 Încercarea de a accesa un fişier de intrare care nu există
(FileNotFoundException)
 Încercarea de a realiza o împărţire la 0 (ArithmeticException)
 Încercarea de a accesa cu metoda charAt() un caracter din afara unui String
(StringIndexOutOfBoundsException)
 Încercarea de a converti în întreg cu ajutorul metodei Integer.parseInt () un
String necorespenzător (NumberFormatException)
 Utilizarea excepţiilor este un mecanism elegant de a renunţa la execuţia secvenţei de
cod care a provocat excepţia, semnalarea acesteia şi eventual execuţia unei
secvenţe de tratare corespunzătoare

 O excepţie este un obiect ce aparţine casei Throwable sau unei clase derivate din
aceasta

 Java oferă o ierarhie de clase predefinite pentru tratarea excepţiilor, care se găsesc 2
în pachetul java.lang. La rădăcina ierarhiei se află clasa Throwable
 Din clasa Throwable sunt derivate în mod direct următoarele două ierarhii de clase:
 Error – pentru erorile nerecuperabile. De obicei aceste erori se referă la maşina
virtuală sau la editarea legăturilor. Aceste erori nu trebuie tratate pentru că în
cazul apariţiei unor astfel de erori nu mai poate continua execuţia. În astfel de
situaţii se afişează un mesaj şi programul se închide

 Exception se împarte în două ierarhii:


o O ierarhie care porneşte din clasa RuntimeException (se referă în general
la greşeli de programare cum ar fi depăşirea vectorilor sau utilizarea
proastă a tipurilor)
o O altă ierarhie care poate să fie creată de către programator

 Programatorul poate utiliza clasele predefinite de excepţii sau poate să-şi definească
propriile clase pentru tratarea excepţiilor

 Clasele definite de programator pentru tratarea excepţiilor trebuie să fie derivate


direct sau indirect din Throwable (prin convenţie se derivează din clasa Exception)

 Definirea unui tip de excepţie de către utilizator se justifică de obicei prin:


 necesitatea reţinerii de date suplimentare şi/sau mesaje mai detaliate despre
excepţie;
 necesitatea ca dintr-o clasă mai largă de excepţii să fie captate doar o anumită
3
parte.
 Tratarea erorilor care pot apărea la rulare se poate realiza și fără a utiliza
mecanismul de tratare pus la dispoziție de limbaj, realizând una din următoarele
acțiuni:
 terminarea forţată a programului - nu reprezintă o tratare propriu-zisă a erorii; de
multe ori o eroare nu este chiar atât de gravă, iar programul poate şi trebuie să
se descurce cu ea într-un mod mai inteligent.

 returnarea unei valori speciale pe post de „eroare“.

 poziţionarea unui indicator de eroare şi returnarea unei valori valide

 apelul unei funcţii prevăzute special pentru cazurile de eroare; această funcţie,
la rândul ei are de ales între cele 3 alternative de mai sus.

 Mecanismele de emitere-captare a excepţiilor:


 permit separarea explicită a secvenţelor de cod care tratează situaţiile normale
faţă de cele care tratează excepţiile.

 oferă suportul necesar pentru a „forţa“ o funcţie să returneze, în situaţii


deosebite, valori ale altui tip decât cel specificat în antet ca tip returnat.

4
 Tratarea unei excepţii are două componente:
 Componenta care semnalează excepţia (throws)
 Componenta care tratează excepţia (prinde excepţia)

 Java pune la dispoziţie trei construcţii de bază pentru lucrul cu excepţii


 instrucţiunea throw pentru emiterea excepţiilor;
 blocul catch pentru definirea secvenţei de captare şi tratare a unei excepţii;
 blocul try pentru delimitarea secvenţelor de cod sensibile la apariţia excepţiilor.

Instrucţiunea throw
throw obiect;

 obiect trebuie să fie o referinţă la un obiect derivat din clasa Exception

 O instrucţiune throw poate să apară într-o funcţie numai dacă:


 Se găseşte într-un bloc try - catch care captează tipul de excepţie returnat de
expresia din throw
 Definiţia funcţiei este însoţită de o clauză throws în care apare tipul de excepţie
respectiv
 Excepţia generată aparţine claselor RuntimeException sau Error sau
descendenţilor acestora 5
Clauza throws
 Dacă într-o metodă este posibil să apară o excepţie fără ca aceasta să fie tratată,
atunci în linia de declaraţie a metodei trebuie specificat tipul excepţiei respective

 O metodă trebuie să declare că poate să arunce o excepţie pentru ca metodele care


o apelează să prevadă tratarea excepţiei respective

tip_returnat nume_functie(lista_param) throws tip_ex1, tip_ex2,...,tip_exn

 Această clauză se ataşează la antetul unei funcţii şi ea precizează tipurile de excepţii


care pot fi generate pe parcursul execuţiei funcţiei respective

 Dacă în lista de tipuri din clauza throws există cel puţin două tipuri, tip_exi şi tip_exj,
derivate dintr-o superclasă comună tip_exsup, limbajul permite înlocuirea celor două
cu tip_exsup.

 Dacă în listă se trece doar Exception, informaţia este foarte vagă pentru cititorul
programului

 Dacă programatorul nu prevede clauza throws, şi nici secvenţe try-catch într-o funcţie
dezvoltată care apelează funcţii predefinite dotate cu clauze throws, compilatorul va
sesiza acest lucru ca eroare.
6
Blocurile try -catch
 Acestea indică zona de instrucţiuni în care pot să apară excepţiile, tipul excepţiilor şi
modul în care se face tratarea acestora

try {
//secventa obisnuita de operatii, care poate fi intrerupta de aparitia unei exceptii
}
catch(tip_ex1 e){
//tratare exceptie de tipul tip_ex1
}
catch(tip_ex2 e){
//tratare exceptie de tipul tip_ex2
}
//. . .
catch(tip_exn | tip_exm e){ //incepand cu java 7
//tratare exceptie de tipul tip_exn si tip_exm
}
finally {
//secventa optionala
}

 Un bloc try este urmat de 0, 1 sau mai multe blocuri catch şi eventual de un bloc
finally
7
 Dacă pe parcursul executării intrucţiunilor din blocul try este emisă o excepţie,
secvenţa se întrerupe şi se baleiază blocurile catch pentru a-l găsi pe cel
corespunzător excepţiei emise

 Dacă un astfel de bloc există se execută instrucţiunile din el. După aceea execuţia
programului continuă cu linia care urmează după ultimul bloc catch sau, dacă blocul
opţional finally există atunci cu instrucţiunile din el

 Prin bloc catch corespunzător unei excepţii se înţelege un bloc catch care are tipul
specificat ca parametru identic cu tipul excepţiei, sau o supraclasă a tipului excepţiei.

 Dacă nu se găseşte nici un catch corespunzător unei excepţii se caută un try


înconjurător şi catch-ul corespunzător (blocurile try-catch pot fi imbricate, aşadar se
repetă procesul)

 Dacă se ajunge cu propagarea până în main excepţia este preluată de handler-ul de


excepţii al maşinii virtuale care determină abandonul programului, cu afişarea unui
mesaj corespunzător

 Un bloc catch se poate termina normal (adică prin epuizarea instrucţiunilor care îl
compun) sau printr-un throw care să emită o nouă excepţie sau tot pe cea primită. În
acest din urmă caz are loc retransmisia excepţiei. 8
Secvenţa finally

 Secvenţa finally reprezintă un mecanism prin care se forţează execuţia unei porţiuni
de cod indiferent dacă o excepţie a apărut sau nu.

public static boolean Cautare(String nume_fis,String cuvant ) throws IOException{


BufferedReader flux_in = null;

try{
flux_in = new BufferedReader (new InputStreamReader(new FileInputStream(nume_fis)));
String linie;
while ((linie = flux_in.readLine()) != null)
if(linie.equals(cuvant)) return true;
return false; //nu s-a gasit cuvantul in fisier
}
finally {
System.out.println("Secventa finally");
if(flux_in != null) flux_in.close();
}
}

 Codul din secventa finally se executa chiar dacă codul din try returnează valoare

9
Blocuri try cu resurse

 O resursă este un obiect care trebuie închis după ce programul a terminat de lucrat
cu el (vezi obiectul numit flux_in din exemplul precedent)
 Într-ul bloc try cu resurse, resursele sunt închise automat la finalul blocului (nu este
necesară închiderea lor explicită, precum în exemplul precedent)
 Orice obiect care implementează interfaţa java.lang.AutoCloseable, poate fi folosit ca
o resursă
 Codul din exemplul precedent poate fi rescris mai comasat dacă se fosloseşte un
bloc try cu resurse, precum în exemplul următor:

public static boolean Cautare2(String nume_fis,String cuvant ) throws IOException{

try(BufferedReader flux_in= new BufferedReader (new InputStreamReader(new


FileInputStream(nume_fis)))){

String linie;
while ((linie = flux_in.readLine()) != null)
if(linie.equals(cuvant)) return true;
return false; //nu s-a gasit cuvantul in fisier
}
}

 Dacă sunt mai multe resurse acestea se separă prin simbolul punct şi virgulă

10
 Exemplu de utilizare a unei excepţii predefinite

import java.io.*;
public class Tablou {
public static void main (String[ ] args) {
int [ ] tab = new int[10];
int i;

try {
for(i=0;;)
tab[i++]=i;
}
catch(IndexOutOfBoundsException e) { //cand i >=tab.length
System.out.println(e);
}
}
}

11
 Exemplu de definire şi utilizare a unei excepţii
class OExceptie extends Exception{
OExceptie(String s){
super(s);
}
}

class OClasa {
public static void oFunctie( ) throws OExceptie{
//. . .
if(eroare) throw new OExceptie( “Mesaj!”);
}
}

class ClientClasa{
public static void main(String [] args){
try{
OClasa. oFunctie( ) ;
}
catch(OExceptie e){
System.out.println(e);
}
}
}

12
6. Dezvoltarea aplicaţiilor cu
interfaţă grafică în Java
– biblioteca Swing

Sl. dr. ing. Raul Robu


www.aut.upt.ro/~rraul

2020-2021, Semestrul 1
6.1 Noţiuni introductive

 Biblioteca Swing este formată dintr-un set de componente grafice configurabile, a căror aspect
poate fi stabilit la rulare. Programatorii pot utiliza Swing pentru a crea aplicaţii grafice puternice.
Componentele din aplicaţiile grafice pot fi uşor extinse sau modificate pentru a le controla apariţia
şi comportamentul

 Swing este scrisă complet în Java, lucru care îi sporeşte portabilitatea

 Swing este o parte a familiei de produse java cunoscuta sub numele Java Foundation Classes (
JFC)

 JFC este o suită de biblioteci proiectate pentru a asista programatorii în crearea de aplicaţii cu
Java. Swing este doar una din cele cinci biblioteci care fac parte din JFC:

2
 AWT (Abstract Window Toolkit) este setul de bază de instrumente GUI livrat cu toate versiunile
de JDK. În vreme ce biblioteca Swing nu reutilizează nici una din componentele mai vechi ale
AWT, ea construieşte unele componente introduse în AWT 1.1.

 Accessibility - permite dezvoltarea de aplicaţii Java care lucrează cu dispozitive de intrare şi


ieşire alternative cum ar fi Screen Readers, braile keyboards, etc, care se adresează persoanelor
cu dizabilităţi.

 2D API - conţine clase pentru implementarea unor stiluri diferite de desenare, forme complexe,
fonturi şi culori

 Drag and Drop – este unul din cele mai comune concepte utilizate în interfeţele grafice de azi.
Utilizatorului i se permite sa facă click şi să tragă un obiect grafic mutându-l într-o altă fereastră cu
rezultate predictibile. Biblioteca permite utilizatorilor să implementeze elemente “dropable”

AWT şi Swing

 În versiunea JDK 1.1 clasele swing trebuie descărcate separat şi incluse ca o arhivă swingall.jar
în classpath, iar în versiunile JDK mai mari sau egale cu 1.2, Swing este integrat

3
Clase AWT
JDK Clase AWT

CLASSPATH Clase Swing


Clase Swing JFC
swingall.jar
Începând cu JDK 1.2
JDK1.1

 Scopul bibliotecii AWT a fost de a oferi un pachet de clase care să-i permită programatorului să
construiască aplicaţii care să arate bine pe toate platformele. Acest scop nu a fost atins. Existau
probleme la accesarea fonturilor şi a unor elemente GUI din sistemul de operare. AWT 1.0 nu era
orientat pe obiecte.

 Lucrurile s-au îmbunătăţit odată cu AWT 1.1, care are o abordare mult mai clară şi orientată pe
obiecte, dar în continuare clasele AWT nu furnizau funcţionalitatea necesară aplicaţiilor dezvoltate
pe scară largă

 Swing reprezintă o regândire a lui AWT. Swing este construit deasupra nucleului bibliotecii AWT1.1

 Swing conţine mult mai multe componente grafice decât predecesorul sau AWT 1.1

4
 Swing depinde mult de mecanismul de manipulare al evenimentelor din AWT 1.1
6.2 JFrame
 Orice aplicaţie cu interfaţă grafică începe cu crearea ferestrei pe care vor fi amplasate controalele

 Fereastra pe care vor fi amplasate controalele este de tip JFrame

Crearea unei ferestre de o dimensiune specificată


JFrame myFrame = new JFrame("Prima fereastra");
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setSize(300, 300);
myFrame.setVisible(true);

Schimbarea culorii de fundal


JFrame myFrame = new JFrame("Prima fereastra");
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setSize(300, 300);
myFrame.getContentPane().setBackground(Color.red);
myFrame.setVisible(true);

5
Amplasarea ferestrei pe ecran
myFrame.setLocation(100, 400); y=400

X=100
Activarea / dezactivarea ferestrei
myFrame.setEnabled(true); ECRAN

Manipularea butoanelor ferestrei


 Fereastra fără butoane

JFrame myFrame = new JFrame();


myFrame.setBounds(100, 200, 300, 300);
myFrame.setUndecorated(true);
myFrame.setVisible(true);

 Fereastră cu butonul de maximizare dezactivat

myFrame.setResizable(false);

6
 Fereastră fără buton de maximizare şi minimizare
 Soluţia 1

myFrame.setUndecorated(true); //asa se alege sa nu aiba niciun un buton dreapta sus


myFrame.getRootPane().setWindowDecorationStyle(JRootPane.FILE_CHOOSER_DIALOG );

 Cele patru containere JFC/ Swing (JFrame, JDialog, JWindow, and


JApplet) sunt ilustrate în relaţie cu clasele AWT pe care le extind.
Toate aceste containere implementează interfaţa RootPaneContainer
si toate deleagă operaţiile lor către JRootPane
 Soluţia 2
JDialog d=new JDialog();
d.setLocation(400, 300);
d.setSize(300, 300);
7
d.setVisible(true);
Shimbarea icoanei aplicaţiei
ImageIcon myIcon = new ImageIcon("chitara.png");
myFrame.setIconImage(myIcon.getImage());

Alegerea unei imagini de fundal

O soluţie:

JFrame frm = new JFrame("Prima aplicatie");

ImageIcon ic=new ImageIcon("Winter.jpg");


JLabel et=new JLabel(ic);

frm.setContentPane(et);

frm.pack();
frm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frm.setVisible(true);

8
Lucrul cu evenimente
 Afişarea unui mesaj la un click pe JFrame şi afişarea în consolă a coordonatelor mouse-ului când
acesta ajunge deasupra JFrame-ului
//…
class EvenimenteJFrame1{
public static void main(String[]args){
JFrame frame=new JFrame("Face-ti click pe JFrame");
frame.addMouseListener(new MouseClick());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400,400);
frame.setVisible(true);
}
}
class MouseClick extends MouseAdapter{

public void mouseClicked(MouseEvent event)


{
JOptionPane.showMessageDialog(null, "S-a facut click pe fereastra");
}

public void mouseEntered( MouseEvent e ) {


System.out.println( "("+ MouseInfo.getPointerInfo().getLocation().x +","+
MouseInfo.getPointerInfo().getLocation().y +")");
}
}

9
 Afişarea coordonatelor mouse-ului la trecerea acestuia deasupra ferestrei
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import javax.swing.JFrame;

class EvenimenteJFrame2{
public static void main(String[]args){
JFrame f = new JFrame();
f.getContentPane(). addMouseMotionListener(new CoordonateMiscareMouse());
f.setSize(600, 600);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
}

class CoordonateMiscareMouse implements MouseMotionListener {

@Override
public void mouseDragged(MouseEvent e) {
// TODO Auto-generated method stub
mouseMoved(e);
}

@Override
public void mouseMoved(MouseEvent e) {
// TODO Auto-generated method stub
int mX = (int) e.getPoint().getX();
int mY = (int) e.getPoint().getY();
System.out.println(mX+" "+mY);
}
}
10
6.3 JLabel
Crearea unui control JLabel şi adăugarea acestuia pe JFrame
JFrame frm=new JFrame();
frm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frm.setSize(300, 300);
frm.setVisible(true);

JLabel et=new JLabel("Ana are...");


frm.getContentPane( ).add(et);

Modificarea textului de pe un control JLabel


//…
class Eticheta extends Thread{
public static void main(String[]args){ După o secundă....
JFrame frm=new JFrame();
frm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frm.setSize(300, 300);
frm.setVisible(true);

JLabel et=new JLabel("Ana are...");


frm.getContentPane( ).add(et);
try {
sleep(1000);
et.setText("...mere");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} 11
Modificarea culorii textului
et.setForeground(Color.RED );

Modificarea culorii de umplere a etichetei


et.setOpaque(true);
et.setBackground(Color. green);
//Color c=new Color(0,128,0);
//et.setBackground(c);

 culoarea de fundal a controlului JLabel nu se modifica dacă nu este setată opacitatea


 după ce se modifică culoarea de umplere se observă că eticheta se întinde de toată suprafaţa
JFrame-ului

Modificarea dimensiunii şi poziţiei


//frm.setLayout(null);
et.setLocation(10, 10);
et.setSize(200, 50);

Modificarea fontului
et.setFont(new Font("Serif", Font.BOLD, 48));

12
Alinierea textului în cadrul etichetei

et.setHorizontalAlignment(JLabel.LEFT);

et.setHorizontalAlignment(JLabel.RIGHT);

Bordarea etichetei

 Se utilizează interfaţa Border care descrie un obiect ce e capabil să redea un chenar în jurul unei
componente swing

Border blackline, raisedetched, loweredetched,raisedbevel, loweredbevel, empty;

blackline = BorderFactory.createLineBorder(Color.black);
raisedetched = BorderFactory.createEtchedBorder(EtchedBorder.RAISED);
loweredetched = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED);
raisedbevel = BorderFactory.createRaisedBevelBorder();
loweredbevel = BorderFactory.createLoweredBevelBorder();
empty = BorderFactory.createEmptyBorder();

et.setBorder(raisedetched);

13
Evenimentele JLabel-ului
//… class MouseStaff implements MouseListener{
class Eticheta2{ private int nr_click=0;
public static void main(String []args){ private int nr_vizite=0;
JFrame frm=new JFrame(); private JLabel et;

frm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); public MouseStaff (JLabel et){


frm.setSize(300, 300); this.et=et;
frm.setVisible(true); }

frm.setLayout(null); public void mouseClicked(MouseEvent e) {


JLabel et=new JLabel("Numar de click-uri: 0"); nr_click++;
et.setText("Numar de click-uri:"+nr_click);
et.setOpaque(true);
et.setBackground(Color.white); }

et.setLocation(10, 10); public void mouseEntered(MouseEvent e) {


et.setSize(200, 50); nr_vizite++;
et.addMouseListener(new MouseStaff(et)); et.setText("Numarul de vizite:"+nr_vizite);
frm.add(et); }
}
} public void mouseExited(MouseEvent e) {}

public void mousePressed(MouseEvent e) {}

public void mouseReleased(MouseEvent e) {}


}

14
6.4 JTextField
Crearea unei casete de text particulatizate
JFrame myFrame = new JFrame("JTextFied");
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setSize(300, 300);
myFrame.getContentPane().setBackground(Color.yellow);

myFrame.setLayout(null);

JTextField myText=new JTextField();


myText.setBounds(10, 10, 200, 40);

myText.setFont(new Font("Times New Roman“, Font.BOLD + Font.ITALIC,40));

myText.setBackground(Color.blue );
myText.setToolTipText(“A kind of Magic");
myText.setForeground(Color.white);

myFrame.getContentPane(). add(myText);
myFrame.setVisible(true);

15
Evenimente
Apăsarea tastei Enter
myText.addActionListener(new EnterApasat(myText));
//…
class EnterApasat implements ActionListener
{
private JTextField myText;
public EnterApasat(JTextField myText) {
this.myText=myText;
}

public void actionPerformed(ActionEvent e) {


JOptionPane.showMessageDialog(null, myText.getText());

}
}

16
Apăsarea oricărei taste

//…
myText.getDocument().addDocumentListener(new TastaApasata(myText));
//…
class TastaApasata implements DocumentListener
{
private JTextField myText;
public TastaApasata(JTextField myText) {
this.myText=myText;
}
public void changedUpdate(DocumentEvent e) {
System.out.println(myText.getText());
}
public void insertUpdate(DocumentEvent e) {
System.out.println(myText.getText());
}
public void removeUpdate(DocumentEvent e) {
System.out.println(myText.getText());
}
}

17
6.5 JPanel
Crearea unui JPanel particulatizat

JFrame myFrame = new JFrame("JPanel");


myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setSize(300, 300);
myFrame.getContentPane().setBackground(Color.WHITE);

myFrame.setLayout(null);

JPanel myPanel=new JPanel();

myPanel.setToolTipText("JPANEL");
myPanel.setBounds(10, 20, 100, 200);
myPanel.setBackground(Color.MAGENTA);

Border blackline, raisedetched, loweredetched,raisedbevel, loweredbevel, empty;

blackline = BorderFactory.createLineBorder(Color.black);
raisedetched = BorderFactory.createEtchedBorder(EtchedBorder.RAISED);
loweredetched = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED);
raisedbevel = BorderFactory.createRaisedBevelBorder();
loweredbevel = BorderFactory.createLoweredBevelBorder();
empty = BorderFactory.createEmptyBorder();

myPanel.setBorder(raisedbevel);

myFrame.getContentPane().add(myPanel);
myFrame.setVisible(true);
18
Lucrul cu evenimente
Mouse-ul şi suprafaţa Jpanel-ului

//…
myPanel.addMouseListener(new MouseDeasupa());
//…
class MouseDeasupa implements MouseListener
{
public void mouseClicked(java.awt.event.MouseEvent arg0) {
System.out.println("Mouse click");
}

public void mouseEntered(java.awt.event.MouseEvent arg0) {


System.out.println("Mouse deasupra suprafetei");
}

public void mouseExited(java.awt.event.MouseEvent arg0) {


System.out.println("Mouse in afara suprafetei");
}

public void mousePressed(java.awt.event.MouseEvent arg0) {


System.out.println("Mouse apasat");
}

public void mouseReleased(java.awt.event.MouseEvent arg0) {


System.out.println("Mouse eliberat");
}
}
19
 Afişarea coordonatelor mouse-ului la trecerea acestuia deasupra JPanel-ului
//…
myPanel.addMouseMotionListener(new MouseMove());
//…

class MouseMove implements MouseMotionListener


{

public void mouseDragged(java.awt.event.MouseEvent e) {}

public void mouseMoved(java.awt.event.MouseEvent e) {


System.out.println("("+e.getX()+","+e.getY()+")");
}
}

20
6.6 JButton
Crearea unui control JButton
//…
JFrame myFrame = new JFrame("JButton");

myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setSize(300, 300);
myFrame.getContentPane().setLayout(null);

JTextField myText=new JTextField();


myText.setBounds(10, 10, 100, 20);

JButton myButton=new JButton("Apasa-ma!");


myButton.setBounds(120, 10, 100, 20);
myButton.addActionListener(new ButtonClick(myText));

myFrame.getContentPane(). add(myText);
myFrame.getContentPane(). add(myButton);
myFrame.setVisible(true);
//…
class ButtonClick implements ActionListener
{
private JTextField myText;
public ButtonClick(JTextField myText){
this.myText=myText;
}
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(null, myText.getText());

} 21
}
Crearea unui buton cu imagine pe fundal
File f=new File("push.jpg");
Image img = ImageIO.read(f);

Image resizedImage = img.getScaledInstance(myButton2.getWidth(), myButton2.getHeight(), 0);

myButton2.setIcon(new ImageIcon(resizedImage));
//myButton2.setIcon(new ImageIcon("push.jpg"));

myFrame.getContentPane().add(myButton2);

22
6.7 Amplasarea controalelor în cadrul containerelor
 Swing permite gruparea unor controale împreună cu ajutorul unor containere. Un container este o
componentă ce poate conţine alte componente

 Modul în care sunt amplasate controalele în cadrul unui container este controlat de o componentă
numită LayoutManager (Gestionar de Componente). Aceasta decide cum sunt amplasate
controalele în funcţie de tipul LayoutManager-ului utilizat şi de ordinea în care au fost adăugate
controalele în container. Dimensiunea, forma şi amplasamentul controalelor diferă de la un layout
manager la altul. Un layout manager adaptează dimensiunea şi poziţia controalelor dacă se
schimbă dimensiunea ferestrei

 Metoda care permite stabilirea tipului de layout manager utilizat este:


setLayout(LayoutManger mgr)

 Tipuri de layout manager


 BorderLayout – layout-ul implicit pentru containerul JFrame-ului
 FlowLayout
 GridLayout
 BoxLayout
 CardLayout
 GridBagLayout
 GroupLayout
 SpringLayout 23
Amplasarea controalelor în poziţii absolute

 Se utilizează atunci când containerul conţine componente a


căror dimensiune nu este afectată de modificarea dimensiunii
containerului

 Se dezactivează componenta LayoutManager a container-ului:


myFrame.getContentPane().setLayout(null);

 Se utilizează metoda setBounds pentru fiecare componentă din


container, cu scopul de a stabili poziţia şi dimensiunea
componentei
JFrame myFrame = new JFrame("Login"); lblUsername.setBounds(20,10,70,20);
myFrame.setDefaultCloseOperation txtUsername.setBounds(90, 10, 150, 20);
(JFrame.EXIT_ON_CLOSE); lblPassword.setBounds(20,40,70,20);
myFrame.setLocation(400,400); txtPassword.setBounds(90, 40, 150, 20);
myFrame.setSize(270, 140); btnOk.setBounds(20, 70, 100, 20);
myFrame.getContentPane().setLayout(null); btnCancel.setBounds(140, 70, 100, 20);

JLabel lblUsername=new JLabel("Username"); myFrame.getContentPane().add(lblUsername);


JTextField txtUsername=new JTextField(); myFrame.getContentPane().add(txtUsername);
myFrame.getContentPane().add(lblPassword);
JLabel lblPassword=new JLabel("Password"); myFrame.getContentPane().add(txtPassword);
JPasswordField txtPassword=new JPasswordField(); myFrame.getContentPane().add(btnOk);
myFrame.getContentPane().add(btnCancel);
JButton btnOk=new JButton("Ok"); myFrame.setVisible(true);
JButton btnCancel=new JButton("Cancel"); 24
BorderLayout

 Acesta este LayoutManager-ul implicit pe care Container-ul JFrame-ului îl utilizează

 BorderLayout împarte container-ul în 5 regiuni (sus, jos, stânga, dreapta şi centru)

 La adăugarea unui control se poate utiliza metoda add supraîncărcată care primeşte ca şi parametri
controlul de adăugat dar şi o constantă care corespunde poziţiei în care va fi amplasat controlul în
cadrul container-ului

JFrame myFrame = new JFrame("BorderLayout");


myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setSize(300, 300);
//myFrame.getContentPane().setLayout(new BorderLayout());

myFrame.getContentPane().add(BorderLayout.NORTH, new JButton("Nord"));


myFrame.getContentPane().add(BorderLayout.SOUTH, new JButton("Sud"));
myFrame.getContentPane().add(BorderLayout.EAST, new JButton("Est"));
myFrame.getContentPane().add(BorderLayout.WEST, new JButton("Vest"));
myFrame.getContentPane().add(BorderLayout.CENTER,new JButton("Centru"));
myFrame.setVisible(true);

25
FlowLayout
 Controalele “curg” pe fereastra, de la stânga la dreapta până când se umple spaţiul liber pe
orizontală. Apoi se trece la rândul următor şi controalele îşi continuă aşezarea în linie, unul lângă altul

 Controalele au dimensiunea lor “naturală”. De exemplu dimensiunea unui control JButton sau a unui
control JLabel se adaptează la dimensiunea Stringului sau. Toate controalele vor fi compactate la
dimensiunea lor cea mai mica

 Dacă se schimbă dimensiunea ferestrei, layout manager-ul va schimba amplasamentul controalelor în


mod corespunzător

 Dimensiunea unui control poate fi modificată cu ajutorul metodei


setPreferredSize()

JFrame myFrame = new JFrame("FlowLayout");


myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setSize(300, 300);
myFrame.getContentPane().setLayout(new FlowLayout());

for (int i=1;i<=20;i++){


myFrame.getContentPane().add(new JButton("Button"+i));

}
myFrame.setVisible(true);

26
GridLayout
 GridLayout permite construirea unui tabel de componente şi pe măsură ce acestea sunt adăugate
sunt amplasate de la stânga la dreapta şi de sus în jos în celulele tabelului

 Fiecare componentă ocupă tot spaţiul disponibil în celula în care se află şi toate celulele au acceaşi
dimensiune

 În constructor se specifică numărul de rânduri şi numărul de coloane necesare

JFrame myFrame = new JFrame("FlowLayout");


myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setSize(300, 300);
myFrame.getContentPane().setLayout(new GridLayout(7,3));

for (int i=1;i<=20;i++){


myFrame.getContentPane().add(new JButton("Button"+i));

}
myFrame.setVisible(true);

27
BoxLayout
 Cu ajutorulul lui Boxlayout se pot amplasa controalele unele lângă altele atât pe orizontală cât şi pe
verticală

JFrame myFrame = new JFrame("BoxLayout");


myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setSize(300, 300);
Container cont=myFrame.getContentPane();
myFrame.getContentPane().setLayout(new BoxLayout(cont, BoxLayout.Y_AXIS));

for (int i=1;i<=10;i++){


JButton btn =new JButton("Button"+i);
btn.setAlignmentX(Component.CENTER_ALIGNMENT);
myFrame.getContentPane().add(btn);
}

myFrame.setVisible(true);

28
CardLayout
 Clasa CardLayout administrează de regulă două sau mai multe componente (de regulă instanţe a lui
JPanel) care folosesc acelaşi spaţiu de afişare. Când se utilizează clasa CardLayout utilizatorul poate
alege între componente folosind de exemplu un comboBox

GridBagLayout
 GridBagLayout este unul din cei mai flexibili şi complecşi manageri de aspect (LayoutManager) pe
care platforma Java îi oferă. GridBagLayout plasează componentele într-un tabel cu rânduri şi
coloane, permiţând anumitor componente să se întindă pe mai multe rânduri sau coloane. Rândurile
şi coloanele pot avea înălţimi şi lăţimi diferite. GridBagLayout plasează componentele în dreptunghiuri
(celule) şi apoi cu ajutorul metodei setPreferredSize se poate stabili cât de mare va fi celula.

GroupLayout
 GroupLayout lucrează cu două layout-uri: unul vertical şi unul orizintal. Layout-ul este definit pentru
fiecare dimensiune separat. Layout-ul corespunzător fiecărei axe este total independent de cel a
celeilalte axe. Când se lucrează asupra unei singure axe se rezolva la un moment dat doar o jumătate
de problemă. Avantajul este că e mai uşor de gestionat o singură dimensiune decât două dimeniuni la
un moment dat.

SpringLayout
 SpringLayout este un layout manager foarte flexibil, care poate să emuleze multe din caracteristicile
altor layout manageri. SpringLayout se situează la un nivel de jos şi de accea se recomandă mai
degrabă utilizarea lui împreună cu un GUI builder decât din cod

29
6.8 JComboBox
 Adăugarea listei de opţiuni a JComboBox-ului
JFrame myFrame = new JFrame("JComboBox");
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setSize(200, 200);
myFrame.setLayout(null);

String []items = { "Chitara", "Baterie", "Orga"};


JComboBox myCombo=new JComboBox(items);
myCombo.addItem("Microfon");
myCombo.setBounds(50,50, 100, 20);

myFrame.getContentPane().add(myCombo);
myFrame.setVisible(true);

 Afişarea unui mesaj când mouse-ul stă deasupra controlului


myCombo.setToolTipText("Instrumente muzicale");

 Schimbarea culorilor
myCombo.setBackground(Color.white );
myCombo.setForeground(Color.magenta);

Object child = myCombo.getAccessibleContext().getAccessibleChild(0);


BasicComboPopup popup = (BasicComboPopup)child;
JList list = popup.getList();
list.setSelectionBackground(Color.yellow);
list.setSelectionForeground(Color.green);
30
 Afişarea unui mesaj la selectarea unui item

//….
myCombo.addActionListener(new SchimbareSelectie(myCombo));
//….

class SchimbareSelectie implements ActionListener


{
private JComboBox myCombo;
public SchimbareSelectie(JComboBox myCombo){
this.myCombo=myCombo;
}

public void actionPerformed(ActionEvent e) {


JOptionPane.showMessageDialog(null,myCombo.getSelectedItem());
}

31
6.9 JCheckBox
//…
public class ExempluCheckBox {
public static void main(String[] args) {
JFrame myFrame = new JFrame("JCheckBox");
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setSize(300, 300);
myFrame.setLayout(new GridLayout(7,1));

JLabel myLabel=new JLabel("Alegeti formatiile care va plac:");

JCheckBox chk1=new JCheckBox("Queen",true);


JCheckBox chk2=new JCheckBox("Nirvana",true);
JCheckBox chk3=new JCheckBox("Metallica",true);
JCheckBox chk4=new JCheckBox("Sepultura",false);
JButton btn=new JButton("Afiseza selectate");
JLabel lblAfis=new JLabel();
lblAfis.setOpaque(true);
lblAfis.setBackground(Color.white);

myFrame.getContentPane(). add(myLabel);
myFrame.getContentPane(). add(chk1);
myFrame.getContentPane(). add(chk2);
myFrame.getContentPane(). add(chk3);
myFrame.getContentPane(). add(chk4);
myFrame.getContentPane(). add(btn);
myFrame.getContentPane(). add(lblAfis);

btn.addActionListener(new AfiseazaSelectate(lblAfis,chk1,chk2,chk3,chk4));

myFrame.setVisible(true);
} 32
}
class AfiseazaSelectate implements ActionListener
{
private JLabel lblAfis;
private JCheckBox chk1;
private JCheckBox chk2;
private JCheckBox chk3;
private JCheckBox chk4;
private String text;

public AfiseazaSelectate(JLabel lblAfis,JCheckBox chk1,JCheckBox chk2,JCheckBox


chk3,JCheckBox chk4){
this.lblAfis=lblAfis;
this.chk1=chk1;
this.chk2=chk2;
this.chk3=chk3;
this.chk4=chk4;
text="";
}

public void actionPerformed(ActionEvent e) {


text="";
if (chk1.isSelected())
text+=chk1.getText()+" ";
if (chk2.isSelected())
text+=chk2.getText()+" ";
if (chk3.isSelected())
text+=chk3.getText()+" ";
if (chk4.isSelected())
text+=chk4.getText()+" ";
lblAfis.setText(text);
}
} 33
 Schimbarea stării unui JCheckBox

//…
chk1.addActionListener(new SchimbareStare(chk1));
//…

class SchimbareStare implements ActionListener


{
private JCheckBox myChk;

public SchimbareStare(JCheckBox myChk){


this.myChk=myChk;
}

public void actionPerformed(ActionEvent e) {


String text;
if (myChk.isSelected())
text=" selectata!";
else
text=" deselectata!";
JOptionPane.showMessageDialog(null,myChk.getText()+text);
}
}

34
6.10 JRadioButton
//Exemplu cu clase imbricate
//…

class Radio_Frame extends JFrame { //clasa exterioara (OuterClass)


private JLabel lblAfis;
private JRadioButton rdo1;
private JRadioButton rdo2;
private JRadioButton rdo3;
private JRadioButton rdo4;
private String text;

public Radio_Frame(){
super("JRadioButton");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(300, 300);
setLayout(new GridLayout(7,1));

JLabel myLabel=new JLabel("Alegeti formatia preferata:");

rdo1=new JRadioButton("Queen", false);


rdo2=new JRadioButton("Nirvana",true);
rdo3=new JRadioButton("Metallica", false);
rdo4=new JRadioButton("Sepultura",false);
JButton btn=new JButton("Afiseza formatia selectata");
lblAfis=new JLabel();
lblAfis.setOpaque(true);
lblAfis.setBackground(Color.white);

ButtonGroup group = new ButtonGroup();


group.add(rdo1);
group.add(rdo2);
group.add(rdo3);
group.add(rdo4);

35
getContentPane(). add(myLabel);
getContentPane(). add(rdo1);
getContentPane(). add(rdo2);
getContentPane(). add(rdo3);
getContentPane(). add(rdo4);
getContentPane(). add(btn);
getContentPane(). add(lblAfis);

btn.addActionListener(new AfiseazaSelectate2());

rdo1.addItemListener(new SchimbareStare2());
}

//clasa interioara (InnerClass)


class AfiseazaSelectate2 implements ActionListener
{
public void actionPerformed(ActionEvent e) {
text="";
if (rdo1.isSelected())
text+=rdo1.getText()+" ";
if (rdo2.isSelected())
text+=rdo2.getText()+" ";
if (rdo3.isSelected())
text+=rdo3.getText()+" ";
if (rdo4.isSelected())
text+=rdo4.getText()+" ";

lblAfis.setText(text);
}
}

36
//clasa interioara (InnerClass)
class SchimbareStare2 implements ItemListener
{

public void itemStateChanged(ItemEvent e) {


String text;
if (rdo1.isSelected())
text=" selectata!";
else
text=" deselectata!";
JOptionPane.showMessageDialog(null,rdo1.getText()+text);
}
}
}//sfarsitul clasei exterioare Radio_Frame (OuterClass)

class Main5{
public static void main(String[] args) {
Radio_Frame frm=new Radio_Frame();
frm.setVisible(true);
}
}

37
6.11 Lucru cu evenimente
 O componentă poate iniţia un eveniment. Fiecare tip de eveniment este reprezentat de o clasă
distinctă. Atunci când un eveniment se produce, acesta este “auzit” de către unul sau mai mulţi
"ascultători“ (listeners), care acţionează pe acel eveniment. Astfel, sursa a unui eveniment şi locul în
care evenimentul este tratat pot fi separate. Codul care se execută atunci când se produce
evenimentul trebuie editat de către programator.

 Fiecare ascultător de evenimente (event listener) este un obiect al unei clase care implementează un
tip particular de interfaţă de ascultare. Deci când vrem să scriem o rutină de tratare a unui eveniment
trebuie să creăm un obiect ascultător pe care trebuie să-l asociem cu componenta care produce
evenimentul. Această asociere se realizează cu ajutorul metodei an addEventListener(), în care
“Event” reprezintă tipul de eveniment care se ascultă.

 Când se creează o clasă listener, singura restricţie este ca aceasta să implementeze interfaţa
corespunzătoare

 Toate componentele Swing includ metodele addEventListener() şi removeEventListener() astfel


încât tipurile potrivite de listeners pot fi adăugate şi eliminate în / din fiecare componentă. Event
reprezintă în fiecare caz şi argumentul metodei, de exemplu addMyListener(MyListener m)

 Tabelul următor cuprinde evenimentele de bază, ascultătorii şi metodele, împreună cu controalele de


bază care suportă aceste evenimente furnizând metode de genul addEventListener() şi
removeEventListener()

38
39
 După cum se vede în tabel fiecare componentă suportă numai anumite tipuri de evenimente

 Pentru a crea un eveniment pentru o componentă:


 trebuie creată o clasă care implementează interfaţa ce conţine metodele de interes.
 o instanţă a clasei repective se transmite ca parametru metodei addEventListener a
componentei

De exemplu, codul care se execută când se face click pe un buton:


myButton.addActionListener(new ButtonClick());

//…

class ButtonClick implements ActionListener


{
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
JOptionPane.showMessageDialog(null, “S-a facut click pe buton!");
}
}

40
Interfaţa listener Metodelele interfeţei
ActionListener actionPerformed(ActionEvent)
AdjustmentListener adjustmentValueChanged( AdjustmentEvent)
ComponentListener componentHidden(ComponentEvent)
componentShown(ComponentEvent)
componentMoved(ComponentEvent)
componentResized(ComponentEvent)
ContainerListener componentAdded(ContainerEvent)
componentRemoved(ContainerEvent)
FocusListener focusGained(FocusEvent)
focusLost(FocusEvent)
KeyListener keyPressed(KeyEvent)
keyReleased(KeyEvent)
keyTyped(KeyEvent)
MouseListener mouseClicked(MouseEvent)
mouseEntered(MouseEvent)
mouseExited(MouseEvent)
mousePressed(MouseEvent)
mouseReleased(MouseEvent)
MouseMotionListener mouseDragged(MouseEvent)
mouseMoved(MouseEvent)
WindowListener windowOpened(WindowEvent)
windowClosing(WindowEvent)
windowClosed(WindowEvent)
windowActivated(WindowEvent)
windowDeactivated(WindowEvent)
windowIconified(WindowEvent)
windowDeiconified(WindowEvent)
41
ItemListener itemStateChanged(ItemEvent)
6.12 JList
 Crearea unei componente JList cu elemente; elementele sunt transmise cu ajutorul constructorului
clasei JList prin intermediul unui vector de String-uri

import javax.swing.JFrame;
import javax.swing.JList;

class MyFrame extends JFrame{

public MyFrame(){
super("JList");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(300, 300);
getContentPane().setLayout(null);

String []items = { "Chitara", "Baterie", "Orga"};


JList myList=new JList(items);
myList.setBounds(100,50, 100, 150);
getContentPane(). add(myList);
}
}

class MainApp{
public static void main(String []args){
MyFrame frm=new MyFrame();
frm.setVisible(true);
}
}

42
 Adăugarea unor elemente în JList după ce componenta a fost creată; În comparaţie cu varianta
precedentă, în locul vectorului de String-uri se transmite un obiect de tip DefaultListModel, acesta
fiind cel care oferă metoda addElement cu care se pot adăuga item-uri în JList

import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JList;

class MyFrame extends JFrame{


public MyFrame(){
super("JList");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(300, 300);
getContentPane().setLayout(null);

DefaultListModel listModel = new DefaultListModel();


JList myList=new JList(listModel);
listModel.addElement("Chitara");
listModel.addElement("Baterie");
listModel.addElement("Orga");
myList.setBounds(100,50, 100, 150);
getContentPane(). add(myList);
}
}

class MainApp{
public static void main(String []args){
MyFrame frm=new MyFrame();
frm.setVisible(true);
}
} 43
 Accesarea tuturor elementelor unui JList; Se utilizează metoda getModel() care returnează un
obiect de tip ListModel. Acesta reprezintă modelul de date care deţine lista de elemente afişate de o
componentă JList. Metoda getSize () returnează numărul de elemente din JList, iar accesarea unui
anumit element se poate face cu ajutorul metodei getElementAt()

for(int i = 0; i < myList.getModel().getSize(); i++)


System.out.println(myList.getModel().getElementAt(i));

44
 Afişarea doar a elementelor selectate într-o componentă JList; Se crează un vector de întregi
care conţine indicii elementelor selectate, în ordine crescătoare (cu ajutorul metodei
getSelectedIndices).
class JListExemplu extends JFrame
{
private JPanel mainPanel;
private JList lstLuni;
private JScrollPane jspScroll;
private JButton btnAfiseaza;
private String luni[] = {"Ianuarie", "Februarie", "Martie", "Aprilie", "Mai", "Iunie", "Iulie","August",
"Septembrie", "Octombrie", "Noiembrie", "Decembrie"};

public JListExemplu(){
mainPanel = new JPanel();
mainPanel.setLayout(new BorderLayout());

lstLuni = new JList(luni);


jspScroll = new JScrollPane(lstLuni);
btnAfiseaza = new JButton("Afiseaza in consola");
btnAfiseaza.addActionListener(new AfiseazaSelectate());
mainPanel.add(jspScroll, BorderLayout.CENTER);
mainPanel.add(btnAfiseaza, BorderLayout.SOUTH);
this.setContentPane(mainPanel);
this.setTitle("Exemplu cu JList");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(300, 200);
}

class AfiseazaSelectate implements ActionListener


{
public void actionPerformed(ActionEvent e)
{
int indici_selctate[] = lstLuni.getSelectedIndices();
System.out.println("Lunile selectate: ");
for (int i = 0; i < indici_selctate.length; i++) {
String luna = (String) lstLuni.getModel().getElementAt(indici_selctate[i]);
System.out.println(" " + luna);
}
}
} 45
}
 Se parcurge vectorul de indici şi cu ajutorul acestuia si al metodei getElementAt() se accesează
elementele selectate

 În cazul în care într-un JList se introduc multe elemente, iar spaţiul necesar pentru a afişa aceste
elemente este mai mare decât dimensiunea JList-ului, atunci se va realiza o tunchiere: nu vor fi
afişate decât o parte din elemente. Această problemă se poate rezolva introducând JList-ul într-un
container JScrollPane, care permite vizualizarea prin derulare, atât pe verticală cât şi pe orizontală,
a întregului obiect JList (vezi exemplul precedent)

 Ştergerea elementelor selectate în JList; Pentru ştergerea elementelor selectate este necesară
utilizarea metodei removeElementAt() a obiectului de tip DefaultListModel; deci este necesară
crearea unui astfel de obiect şi asocierea lui cu obiectul JList, fie prin constructor, fie cu ajutorul
metodei setModel(ListModel)
int []n=myList.getSelectedIndices();

for(int i=n.length-1;i>=0;i--){
listModel.removeElementAt(n[i]);
}

 Particularizarea unei componente JList prin modificarea


culorilor şi fontului:
myList.setBackground(Color.yellow);
myList.setForeground(Color.red);
myList.setSelectionBackground(Color.blue);
myList.setSelectionForeground(Color.white);

46
 Evenimente - Afişarea unui mesaj când se face click pe un element a unei componente JList

//…
class myFrame extends JFrame{
private JList myList;

public MyFrame(){
super("JList");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(300, 300);
getContentPane().setLayout(null);

String []items = { "Chitara", "Baterie", "Orga"};


myList=new JList(items);
myList.setBounds(100,50, 100, 150);
myList.addMouseListener(new JListClick());
getContentPane(). add(myList);
}

class JListClick implements MouseListener


{
public void mouseClicked(MouseEvent e) {
int v[]=myList.getSelectedIndices();
JOptionPane.showMessageDialog(null,myList.getModel().getElementAt(v[0]));
}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
}
}//end MyFrame
47
6.13 Crearea unui meniu

 Obiecte necesare:

 JMenuBar – bara care conţine meniul. Pe aceasta se adaugă obiecte de tip JMenu. Când un
utilizator selectează un obiect de tip JMenu, meniul de tip popup asociat acestuia este afişat,
permiţând utilizatorului să selecteze unul din elementele JMenuItems de pe el

 Un obiect JMenu poate de asemenea să conţină şi separatori (JSeparator)

 JMenuItem – Este un buton care stă într-o listă. Când un utilizator selectează butonul acţiunea
asociată cu acesta se execută

 JCheckBoxMenuItem - O opţiune de meniu care poate fi selectată sau deselectată. Dacă este
selectată opţiunea de meniu apare însoţită de o bifă. Pentru a determina starea componentei, se
pot utiliza metode isSelected, getState, iar pentru a seta componenta setSelected, setState

 JRadioButtonMenuItem - o implementare a unui buton radio într-un meniu.


JRadioButtonMenuItem este o opţiune de meniu care este parte dintr-un grup de opţiuni de meniu,
din care doar una poate fi selectată. Selectarea unui element din grup face ca elementul care este
selectat să devină neselectat. Pentru a controla comportamentul unui grup de controale de tip
radio este necesară utilizarea unui obiect de tip ButtonGroup

48
Exemplu:
//…
class MyFrame2 extends JFrame implements ActionListener{
JTextArea myTextArea;

public MyFrame2(){
super("JMeniu");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(300, 300);
getContentPane().setLayout(new BorderLayout());

JMenuBar myBar=new JMenuBar();

JMenu myMenu1=new JMenu("Meniu 1");


JMenu myMenu2=new JMenu("Meniu 2");
JMenu submenu = new JMenu("Submeniu");

myMenu1.setMnemonic(KeyEvent.VK_M);

JMenuItem mnuItem1=new JMenuItem("Optiunea 1");


JMenuItem mnuItem2=new JMenuItem("Optiunea 2");
JCheckBoxMenuItem mnuItem3=new JCheckBoxMenuItem("Optiunea 3");
JMenuItem mnuItem4=new JMenuItem("Optiunea 4");
JMenuItem mnuItem5=new JMenuItem("Optiunea 5");
JRadioButtonMenuItem mnuItem6=new JRadioButtonMenuItem("Optiunea 6");
JRadioButtonMenuItem mnuItem7=new JRadioButtonMenuItem("Optiunea 7");
submenu.setMnemonic(KeyEvent.VK_S);
submenu.add(mnuItem4);
submenu.add(mnuItem5);

mnuItem1.setMnemonic(‘O');
mnuItem1.setAccelerator(KeyStroke.getKeyStroke("control O")); 49
ButtonGroup group = new ButtonGroup();
mnuItem6.setSelected(true);

group.add(mnuItem6);
group.add(mnuItem7);

myMenu1.add(mnuItem1);
myMenu1.add(mnuItem2);
myMenu1.addSeparator();
myMenu1.add(mnuItem3);
myMenu1.add(submenu);
myMenu1.addSeparator();
myMenu1.add(mnuItem6);
myMenu1.add(mnuItem7);

myTextArea=new JTextArea();
myTextArea.setEditable(false);

myBar.add(myMenu1);
myBar.add(myMenu2);
getContentPane(). add(myBar,BorderLayout .NORTH);
getContentPane(). add(myTextArea,BorderLayout .CENTER);

mnuItem1.addActionListener(this);
mnuItem2.addActionListener(this);
mnuItem3.addActionListener(this);
mnuItem4.addActionListener(this);
mnuItem5.addActionListener(this);
mnuItem6.addActionListener(this);
mnuItem7.addActionListener(this);
}//constructor
50
public void actionPerformed(ActionEvent e) {
JMenuItem source = (JMenuItem)(e.getSource());
String s = "S-a ales " + source.getText()+" de meniu " + " (" +
getClassName(source) + ")\n";
myTextArea.append(s);
}

protected String getClassName(Object o) {


String classString = o.getClass().getName();
int dotIndex = classString.lastIndexOf(".");
return classString.substring(dotIndex+1);
}
}

class Main2{
public static void main(String []args){
MyFrame2 frm=new MyFrame2();
frm.setVisible(true);
}
}

51
6.14 JTabbedPane
 Un obiect JTabbedPane este o componentă care permite utilizatorului să comute între grupuri de
componente, făcând click pe un tab care are un anumit titlu
//…
class MyFrame4 extends JFrame{
public MyFrame4(){
super("JTabbedPane");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(300, 300);

JTabbedPane tabbedPane = new JTabbedPane();

JPanel myPanel1 = new JPanel();


JPanel myPanel2 = new JPanel();

myPanel1.add(new JButton("Button 1"));


myPanel1.add(new JButton("Button 2"));
myPanel1.add(new JButton("Button 3"));

myPanel2.add(new JTextField("TextField", 20));

tabbedPane.addTab("Tabul 1", myPanel1);


tabbedPane.addTab("Tabul 2", myPanel2);
getContentPane(). add(tabbedPane, BorderLayout.CENTER);
}
}
class Main4{
public static void main(String []args){
MyFrame4 frm=new MyFrame4();
frm.setVisible(true);
} 52
}
6.15 JToolBar
import java.awt.*;
import javax.swing.*;

class MyFrame3 extends JFrame{


public MyFrame3(){
super("JMeniu");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(300, 300);
getContentPane().setLayout(new BorderLayout());

JToolBar myBar=new JToolBar();

getContentPane().add(myBar,BorderLayout.NORTH);
JButton btnFirst=new JButton();
JButton btnPrevious=new JButton();
JButton btnNext=new JButton();
JButton btnLast=new JButton();
JTextField txtAfis=new JTextField();

btnFirst.setIcon(new ImageIcon("MoveFirst.png"));
btnPrevious.setIcon(new ImageIcon("MovePrevious.png"));
btnNext.setIcon(new ImageIcon("MoveNext.png"));
btnLast.setIcon(new ImageIcon("MoveLast.png"));

myBar.add(btnFirst);
myBar.add(btnPrevious);
myBar.add(txtAfis);
myBar.add(btnNext);
myBar.add(btnLast);
}
}
class Main3{
public static void main(String []args){
MyFrame3 frm=new MyFrame3();
frm.setVisible(true);
}
} 53
6.15 JTable
package exemplu;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;

class MyFrame1 extends JFrame{


MyFrame1(){
JTable tabel;
setSize(300, 300);
setTitle("JTable");

String[][] date = {
{ "1", "Ana", "20" },
{ "2", "Maria", "30" },
};

String[] coloane = { "Id", "Nume", "Varsta" };


tabel = new JTable(date, coloane);

JScrollPane sp = new JScrollPane(tabel);

getContentPane().add(sp);
setVisible(true);
}
public static void main(String[] args)
{
new MyFrame1();

}
} 54
6.16 Crearea fişierului rulabil (JAR)
 După ce dezvoltarea programului cu interfaţă grafică a fost finalizată se poate crea
fişierul rulabil

 În Eclipse se alege comanda File > Export > Runnable JAR file

 Se dă next şi în următoarea fereastră se alege clasa mainului – numele proiectului şi


se introce numele fişierului JAR şi calea

55
6.17 Designerul grafic WindowBuilder Pro
 WindowBuilder Pro este un designer Swing care simplifică realizarea aplicaţiilor cu
interfaţă grafică în Java, întrucat permite crearea aplicaţiei grafice prin drag and drop
de componente, schimbarea proprietăţilor componentelor cu ajutorul unui Property
Editor şi generarea codului Java. Designerul se instalează ca şi un plug-in în Eclipse

 Se accesează http://www.eclipse.org/windowbuilder/download.php şi se copiază linkul


corepunzător versiunii de Eclipse

 In Eclipse se alege comanda Help > Install New Software… şi se introduce linkul de
pe care se va face instalarea pluginului

După ce a fost instalat


WindowBuilder Pro se poate crea o
aplicaţie cu interfaţă grafică alegând
opţiunea New > Other> Window
Builder> Swing designer> JFrame

56
 Componentele interfeţei grafice se trag prin drag and drop pe JFrame
 Proprietaţile componetelor pot fi configurate din Property Editor
 Codul generat poate fi vizualizat alegând tabul Source (stânga jos)

57
7. Java Database Connectivity
(JDBC) şi MySQL

Sl. dr. ing. Raul Robu


www.aut.upt.ro/~rraul

2020-2021, Semestrul 1
7. Java Database Connectivity (JDBC) şi MySQL

7.1 Caracteristicile generale ale JDBC

 JDBC este o tehnologie care permite conectarea la baze de date şi oferă metode pentru
interogarea şi actualizarea acestora.

 Tehnologia JDBC este orientată către baze de date relaţionale (Oracle, MySql, Microsoft Sql
Server,etc), dar permite conectarea şi la fişiere tabelare (Microsoft Excel spreadsheets) sau
fişierele plate (flat files).

 Conectarea la un anumit tip de bază de date se face cu ajutorul driver-ului aferent bazei de date
respective

 Clasele JDBC sunt conţinute în pachetele java.sql şi javax.sql.

 Clasa DriverManager este folosită pentru a crea conexiunile JDBC.

 Conexiunile JDBC suportă crearea şi executarea comenzilor SQL. Acestea pot fi comenzi de
actualizare precum CREATE, INSERT, UPDATE şi DELETE, sau pot fi interogări precum
SELECT. De asemenea procedurile stocate pot fi invocate cu ajutorul unei conexiuni JDBC.
JDBC execută comenzile sql cu ajutorul metodelor oferite de una din următoarele interfeţe:

2
 Statement – este folosită pentru trimiterea comenzilor SQL simple fără parametri
 PreparedStatement – permite folosirea instrucţiunilor SQL precompilate şi a parametrilor de
intrare în interogări.
 CallableStatement – permite executarea unor proceduri stocate pe baza de date.

 Comenzile SQL precum INSERT, UPDATE şi DELETE returnează un contor care reprezintă
numărul de rânduri care au fost afectate de comandă

 Interogările SQL (SELECT) returnează un obiect JDBC, de tip ResultSet, care este organizat pe
rânduri şi coloane. Coloanele pot să fie accesate fie cu ajutorul numelui, fie cu ajutorul indexului
coloanei.Un ResultSet conţine metadate care descriu numele coloanelor si tipul acestora

7.2 MySQL
 Caracteristici

 Unul din cele mai populare SGBD-uri (Sisteme de Gestiune a Bazelor de Date) din lume

 Scris în C şi C++

 Testat cu o arie largă de compilatoare

 Funcţionează pe multe platforme (Linux, Mac OS, Windows, Solaris, etc)

3
 Testat cu Purify (un instrument software care ajută la buna gestiune a memoriei)

 Utilizează un mod de proiectare cu module independente

 Proiectat astfel încât să poată fi rulat cu uşurinţă pe mai multe procesoare

 Furnizează motoare de stocare tranzacţionale sau ne-tranzacţionale

 Foloseşte un sistem de alocare bazat pe fire de execuţie foarte rapid

 Execută foarte rapid comenzi de tip join

 Implementează tabele hash, care sunt folosite ca tabele temporare

 Server-ul poate fi tratat ca un program separat şi poate fi utilizat în aplicaţii de tip client-
server în reţea, dar poate fi tratat şi ca o librărie care poate fi încorporată în aplicaţii de sine
stătătoare. Aceste aplicaţii pot fi utilizate în medii în care nu este disponibilă nici o reţea

 Descărcare şi instalare
 http://dev.mysql.com/downloads/installer/

4
 La instalare alegeţi opţiunea Default care instalează următoarele instrumente
 MySQL Server

 MySQL Workbench – este un instrument vizual care permite administrarea bazelor de


date
 Conectorii care permite accesarea bazelor de date MySQL din Java , C, C++, .Net,
ODBC, Excel
 MySQL Notifier care permite monitorizarea şi schimbarea stării serverului MySQL, cu
ajutorul unui indicator care este plasat în system tray
 Documentaţie si exemple

 Utilizarea
 în sistemul de operare a fost instalat un serviciu care se lansează automat atunci când
pornim calculatorul. Acest serviciu porneşte serverul MySQL.
 Serverul pornit ascultă şi acceptă, pe bază de utilizator şi parolă, cererile de conectare

5
 Conectarea la server, crearea bazelor de date (schemelor) şi a tabelelor, popularea acestora cu
date, etc se realizează cu ajutorul instrumentului MySQL Workbench

 Întâi se realizează conectarea la serverul mySQL cu ajutorul parolei stabilite la instalare

6
 O bază de date poate conţine tabele, vederi sau proceduri stocate
 Se deschide baza de date în care se doreşte să se creeze tabelul (sau se creează o nouă bază
de date)
 Se face click dreapta pe secţiunea Tables şi se alege comanda Create Table

7
 Se generează automat comanda SQL de creare a tabelului în conformitate cu cele menţionate în
interfaţa grafică

 Popularea cu date a tabelului creat se face prin click dreapta pe numele tabelului şi alegând
comanda Edit Table Data
8
 După introducerea datelor se apasă comanda Apply, în urma căreia se generează automat
comenzile SQL - INSERT pentru introducerea datelor în tabel

 Popularea cu date a tabelului creat se face prin click dreapta pe numele tabelului şi alegând
comanda Edit Table Data
9
 Conectarea din Eclipse la MySQL

 Pentru conectare la o bază de date MySQL, este necesară includerea în proiect a driverului de
conectare la baza de date (fişierul jar de mai jos)
C:\Program Files\MySQL\Connector J 5.1.20.0\mysql-connector-java-5.1.20-bin.jar

 Se face click dreapta pe proiect şi se alege Properties> Java Build Path>Libraries>Add External
Jar

10
7.3 Accesarea unei baze de date folosind JDBC

 Presupune parcurgerea următorilor paşi:


 Stabilirea unei conexiuni către baza de date
 Execuţia instrucţiunilor SQL
 Procesarea rezultatelor
 Închiderea conexiunii cu baza de date

7.3.1 Stabilirea unei conexiuni către baza de date

 Odată ce s-a încărcat un driver putem să-l folosim pentru stabilirea unei conexiuni către baza de
date. O conexiune JDBC este identificată printr-un URL JDBC specific

 Sintaxa standard pentru URL-ul unei baze de date este cea de mai jos. Driver este un nume de
driver valid, server este numele sau adresa IP a calculatorului care găzduieşte serverul MySQL,
port este numărul unui port deschis pe care se poate face conectarea
jdbc:driver://server:port/baza_de_date

 Pentru baza de date test, în care a fost creat tabelul persoane, din exemplul precedent URL-ul
este următorul

String url = "jdbc:mysql://localhost:3306/test";

11
 Pentru stabilirea unei conexiuni se foloseşte metoda statică getConnection() din clasa
DriverManager

Connection con = DriverManager.getConnection (url, "root", "root");

7.3.2 Execuţia unei instrucţiuni SQL

 După ce s-a stabilit conexiunea se pot trimite instrucţiuni SQL către baza de date
 API-ul JDBC specifică 3 interfeţe (cărora cel care implementează driver-ul trebuie să le dea
implementare) pentru trimiterea de interogări către baza de date, fiecăreie corespunzându-i o
metodă specială în clasa Connection, cu care se pot crea instanţele corespunzătoare

 Pentru execuţia unei instrucţiuni SQL neparametrizate se foloseşte metoda createStatement()


aplicată unui obiect de tip Connection
Statement sql;
sql = (Statement) con.createStatement();

12
 Pentru a trimite SGBD-ului comenzile SQL putem utiliza una din metodele execute(),
executeQuery() sau executeUpdate() a obiectului de tip Statement.

 Metoda executeQuery() este utilizată în cazul interogărilor (SELECT) care returnează o mulţime
rezultat

 Metoda executeUpdate() este utilizată în cazul unor comenzi de tip INSERT, UPDATE, DELETE.
Metoda returnează un întreg care reprezintă numărul de rânduri afectate

 Metoda execute() se poate utiliza pentru a rula comenzi de tipul insert, update delete şi în
interogări care furnizează mai mult decât o mulţime rezultat

 Pentru a prelua datele din tabelul persoane executăm o interogare SELECT şi preluăm datele într-
un obiect de tip ResultSet

ResultSet rs;
rs = sql.executeQuery("select * from persoane");

13
7.3.3 Procesarea rezultatelor

 Pentru parcurgerea simplă a înregistrărilor unui obiect din clasa ResultSet se poate utiliza metoda
next(), iar pentru obţinerea valorilor unui câmp se pot utiliza metode de genul getString(), getInt()
getBoolean(), etc în acord cu tipul câmpului. Parametrul acestei metode este fie denumirea
câmpului fie index-ul acestuia (primul câmp are index-ul 1)

 Pentru afişarea pe ecran a datelor din tabelul persoane se poate utiliza codul de mai jos

while (rs.next())
System.out.println("id="+rs.getInt("Id")+", nume= " + rs.getString("nume")+ ",
varsta="+rs.getInt(3));

 Iniţial cursorul este poziţionat înaintea primei linii.

 Metodele first(), previous(), next(), last() permit deplasarea cursorului

 Poziţionarea cursorului pe orice linie se poate face cu ajutorul metodei absolute()

 Determinarea liniei curente se poate face cu ajutorul metodei getRow()

 Pentru a determina dacă cursorul este la sfârşit sau început se pot utiliza metode: isFirst(),
isLast(), isBeforeFirst(), isAfterLast()

14
7.3.4 Actualizarea bazei de date

 Baza de date se poate actualiza prin rularea unor interogări SQL de tip INSERT, UPDATE,
DELETE dar şi cu ajutorul unor actualizări programatice.
 Actualizările programatice sunt actualizări aplicate direct ResultSet-ului care sunt automat
efectuate şi asupra bazei de date.
 Actualizările programatice nu se pot executa decât dacă obiectul de tip Statement a fost creat cu
parametrii de mai jos
sql = (Statement)con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
ResultSet.CONCUR_UPDATABLE);

 Actualizări programatice pentru modificarea datelor:

rs.first();
rs.updateInt("varsta", 30);
rs.updateString(2, "Alina");
rs.updateRow();

 Actualizări programatice pentru inserarea unui rând:


rs.moveToInsertRow();
rs.updateInt(1, 4);
rs.updateString("nume", "Mariuca");
rs.updateInt("varsta",34);
rs.insertRow();
 Actualizări programatice pentru ştergerea unui rând:
rs.first();
rs.deleteRow();
15
7.3.5 Închiderea unei conexiuni la baza de date
 Colectorul de reziduuri nu ştie dacă este cazul sau nu să elibereze resursele exterioare, de aceea
este bine ca programatorii să închidă explicit conexiunile către baza de date
con.close();
sql.close();
rs.close();

 Exemplu, preluarea datelor din tabelul persoane:


package exemplu;

import java.sql.*;

public class testJDBC1 {


public static void main(String[]args) throws SQLException {
String url = "jdbc:mysql://localhost:3306/test";
Statement sql;

ResultSet rs;
Connection con = DriverManager.getConnection (url, "root", "root");
sql = con.createStatement();
rs = sql.executeQuery("select * from persoane");
while (rs.next())
System.out.println("id="+rs.getInt("Id")+", nume= " + rs.getString("nume")
+ ",varsta="+rs.getInt(3));

con.close();
sql.close();
rs.close();
}
}
16
7.3.6 Utilizarea comenzilor SQL cu parametri
 Interfaţa PreparedStatement permite rularea comenzilor SQL cu parametri. Comanda SQL se
transmite metodei prepareStatement() din interfaţa Connection. Metoda returnează un obiect de
tip PrepareStatement.
 Stabilirea valorilor parametrilor se face cu ajutorul unor metode precum setString(), setInt() care
primesc ca şi parametri indexul parametrului care va fi setat şi valoarea acestuia
 Exemplu, preluarea datelor din tabelul persoane:
package exemplu;

import java.sql.*;

public class testJDBC2 {


public static void main(String[]args) throws SQLException {
String url = "jdbc:mysql://localhost:3306/test";
Connection con = DriverManager.getConnection (url, "root", "root");

PreparedStatement pr=con.prepareStatement("select * from persoane where nume=?"


+ " and varsta=?");

pr.setString(1, "Vladut");
pr.setInt(2, 15);
ResultSet rs = pr.executeQuery();
while (rs.next())
System.out.println("id="+rs.getInt("Id")+", nume= " + rs.getString("nume")
+ ", varsta="+rs.getInt(3));

con.close(); pr.close(); rs.close();


}
}
1717
7.3.7 Apelul procedurilor stocate

 Cu ajutorul utilitarului MySQL Workbench se creează o procedura stocată pe server, în baza de


date test, tabela persoane, prin click drepta pe Routines și apoi alegând comanda Create
Procedure

 Procedura creată va permite adăugarea unei noi înregistrări în tabela persoane și va avea
conținutul de mai jos

18
 Exemplul de mai jos arată cum se poate apela procedura stocată si afișează conținutului tabelei:
package exemplu;
import java.sql.*;

public class testJDBC3 {


public static void main(String[]args) throws SQLException, InstantiationException,
IllegalAccessException, ClassNotFoundException{
String url = "jdbc:mysql://localhost:3306/test";
Statement sql;

ResultSet rs;
Class.forName ("com.mysql.cj.jdbc.Driver").newInstance ();
Connection con = DriverManager.getConnection (url, "root", "root");

CallableStatement call=con.prepareCall("{call adaugare(?,?,?)}");


call.setInt(1,5) ;
call.setString(2,"John");
call.setInt(3,22);
call.execute();

sql = (Statement) con.createStatement();


rs = sql.executeQuery("select * from persoane");
while (rs.next())
System.out.println("id="+rs.getInt("Id")+", nume= " + rs.getString("nume")
+ ", varsta="+rs.getInt(3));

con.close();
sql.close();
rs.close();
}
} 19
7.3.8 Determinarea denumirii coloanelor unei tabele și tipul lor

 În aplicații în care se lucrează cu diferite tabele care o structură diferită, determinarea unor
metadate cum ar fi numărul de coloane, denumirea lor, tipul etc se poate realiza cu ajutorul
interfeței ResultSetMetaData:

 Liniile de cod de mai jos ilustrează cum pot fi determinate denumirile coloanelor, numărul lor, tipul
și dimensiunea. Liniile de cod pot fi completatate și rulate în unul din exemplele precedente

ResultSetMetaData meta = rs.getMetaData();

for (int i=1;i<=meta.getColumnCount();i++)


System.out.println(meta.getColumnName(i)+
", "+meta.getColumnTypeName(i)+
", "+meta.getColumnDisplaySize(i));

20
7.4 Corespondenţa dintre tipurile de date MySQL şi tipurile de date Java
 Tipurile de date din SQL sunt diferite de tipurile de date din Java

 Conectorul MySQL Connector/J este flexibil în ceea ce priveşte modul în care gestionează
conversiile dintre tipurile de date MySQL şi cele Java

 În general orice tip MySQL poate fi convertit în java.lang.String, şi orice tip numeric poate fi
convertit la orice tip numeric din Java, deşi pot apărea rotunjiri, depăşiri, posibile pierderi de
precizie

 Tabelul de mai jos ilustrează în ce mod sunt mapate tipurile de date din MySQL la cele din Java
de către driver

21
7.5 Comenzi SQL
 SELECT
SELECT <listacampuri> FROM <numetabela> WHERE <expresiebooleana>

//exmplu de interogare a tabelului persoane

Select nume,varsta from persoane where id=1


Select * from persoane where nume like ‘o%’

 INSERT
INSERT INTO <numetabela> (camp1,camp2,…,campn) values (valcamp1,valcamp2,…,valcampn)

//exmplu de inserare in tabelul persoane

insert into persoane values (4,‘Gigi',30);

 UPDATE
UPDATE <numetabela> SET camp2= valcamp2,…,campn= valcampn WHERE camp1=valcamp1

//exmplu de actualizare a tabelului persoane

update persoane set nume='Banel',varsta=35 where id=1


 DELETE
DELETE FROM <numetabela> WHERE <conditie>

//exmplu de stergere din tabelul persoane

delete from persoane where id=4 22


8. Procesarea documentelor XML

2020-2021, Semestrul 1
8.1 Noţiuni generale
 XML (eXtensible Markup Language) este un limbaj definit de Consorţiul Web (W3C)

 XML este un set de reguli, specificaţii şi convenţii pentru structurarea datelor în fişiere text

 Avantajul utilizării unui fişier text faţă de unul binar este că programatorul sau utilizatorul poate citi,
înţelege şi utiliza respectivul fişier fără a fi nevoie de programul care l-a produs

 XML utilizează taguri, atribute şi text. Tagurile sunt cuvinte cuprinse între caracterele “<” şi “>” iar
atributele sunt secvenţe de forma nume= “valoare” şi apar în interiorul tag-urilor

 XML-ul utilizează tag-uri doar pentru a delimita datele iar interpretarea este dată de aplicaţia care
îl utilizează

 Procesarea documentelor XML se poate realiza cu:


 SAX (Simple API for XML Parsing)– oferă o interfaţă bazată pe evenimente pentru parsarea
documentelor XML

 DOM (Document Object Model) – oferă un set de interfeţe pentru reprezentarea


arborescentă a documentelor XML

2
8.2 Exemplu de document XML
 Fişierul optionale.xml conţine informaţii cu privire la materiile opţionale şi va fi utilizat de către
unele din exemplele care urmează:

<?xml version="1.0"?>
<optionale>
<optional id=“c1">
<nume>Postmodernismul si Abordarea Metafilosofica a Culturii</nume>
<profesor>Tudor Radac</profesor>
<an>3</an>
<url>http://www.pamc.ro</url>
<studenti min="20" max="160"/>
<recomandari>
<curs>PM</curs>
<curs>AMC</curs>
</recomandari>
</optional>

<optional id=“c2">
<nume>Conceptul de Fenomenologie si Ego Transcedental</nume>
<profesor>Ana Tudose</profesor>
<an>4</an>
<url>http://www.cfet.ro</url>
<studenti min="30" max="140"/>
<recomandari>
<curs>CF</curs>
<curs>ET</curs>
</recomandari>
</optional>
<optional id=“c3">
<nume>Mecanismele Cunoasterii Stiintifice</nume>
<profesor>Matei Ionescu</profesor>
<an>2</an>
<url>http://www.mcs.ro</url>
<studenti min="25" max="100"/>
<recomandari>
<curs>RST</curs>
<curs>IMN</curs>
</recomandari>
</optional>
</optionale>

 Fişierul poate fi creat cu ajutorul unor editoare text precum Notepad ,TextPad , etc
 În Eclipse se poate crea un fişier XML prin click dreapta pe proiect şi alegând comanda New >
File
 fişierul creat poate fi editat cu ajutorul editorului de text sau cu ajutorul editorului XML
XML Editor:

Text Editor:
8.3 SAX
8.3.1 Aspecte teoretice
 Simple API for XML parsing (SAX) – permite procesarea documentelor XML prin intermediul
unor evenimente.

 Se parcurge fişierul XML iar la apariţia elementelor XML se vor lansa evenimente care pot fi
tratate

 Programatorul va trata evenimentele dorite şi în funcţie de elementul (tagul XML) generator va


realiza anumite operaţii

 Pachete necesare sunt:


 javax.xml.parsers – conţine clasele necesare procesării documentelor XML atât cu SAX cât
şi cu DOM

 org.xml.sax – oferă interfeţele necesare procesării documentelor cu XML prin SAX

 org.xml.sax.helpers – defineşte clasa ajutătoare pentru utilizarea interfeţelor SAX

 org.xml.sax.ext – oferă două interfeţe pentru a extinde SAX

5
8.3.2 Obţinerea informaţiilor din documente XML
 Fiecărui eveniment îi corespunde o metodă definită în interfaţa ContentHandler. Metodele sunt
apelate automat de către parser, iar cele mai importante sunt următoarele:

public void startDocument() throws SAXException

 este apelată automat în momentul în care începe procesarea documentelor XML


public void endDocument() throws SAXException

 Se execută la terminarea procesării documentului


public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException

 Se apelează la apariţia unui tag.

 Unele documente XML pot utiliza spaţii de nume. Acestea sunt utile pentru a furniza nume unice
elementelor şi atributelor dintr-un fişier XML. Spaţiile de nume sunt introduse în documentele XML
cu ajutorul atributului xmlns.

 uri (uniform resource identifier ) este un string utilizat pentru a identifica un nume sau o resursă

 Dacă este activată facilitatea de procesare a spaţiilor de nume şi dacă spaţiul de nume are ataşat
un uri atunci adresa spaţiului de nume va fi transmisă în parametrul uri. În acest caz localName
va conţine numele tag-ului fără prefix, iar qName cu tot cu prefix
6
 Dacă nu este activă facilitatea de procesare a spaţiilor de nume atunci localName va conţine şirul
vid iar qName numele tag-ului. Atributele vor fi memorate în parametrul attributes. Dacă tag-ul nu
posedă atribute atunci parametrul va fi un obiect vid
public void endElement(String uri, String localName, String qName) throws SAXException

 Această metodă este apelată atunci când este întâlnit un tag de sfârşit. Dacă tagul este de sine
stătător (de exemplu <studenti />) atunci se apelează automat după startElement

public void characters(char[] ch,int start, int length) throws SAXException

 Se execută la apariţia unui text aflat între taguri. Respectivul text se află în parametrul ch,
începând cu poziţia start şi va conţine length caractere

 Toate metodele din interfaţa ContentHandler transmit mai departe excepţia SAXException

 Interfaţa ContentHandler este implementată de clasa DefaultHandler. Aceasta mai


implementează interfeţele DTDHandler, EntityResolver şi ErrorHandler

 Pentru a obţine informaţiile din documentele XML putem fie să implementăm interfaţa
ContentHandler fie să extindem clasa DefaultHandler

 Clasa SAXParserFactory este utilizată pentru crearea şi configurarea de parsere bazate pe SAX.
Obţinerea unei noi instanţe se realizează cu ajutorul metodei statice newInstance() a clasei
SAXParserFactory

7
 Clasa SAXParser este utilizată pentru procesarea efectivă a documentelor XML. Instanţele
acestei clase se obţin prin apelul metodei newSAXParser() din clasa SAXParserFactory.

 Fişierul XML este transmis ca parametru metodei parse() împreună cu un obiect de tip
DefaultHandler()

 Exemplu, obţinerea informaţiilor din fişierul optionale.xml


import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

class OptionaleXML extends DefaultHandler{


private int sw;
public OptionaleXML(){
sw=0;
}
public void startDocument() throws SAXException{
System.out.println("Cursurile optionale sunt:");
}
public void startElement(String uri, String localName, String qName,Attributes
attributes) throws SAXException {
if (qName.equals("nume")) sw=1;
}
public void endElement(String uri, String localName, String qName)throws SAXException {
if (qName.equals("nume")) sw=0;
}
8
public void characters(char[] ch,int start, int length) throws SAXException{
if (sw==1) {
System.out.print(" - ");
for (int i=0;i<length;i++)
System.out.print(ch[start+i]);
System.out.println("");
}
}
}

class Main{
public static void main(String []args){
SAXParserFactory factory=SAXParserFactory.newInstance();
DefaultHandler handler=new OptionaleXML();
SAXParser parser;
try{
parser=factory.newSAXParser();
parser.parse("optionale.xml", handler);
}
catch(Exception ex){
System.out.println(ex.toString());
}
}
}

9
8.3.3 Obţinerea valorilor atributelor unui tag
 Valorile atributelor se obţin cu ajutorul metodei getValue(numeAtribut), a parametrului attributes
 Codul de mai jos poate fi completat în metoda startElement () din exemplul precedent

else if (qName.equals("studenti"))
{
String min=attributes.getValue("min");
String max=attributes.getValue("max");
if (min!=null){
System.out.println("\tMin="+min);
}
if (max!=null){
System.out.println("\tMax="+max);
}
}

 În cazul în care nu se cunosc atributele acestea pot fi determinate cu ajutorul metodei


getQName(index)
 Determinarea tuturor atributelor şi valorilor unui tag se poate realiza şi ca în exemplul de mai jos

else if (qName.equals("studenti"))
{
for (int i=0;i<attributes.getLength();i++)
System.out.println("\t"+attributes.getQName(i)+"="+attributes.getValue(i));
}

10
8.3.4 Procesarea documentelor XML care conţin DTD
 Pentru ca un document XML să respecte o anumită structură, se defineşte un DTD (Document
Type Definition)
 DTD specifică care este structura documentului XML: numele tag-ului rădăcină, subtagurile
acestuia, cu eventuale condiţii de repetare sau de obligativitatea apariţiei, lista atributelor pentru
fiecare tag, etc
 Fişierul optionale2.xml are în plus faţă de fişierul optionale.xml, următoarele linii
<!DOCTYPE optionale SYSTEM "cursuri.dtd"> <!-- linia 2 -->
//…
<optional id="c4" /> <!-- penultima linie -->

 Se creează următorul DTD-ul cursuri.dtd pentru fişierul optionale2.xml creat. Fişierul


cursuri.dtd are următorul conţinut:
<!ELEMENT optionale (optional)+>
<!ELEMENT optional (nume,profesor,an,url,studenti,recomandari)>
<!ELEMENT nume (#PCDATA)>
<!ELEMENT profesor (#PCDATA)>
<!ELEMENT an (#PCDATA)>
<!ELEMENT url (#PCDATA)>
<!ELEMENT studenti EMPTY>
<!ELEMENT recomandari (curs)+>
<!ELEMENT curs (#PCDATA)>

<!ATTLIST optional id ID #REQUIRED >

<!ATTLIST studenti
min CDATA #IMPLIED
max CDATA #IMPLIED
> 11
 Într-un document DTD, elementele sunt declarate cu următoarea sintaxă:
<!ELEMENT numele_elementului categoria>

//sau

<!ELEMENT numele_elementului (conţinutul_elementului)>

 Elementele care nu pot conţine decât text (nu mai pot conţine alte elemente) se declară cu
#PCDATA între paranteze rotunde (Parsed Character Data)

 Pentru elementele fără conţinut se foloseşte cuvântul cheie EMPTY

 Elementele care pot sa aibă unul sau mai mulţi copii sunt declarate punând numele copiilor între
paranteze rotunde
<!ELEMENT numele_elementului(copil1)>

//sau

<!ELEMENT numele_elementului (copil1,copil2,...)>

 Când copii sunt declaraţi într-o secvenţă separată prin virgule, copii trebuie să apară în aceeaşi
ordine în document

 Declararea unei singure apariţii a unui copil se face în felul următor:

<!ELEMENT element-name (child-name)>

12
 Declararea a cel puţin unei apariţii a unui copil se face în felul următor:

<!ELEMENT element-name (child-name)+>

 Declararea unui atribut se face în felul următor:


<!ATTLIST numele_elementului numele_atributului tipul_atributului valoarea_implicită>

 Tipul atributului poate fi:


 ID – valoarea este un id unic
 CDATA –valoarea este formată dintr-un set de caractere
 NOTATION – valoarea este numele unei notaţii
 etc
 Valoarea implicită poate fi una din următoarele:
 value - o valoare dorită
 #REQUIRED – atributul este cerut
 #IMPLIED – atributul nu este cerut
 etc

 Dacă se execută programul anterior asupra fişierului optional2.xml nu se observă nici o


modificare, ceea ce înseamnă că programul nu realizează validarea documentului XML. Pentru
aceasta se înlocuieşte conţinutul funcţiei main() cu următorul cod:

13
SAXParserFactory factory=SAXParserFactory.newInstance();
DefaultHandler handler=new OptionaleXML();
SAXParser parser;
XMLReader xmlReader;
factory.setValidating(true);

try{
parser=factory.newSAXParser();
xmlReader=parser.getXMLReader();
xmlReader.setContentHandler(handler);

xmlReader.parse(“optionale2.xml");
}
catch(Exception ex){
System.out.println(ex.toString());
}

 Interfaţa XMLReader este folosită pentru parsarea documentului

 Pentru stabilirea obiectului care implementează interfaţa ContentHendler se utilizează metoda


setContentHandler şi i se dă ca parametru o instanţă a clasei create

 Pentru pornirea parsării se apelează metoda parse() care primeşte ca şi parametru fişierul xml de
parsat

 Activarea validării documentului XML se realizează cu ajutorul metodei setValidating() a obiectului


factory
14
 Rularea programului cu aceste modificări va genera un mesaj de eroare care semnalează faptul
că tagul opţional este incomplet, deoarece este obligatorie indicarea celorlalte subtaguri. Deci
acum programul cercetează dacă documentul XML respectă structura specificată

 În plus este afişat un mesaj de avertizare care anunţă faptul că nu a fost specificat un obiect care
să trateze erorile şi că este utilizat unul implicit care nu reţine decât primele 10 erori.

 Pentru a elimina warningul trebuie să definim un obiect a cărui clasă să implementeze interfaţa
ErrorHandler. Putem să specificăm obiectul handler care aparţine unei clase ce extinde
DefaultHandler, pentru că aceasta implementează şi ErrorHandler. În funcţia main(), înainte de
parsare se adaugă următoarea linie:
xmlReader.setErrorHandler(handler);

 Această modificare face ca să nu mai apară nici un mesaj de eroare pentru că, clasa
DefaultHandler implementează funcţiile specificate în interfaţa ErrorHandler ca şi funcţii vide

 Pentru a semnala erorile care apar va trebui sa redefinim în clasa OptionaleXML warning(),
error() şi fatal()
15
 Pentru aceasta se poziţionează cursorul în interiorul clasei OptionaleXML şi se alege opţiunea de
meniu Source> Override/Implement Methods şi se aleg cele 3 metode

 În cele 3 metode se completează următorul cod

public void error(SAXParseException e) throws SAXException {


throw new SAXException("Error:“+e.getMessage());
}
@Override
public void fatalError(SAXParseException e) throws SAXException {
throw new SAXException("Fatal Error:“+e.getMessage());
}
@Override
public void warning(SAXParseException e) throws SAXException {
System.out.println("Warning“+e.getMessage());
}

 Programul determină că pentru un opţional nu se specifica datele şi generează un mesaj de


eroare, iar warningul precedent dispare

16
8.4 DOM
 DOM (Document Object Model) este un standard stabilit de consorţiul Web şi conţine interfeţe
pentru manipularea arborelui de obiecte asociat unui document XML
 Cu ajutorul DOM se pot adăuga noi noduri în arbore respectiv modifica sau şterge cele existente
 Fişierul studenti.xml:
<?xml version="1.0" ?>
<!DOCTYPE studenti[
<!ELEMENT studenti (student)+>
<!ELEMENT student (nume,an,grupa)>
<!ELEMENT nume (#PCDATA)>
<!ELEMENT an (#PCDATA)>
<!ELEMENT grupa (#PCDATA)>
]>
<studenti>
<student>
<nume>Maria Tudoroiu</nume>
<an>2</an>
<grupa>3</grupa>
</student>
<student>
<nume>Marian Mocofan</nume>
<an>3</an>
<grupa>1</grupa>
</student>
<student>
<nume>Ionel Panduru</nume>
<an>4</an>
<grupa>2</grupa>
</student>
17
</studenti>
 Pachetele necesare procesării documentelor XML prin intermediul DOM sunt:
 javax.xml.parsers – conţine clasele necesare procesării documentelor XML (atât cu SAX cât şi
cu DOM)
 org.w3c – oferă clasele pentru reprezentarea arborescentă a documentelor XML

8.4.1 Crearea arborelui asociat unui document XML

 Înainte de crearea unui arbore trebuie să obţinem o instanţă a unui obiect


DocumentBuilderFactory. Acesta este util pentru obţinerea de obiecte DocumentBuilder care la
rândul lor permit crearea arborilor asociaţi documentelor XML

 Obţinerea unei instanţe a clasei DocumentBuilderFactory se realizează în urma apelului metodei


statice newInstance() din acceaşi clasă.

 Metoda newDocumentBuilder() întoarce o referinţă la un obiect de tip DocumentBuilder care


posedă metoda parse(). Acesta este utilizat la parsarea documentelor XML şi la crearea arborelui
corespunzător

 Pentru crearea arborelui corespunzător documentului 1.xml vom utiliza următorul cod:
DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
DocumentBuilder builder=factory.newDocumentBuilder();
Document document=builder.parse(“optionale.xml");

 În memorie s-a construit arborele corespunzător documentului XML, iar referirea la acesta se va
realiza prin intermediul interfeţei Document 18
8.4.2 Manipularea arborelui DOM

 Fiecărui nod din arbore îi corespunde un obiect care implementează interfaţa Node. Aceasta
posedă subinterfeţe corespunzătoare fiecărui tip de nod. Spre exemplu unui tag îi corespunde
interfaţa Element.

 Pentru identificarea tipului unui nod, interfaţa Node oferă metoda getNodeType() care poate
returna mai multe valori de tip short, o parte din acestea sunt:
 ATTRIBUTE_NODE –nod de tip atribut
 CDATA_SECTION_NODE – nod corespunzător unei secţiuni CDATA
 COMMMENT_NODE - nod corespunzător unui comentariu
 DOCUMENT_NODE –nod corespunzător unui document XML
 ELEMENT_NODE – nod corespunzător unui tag
 TEXT_NODE – nod de tip text

 Fiecare nod are o listă cu nodurile descendente şi una cu atribute

 Cele mai importante metode din interfaţa Node sunt:


 boolean hasChildNodes() – testează dacă nodul are descendenţi

 boolean hasAttribute() – testează dacă nodul are atribute

 NodeList getChildNodes() – returnează lista nodurilor descendente


19
 Node getFirstChild() - returnează primul nod descendent

 Node getNextSibling () –întoarce nodul următor nodului curent din lista nodurilor
descendente ale nodului părinte

 Node getParentNode() – returnează nodul părinte

 Node removeChild(Node oldChild) – elimină din lista nodurilor descendente nodul


specificat

 NamedNodeMap getAttributes(Node oldChild) – returnează o colecţie cu atributele


corespunzătoare nodului curent

 Valorile returnate de metodele getNodeName() şi getNodeValue () depind de natura nodurilor.


Tabelul de mai jos prezintă valorile returnate pentru o parte din interfeţe:

20
 Exemplul de mai jos procesează documentul optionale.xml şi afişează lista cursurilor opţionale:

try{
DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
DocumentBuilder builder=factory.newDocumentBuilder();
Document document=builder.parse(“optionale.xml");

Element elt=document.getDocumentElement(); //optionale


Node n1,n2,n3;

System.out.println("Elementul radacina este: "+elt.getTagName());

System.out.println("Cursurile optionale sunt:");


n1=elt.getFirstChild();
while(n1!=null){
n2=n1.getFirstChild();
while(n2!=null){
if (n2.getNodeName().equals("nume")){
n3=n2.getFirstChild();
while(n3!=null){
if (n3.getNodeType()==Node.TEXT_NODE)
System.out.println(n3.getNodeValue().trim());
n3=n3.getNextSibling();
}
}
n2=n2.getNextSibling();
}
n1=n1.getNextSibling();
}
}catch(Exception e){
System.out.println(e.toString());
} 21
 Exemplul precedent s-a bazat pe faptul că se cunoştea structura documentului XML. În cazul în
care nu se cunoaşte structura documentului XML se poate utiliza o abordare recursivă pentru a
explora documentul
try{
DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
DocumentBuilder builder=factory.newDocumentBuilder();
Document document=builder.parse(“optionale.xml");
Element elt=document.getDocumentElement();

rec(elt); //apelul metodei recursive


}catch(Exception e){
System.out.println(e.toString());
}
//…
public static void rec (Node n){
NodeList aux;
NamedNodeMap map;

if (n.hasAttributes()){
System.out.println("Atributele tagului "+n.getNodeName()+" sunt: ");
map=n.getAttributes();
for (int i=0;i<map.getLength();i++)
System.out.println("\t"+map.item(i).getNodeName()+"=\""+
map.item(i).getNodeValue()+"\"");
}
if (n.hasChildNodes()){
aux=n.getChildNodes();
for (int i=0;i<aux.getLength();i++)
rec(aux.item(i));
}
} 22
 Dacă un nod posedă atribute atunci acesta este de tip Element (cu siguranţă este un tag) iar
metoda getNodeName() va returna numele tag-ului

 În cazul în care un nod posedă copii se va apela metoda recursivă pentru aceste noduri

 Pentru obţinerea informaţiilor referitoare la nodurile arborelui sunt utilizate interfeţele


NamedNodeMap şi NodeList.

 Interfaţa NamedNodeMap este utilizată pentru a putea accesa o colecţie de noduri cu ajutorul
numelui sau a indecşilor. Este permisă eliminarea sau înlocuirea anumitor noduri

 Interfaţa NodeList reprezintă o colecţie ordonată de noduri. Se pot doar obţine nodurile din
colecţie dar colecţia nu poate fi modificată

8.4.3 Interfaţa Document

 Interfaţa Document are o importanţă deosebită pentru că prin intermediul acesteia se pot crea
noi noduri pentru arborele DOM asociat unui document XML. Aceasta mai oferă posibilitatea
obţinerii unei colecţii de noduri care au acelaşi nume. Cele mai importante metode ale interfeţei
sunt:
 Element getDocumentElement() - returnează nodul rădăcină al arborelui

 Element getElementById() – returnează elementul care are indicatorul precizat sau null
dacă acesta nu există
23
 NodeList getElementsByTag() – Furnizează o colecţie de noduri de tip element care au
numele specificat

 Attribute createAttribute (String nume) – creează un nod de tip atribut cu nume


specificat. Acesta se poate adăuga la un nod de tip Element cu ajutorul metodei
setAttributeNode()

 CDATASection createCDATASection () – creează un nod de tip secţiune CDATA cu


conţinut specificat

 Text createTextNode(String data) – construieşte un nod text

 Element createElement (String numeTag) – Întoarce un nou nod corespunzător


marcatorului cu nume specificat

 Nodurile create de metodele interfeţei Document nu sunt automat adăugate în arborele DOM
corespunzător documentului XML. Acestea ar trebui inserate în lista nodurilor descendente ale
unui nod. Modificarea arborelui nu implică actualizarea fişierului sursă XML

 Se consideră fişierul studenti.xml. Exemplul următor adaugă un nou nod în arborele DOM
(adaugă un nou student) şi apoi parcurge nodurile “nume” din arbore

24
 Pentru a putea crea marcatorul nume cu textul Ana Lana este necesar să să creăm două noduri:
unul corespunzător tagului nume, iar celălalt corespunzător nodului text. Nodul tagului va avea ca
descendent nodul cu text. Analog se creează nodurile corespunzătoare marcatorilor an şi grupa.
Aceştia 3 marcatori vor fi descendenţi pentru nodul student
DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
DocumentBuilder builder=factory.newDocumentBuilder();

Document document=builder.parse(“studenti.xml");

Node stud=document.createElement("student");

Node n=document.createElement("nume");
Node t=document.createTextNode("Ana Lana");
n.appendChild(t);
stud.appendChild(n);

n=document.createElement("an");
t=document.createTextNode("3");
n.appendChild(t);
stud.appendChild(n);

n=document.createElement("grupa");
t=document.createTextNode("2");
n.appendChild(t);
stud.appendChild(n);

document.getDocumentElement().appendChild(stud);

System.out.println("lista tuturor studentilor:");

NodeList lista=document.getElementsByTagName("nume");
for (int i=0;i<lista.getLength();i++)
System.out.println(lista.item(i).getFirstChild().getNodeValue()); 25
9. Caracteristicile Java 8

Sl. dr. ing. Raul Robu


www.aut.upt.ro/~rraul

2020-2021, Semestrul 1
9.1 Noțiuni introductive
 Prima versiune Java 8 a fost lansată în martie 2014

 Principalele noutăți aduse de Java 8 sunt:

 Metode implicite în interfețe – permit introducerea de metode implementate în interfețe,


alături de metode abstracte

 Expresii Lambda – sunt funcții anonime care de regulă sunt transmise ca și parametru de
intrare al altor funcții

 Referințe la metode – permit referirea metodelor prin nume, fără a le invoca. Permit
utilizarea funcțiilor ca și parametru. Permit definirea expresiilor Lambda prin referirea directă
a metodelor prin nume

 Interfețe funcționale - interfețe care au maxim o metodă abstractă și a căror implementare


poate fi specificată prin expresiile Lambda

 Stream API – o clasă iterator specială care permite procesarea colecțiilor de obiecte într-o
manieră functională

 Date Time API – un API îmbunătățit pentru procesarea datelor calendaristice


2
9.2 Metode implicite în interfețe
 Se definesc cu ajutorul cuvantului cheie default. Se utilizează când se dorește să se adauge o
nouă funcționalitate unei interfețe și în același timp să se pastreze compatibilitatea cu clasele care
deja implementează interfața
 Fișierul Figura.java are conținutul de mai jos
package exemplu1;
public interface Figura {
public void deseneaza_figura();

default void seteaza_culoare_umplere() {


System.out.println(“Seteaza culoarea de umplere rosie");
}
default void seteaza_culoare_contur() {
System.out.println(“Seteaza culoarea conturului verde");
}
}

 Fișierul Triunghi.java are conținutul de mai jos


package exemplu1;

public class Triunghi implements Figura{


@Override
public void seteaza_culoare_contur() {
System.out.println(“Seteaza culoarea conturului neagra");
}
3
@Override
public void deseneaza_figura() {
seteaza_culoare_umplere(); //acces la metoda implicita din interfata
seteaza_culoare_contur(); //acces la metoda din clasa Triunghi
System.out.println("Deseneaza un triunghi");
}
}

 Fisierul MainApp.java are conținutul de mai jos


package exemplu1;

public class MainApp {


public static void main(String[] args) {
Figura f=new Triunghi();
f.deseneaza_figura();
}
}

 Interfața Figura are o metodă abstractă și două metode implicite

 Clasa Triunghi implementează interfața Figura, specifică o implementare pentru metoda abstractă și
redefinește una din cele două metode implicite

 Din metoda deseneaza_figura() a clasei Triunghi se apelează atât metoda implicită din interfață cât
si cea redefinită în clasă
4
9.3 Expresii Lambda
 O expresie Lambda este caracterizată prin următoarea sintaxă:
parametri -> corpul expresiei

 Expresiile Lambda sunt utilizate în principal pentru a defini implementarea inline (printr-o linie de
cod) a unei interfețe cu o singură metodă

 Expresia Lambda elimină necesitatea unei clase anonime și oferă o capacitate de programare
funcțională foarte simplă, dar puternică pentru Java.

 Caracteristicile expresiilor Lambda sunt următoarele:.

 Nu este necesar sa se declare tipul unui parametru. Compilatorul poate sa deducă tipul lui

 Parantezele rotunde sunt necesare doar pentru cel putin doi parametri. Pentru un parametru
sunt opționale

 În corpul expresiilor sunt necesare paranteze accolade doar dacă acestea conțin două sau mai
multe instrucțiuni

 Cuvantul return este opțional – compilatorul automat returnează valoarea dacă corpul are o
singură expresie care să returneze valoarea

5
 Fisierul Persoana.java are conținutul de mai jos
package exemplu2;

class Persoana{
private String nume;
private int varsta;
public Persoana(String nume, int varsta) {
this.nume = nume;
this.varsta = varsta;
}
public String getNume() {
return nume;
}
public int getVarsta() {
return varsta;
}
@Override
public String toString() {
return nume + " " + varsta;
}
}

6
 Fisierul MainApp.java are conținutul de mai jos
package exemplu2;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

class MainApp {
public static void main(String[]args) {
List<Persoana> pers=new ArrayList<Persoana>();
pers.add(new Persoana("Maria",23));
pers.add(new Persoana("Ana",24));
pers.add(new Persoana("Oana",22));

Collections.shuffle(pers);
System.out.println("Colectia aleatoare: "+pers);

Collections.sort(pers, new Comparator<Persoana>() {


@Override
public int compare(Persoana o1, Persoana o2) {
return o1.getNume().compareToIgnoreCase(o2.getNume());
}
});
System.out.println("Colectia ordonata dupa nume in Java 7 style: "+pers);

7
Collections.shuffle(pers);
Collections.sort(pers,(Persoana a,Persoana b)->a.getNume().compareToIgnoreCase(b.getNume()));
System.out.println("Colectia ordonata dupa nume in Java 8 style 1: "+pers);

Collections.shuffle(pers);
Collections.sort(pers,(a,b)->{
return a.getNume().compareToIgnoreCase(b.getNume());
});
System.out.println("Colectia ordonata dupa nume in Java 8 style 2: "+pers);

Collections.shuffle(pers);
Collections.sort(pers,(Persoana a,Persoana b)->{
if (a.getVarsta()<b.getVarsta()) return -1;
else
if(a.getVarsta()>b.getVarsta()) return 1;
else return 0;
});

System.out.println("Colectia ordonata dupa varsta in Java 8 style 3: "+pers);


}
}

8
 În Java 7 ordonarea unei colecții se face cu ajutorul unei clase care implementează interfața de
comparare

 În Java 8 acestă problemă poate fi rezolvată cu ajutorul notațiilor Lambda

 Parametrii care apar în expresia Lambda sunt parametrii metodei de comparare a interfeței. Tipul
acestor parametrii poate să fie specificat (Persoana a, Persoana b) sau nu (a,b) (vezi slide-ul
precedent).

 Corpul expresiei Lambda conține codul din metoda de comparare, care este automat returnat

 În cazul în care corpul expresiei Lambda conține mai multe linii de cod, este necesar să se utilizeze
paranteze accolade și să se returneze prin utilizarea lui return valorile dorite (vezi exemplul de
ordonare după vârsta din slideul precedent)

package exemplu3;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;

public class MainApp {


public static void main(String[]args) {
JFrame myFrame = new JFrame("JButton");
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setSize(300, 300);
myFrame.getContentPane().setLayout(null); 9
JButton myButton=new JButton("Apasa-ma!");
myButton.setBounds(100, 10, 100, 20);
myButton.addActionListener(e->JOptionPane.showMessageDialog(null,"Click pe buton"));
myFrame.add(myButton);
myFrame.setVisible(true);
}
}

 Exemplul de mai sus ilustrează cum se poate adăuga un listener pe un buton in stilul Java 8, cu
ajutorul expresiilor Lambda

10
9.4 Referințe de metode
 O referință de metodă este o construcție Java 8 care poate fi folosită pentru a referi o metodă fără a
o apela

 O referință de metodă poate fi identificată cu ajutorul notatiei :: care separă o clasă sau un obiect de
numele unei metode

 Obținerea unei referințe de constructor se poate realiza în felul următor:

String::new

 Obținerea unei referințe către o metodă statică:


String::valueOf

 Obținerea unei referințe către o metodă legată de un obiect:


str::toString

 Obținerea unei referințe către o metodă nelegată de un obiect:


String::toString

11
 Fisierul MethodReference.java are conținutul de mai jos:
package exemplu4;

import java.util.List;
import java.util.ArrayList;

public class MethodReference {


public static void main(String args[]) {
List<String> orase = new ArrayList<String>();
orase.add("Timisoara");
orase.add("Arad");
orase.add("Cluj");
orase.add("Targu-Mures");

orase.forEach(System.out::println);
}
}

 forEach este un nou operator introdus în Java 8 pentru a traversa colecțiile. Acesta traversează
colecția și realizează pentru fiecare element actiunea transmisă ca și parametru de intrare

 O referință către metoda println a fost transmisă ca și parametru de intrare a metodei forEach care
traversează colecția și realizează acțiunea dată de parametrul de intrare pentru fecare element al
colecției, lucru care va determina afișarea colecției în consolă

12
9.5 Stream API
 Un stream este un iterator a cărui rol este de a accepta un set de acțiuni să fie aplicate fiecăruia din
elementele pe care le conține

 Un stream reprezinta o secventă de obiecte dintr-o sursă cum ar fi o colecție de obiecte.

 Streamurile au fost proiectate cu scopul de a face procesarea colecțiilor mai simplă și concisă.

 Spre deosebire de colecții cu ajutorul streamurilor se pot face o serie de prelucrări încă din etapa de
declarare

 Operațiile streamurilor pot să fie ori intermediare ori terminale. Operațiile terminale returnează un
rezultat de un anumit tip, iar cele intermediare returneaza însăși streamul, în acest fel se pot pune
într-o singură instrucțiune mai multe apeluri de metodă

 Operațiile stremurilor pot să fie executate secvențial sau parallel

 Colecțiile în Java 8 au fost extinse deci se poate crea un Stream foarte ușor apelând una din
metodele Collection.stream() or Collection.paralelStream()

 Streamul paralel împarte taskul furnizat în mai multe taskuri pe care le rulează în diferite fire de
execuție.

13
 Fisierul MainApp.java:
package exemplu5;
import java.util.ArrayList;
import java.util.List;

public class MainApp {


public static void main(String[] args) {
List<String> orase = new ArrayList<>();
orase.add("Timisoara");
orase.add("Arad");
orase.add("Cluj");
orase.add("Targu-Mures");

System.out.println("Orase care incep cu litera 'T':");


orase
.stream()
.filter((s) -> s.startsWith("T"))
.forEach(System.out::println);

System.out.println("\nLista ordonata a oraselor, scrisa cu litere mari: ");


orase
.stream()
.map(String::toUpperCase)
.sorted((a, b) -> a.compareTo(b))
.forEach(System.out::println);
}
} 14
 În exemplul precedent se creează un stream secvențial din colecția de obiecte ce conține
denumirile orașelor, se filtrează colecția pentru a rămâne în ea doar orasele a căror denumire
începe cu literea ‘T’, pentru fiecare element din colecția fitrată se apelează metoda println care
afișează elementul în consolă

 Cel de-al doilea stream transforma toate elementele colecției în majuscule, apoi ordonează colecția
și o afișează

 Liniile de cod de mai jos generează aleatoriu 10 întregi, îi ordonează și îi afișează în consolă

Random random = new Random();


random.ints().limit(10).sorted().forEach(System.out::println);

 În exemplul de mai jos se creeaza un stream paralel pentru o colecție de stringuri, se pune un filtru
care determină doar ementele necompletate și apoi se determină numărul acestora cu ajutorul
metodei count

List<String> strings = Arrays.asList("aaa", "", "zz", "bbb", "eee","", "ddd");

int count = (int) strings.parallelStream().filter(string -> string.isEmpty()).count();


System.out.println(count);

15
9.6 Interfețe funcționale
 Interfețele funcționale au o singură metodă abstractă. Metodele implicite nu sunt abstracte deci pot
fi prezente în număr oricât de mare în interfețele funcționale. Fiecare expresie Lambda va
corespunde acestei metode abstracte. Deaspupra interfețelor functionale se poate adăuga
adnotația @FunctionalInterface. Datorită acestei adnotații compilatorul va semnala eroare dacă se
încearcă adăugarea unei alte metode abstracte

 Se consideră clasa Persoana cu variabilele membre nume și varsta, din pachetul exemplu2. Datele
mai multor persoane vor fi adăugate unei colecții de obiecte de tip List și apoi cu ajutorul unei
interfețe funcționale și a notației Lambda vor fi afișate filtrat, doar datele persoanelor care au peste
22 de ani

package exemplu6;
import java.util.ArrayList;
import java.util.List;
import exemplu2.Persoana;

@FunctionalInterface
interface Filtru{
boolean test(Persoana p);
}

16
class MainApp {
static void afisare_filtrata(List<Persoana> pers, Filtru f) {
for(Persoana p:pers)
if(f.test(p))
System.out.println(p);
}
public static void main(String[]args) {
List<Persoana> pers=new ArrayList<Persoana>();
pers.add(new Persoana("Maria",23));
pers.add(new Persoana("Ana",24));
pers.add(new Persoana("Oana",22));
afisare_filtrata(pers, (Persoana p)->p.getVarsta()>22);
}
}

 În exemplul precedent, pentru a putea utiliza notația Lambda și a afișa datele unei persoane doar
dacă aceasta îndeplinește o condiție, a fost creată o interfață funcțională care conține o metodă de
test ce returneză o valoare booleană care indică dacă un anumit element respectă o condiție sau nu

 Prin notația Lambda se specifică o implementare pentru metoda interfeței funcționale, care
returnează true dacă vârsta unei persoane este mai mare de 22 de ani, respectiv false dacă nu este
mai mare

17
 Expresia Lambda este transmisă ca și parametru de intrare de tip Filtru al metodei statice
afisare_filtrata() şi utilizată în această metodă pentru a afișa doar datele persoanelor care
îndeplinesc condiția impusă:

 Interfața funcțională Filtru a fost creată doar pentru a putea transmite notația Lambda ca și
parametru de intrare a metodei de afișare

 În pachetul java.util.function.* din Java 8 există mai multe interfețe funcționale oferite de limbaj. Una
din aceste interfețe funcționale este interfața Predicate

 Interfața Predicat <T> este o interfață funcțională cu o metodă test(Object) care returnează o
valoare booleană. Metoda testează dacă o anumită condiție este îndeplinită sau nu

Predicate<Integer> predicate1 = n -> n%2 == 0;


int x=5;
System.out.print(predicate1.test(x)?x+" este par":x+" este impar");

 În exemplul precedent în locul creări interfeței funcționale Filtru s-ar fi putut utiliza interfața Predicate
din limbaj care conține o metodă de testare similară. Vezi exemplul următor:

18
package exemplu7;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

import exemplu2.Persoana;

class MainApp {
static void afisare_filtrata(List<Persoana> pers, Predicate<Persoana> f) {
for(Persoana p:pers)
if(f.test(p))
System.out.println(p);
}

public static void main(String[]args) {


List<Persoana> pers=new ArrayList<Persoana>();
pers.add(new Persoana("Maria",23));
pers.add(new Persoana("Ana",24));
pers.add(new Persoana("Oana",22));

afisare_filtrata(pers, (Persoana p)->p.getVarsta()>22);


}
}

 Dacă în pachetul java.util.functions.*; se găsește o interfață care să conțină o metodă a cărei


signatură se potrivește cu expresia Lambda dorită atunci se poate utiliza acea interfață, nu trebuie
creeată una nouă 19
 Mai jos sunt enumerate alte interfețe funcționale care se găsesc în pachetul java.util.functions.*:

 BiConsumer<T,U> are o funcție care acceptă două argumente şi nu returnează nimic

 BiFunction<T,U,R> are o funcție care are doi parametri de intrare și produce un rezultat

 BinaryOperator<T> are o funcție care are doi parametri de intrare de același tip si produce un
rezultat de același tip ca și parametrii de intrare

 Consumer<T> reprezintă o operație care accepta un singur parametru de intrare si nu produce nici
un rezultat

 Function<T,R> reprezintă o funcție care are un parametru de intrare si produce un rezultat

 UnaryOperator<T> reprezintă un o operație cu un singur operand care produce un rezultat de


același tip cu operandul

 etc

20
9.7 Date Time API
package exemplu8;

import java.time.*;
import java.time.temporal.*;
import java.util.Calendar;

public class MainApp {


public static void main(String[] args) {
LocalDateTime dateTimeNow =LocalDateTime.now();

System.out.println("Data si timpul: " + dateTimeNow);

LocalDate d1 = dateTimeNow.toLocalDate(); //LocalDate.now();


System.out.println("Doar data: " + d1);

Month luna = dateTimeNow.getMonth();


int a_cata_luna=dateTimeNow.getMonthValue();

System.out.println(luna+" month = month "+a_cata_luna);

LocalDateTime d2 =dateTimeNow.withMonth(2).withDayOfMonth(2);
System.out.println("d2="+d2);

LocalDate d3 = LocalDate.of(2012, Month.FEBRUARY, 14);


System.out.println("d3=" + d3); 21
LocalTime d4 = LocalTime.of(21, 30);
System.out.println("d4: " + d4);

LocalTime d5 = LocalTime.parse("20:15:30");
System.out.println("date5: " + d5);

LocalDate d6 = LocalDate.parse("2017-11-20");
System.out.println("d6: " + d6);

LocalDate d7 = d1.plus(100, ChronoUnit.DAYS);


System.out.println("Ce data va fi peste 100 de zile: " + d7);

Period period = Period.between(d7, d1);


System.out.println("Perioda dintre d7 si d1 este de: " + period);

LocalDate lunea_viitoare =d1.with(TemporalAdjusters.next(DayOfWeek.MONDAY));


System.out.println("Lunea viitoare este in : " + lunea_viitoare);

Calendar c=Calendar.getInstance();
Instant now = c.toInstant();

LocalDateTime localDateTime = LocalDateTime.ofInstant(now,ZoneId.systemDefault());


System.out.println(localDateTime);
}
}

22
23
9.8 Clasa Optional
 Pentru a evita excepția NullPointerException, de obicei în programe trebuie făcute multe verificări
ale null-ului

 Clasa Optional oferă facilitați pentru scrierea unui cod fără multe teste ale null-ului.

 Prin clasa Optional se pot specifica valori alternative care să fie returnate sau cod alternativ care să
fie rulat când se întâlnește null

 Utilizarea clasei Optional creste lizibilitatea codului

 Metoda de mai jos afișează numărul de cuvinte al unui propoziții dată ca şi parametru de intrare al
funcţiei, printr-un String ale cărui cuvinte sunt separate prin spaţii

public static void nr_cuvinte(String s) {


System.out.println("Propozitia: " + s + " are " + s.split(" ").length + " cuvinte");
}

 Dacă la afelul funcţiei i se transmite un String care are valoarea null, aceasta determină producerea
exceptiei NullPointerException

 Pentru evitarea exceptiei se poate testa null-ul precum în exemplul următor:


24
public static void nr_cuvinte(String s) {
if(s!=null)
System.out.println("Propozitia: " + s + " are " + s.split(" ").length + " cuvinte");
else
System.out.println("Stringul e null");
}

 Lucrând cu clasa Optional, acest test se poate face în felul următor:


public static void nr_cuvinte_cu_optional(String s) {
Optional<String> opt = Optional.ofNullable(s);
if (opt.isPresent())
System.out.println("Propozitia: " + opt.get() + " are " + opt.get().split(" ").length +
" cuvinte");
else
System.out.println("Stringul e null");
}

 Optional este un container care poate să conțină o valoare sau nu. Dacă valoarea este prezentă
funcția get() o va returna

 Metoda ofNullable() returnează un obiect Optional care va descrie valoarea specificată ca şi


parametru, daca aceasta nu este null, în caz contra va returna un Optional gol (Optional.empty)

 API-ul oferă metode adiționale care depind de prezența sau absenta valorii cum ar fi orElse()
metodă care returnează o valoare implicită în caz că valoarea inspectată nu e prezentă sau metoda
ifPresent() care execută un bloc de cod dacă valoarea este prezentă 25
 Un exemplu de utilizare a acestora este următorul:
package exemplu_optional;

import java.util.Optional;

public class MainApp {


public static void fara_optional_uppercase(String s) {
if(s!=null) {
System.out.println(s.toUpperCase());
}
else {
System.out.println("valoare lipsa".toUpperCase());
}
}
public static void cu_optional_uppercase(String s) {
Optional<String> opt=Optional.empty();
opt=Optional.ofNullable(s);
System.out.println(opt.orElse("valoare lipsa").toUpperCase());
}

public static void main(String[]args) {


String s=null;
//String s="test";
fara_optional_uppercase(s);
cu_optional_uppercase(s);
}
} 26
 În exemplul precedent metoda orElse() va returna valoarea din container dacă aceasta este
prezentă sau parametrul de intrare în caz contrar. Valoarea returnată de funcţie este apoi
transformate în majuscule

 Un exemplu de utilizare a metodei ifPresent() este următorul:

opt.ifPresent(value ->System.out.println(value.toUpperCase()) );

 Metoda primește ca şi parametru de intrare un obiect care implementează interfața funcțională


Consumer(), interfață ce conține o metodă care primește un singur parametru de intrare şi care nu
returnează nici un rezultat

 Parametrul de intrare al funcției ifPresent() a fost transmis printr-o expresie lambda care afișează cu
majuscule valoarea din containerul Option

 O situație frecventă de producere a excepției NullPointerException se întâlnește în aplicațiile cu


baze de date în care apar situații de genul:

Persoana p=findPersoanaById(2);
System.out.println(p.getNume());

 În exemplul de mai sus, dacă persoana căutată nu se găseşte, rândul următor produce
NullPointerException

 Situaţia se poate rezolva lucrând cu Optional şi cu metoda ifPresent() în felul următor 27


Optional<Persoana> optional = findPersoanaById(2);

optional.ifPresent(p -> {
System.out.println("Numele persoanei este " + p.getNume());
});

 Metoda findPersoanaById() de mai sus returnează un obiect de tip Optional


asociat unui obiect de tip Persoana

28
10. Fire de execuţie

2020-2021, Semestrul 1
10.1 Crearea firelor de execuţie
 Firele de execuţie reprezintă porţiuni de cod ale aceluiaşi program care se pot executa în paralel
una faţă de alta

 Maşina Virtuală Java permite dezvoltarea de aplicaţii care au mai multe fire de execuţie ce
rulează concurent

 Pentru orice fir de execuţie există o metodă run() care reprezintă codul executat de firul de
execuţie respectiv.

 Mecanismul de fir de execuţie este descris de clasa Thread din biblioteca java.lang

 Clasa Thread conţine o metodă run() fără cod

 Pentru a crea un fir de execuţie există două posibilităţi:


 Se creează o clasă derivată din clasa Thread şi în acea clasă se redefineşte metoda run()

class FirExecutie1 extends Thread{


public void run(){

}
}

//instanţierea firului de execuţie in cazul 1


FirExecutie1 f=new FirExecutie1(); 2
 Se implementează interfaţa Runnable
class FirExecutie2 implements Runnable{
public void run(){

}
}
//instanţierea firului de execuţie în cazul 2
FirExecutie2 g=new FirExecutie2 ();
Thread f=new Thread(g);

 Metoda start() pregăteşte un fir de execuţie de rulare. Începerea rulării efective depinde de mai
mulţi parametri
f.start();

 În cazul 2, din metoda run nu se pot apela metode specifice de tip Thread (sleep(),
setPriority(),getPriority()). Pentru a rezolva această problemă trebuie procedat ca mai jos:
public void run(){
Thread t=Thread.currentThread();

t.sleep(100);
}

 Metoda 2 permite clasei firului de execuție să extindă o altă clasă decât Thread

 Se pot crea grupuri de fire de execuţie


ThreadGroup g=new ThreadGroup (“Nume grup”);

If(g.activeCount()!=0){ // test dacă există fire de execuţie active
… // prelucrări specifice
g.Stop(); //oprirea tuturor firelor de executie
} 3
 Grupurile de fire de execuţie pot să conţină alte grupuri

 Oprirea brutală a firelor de execuţie poate să aibă consecinţe neaşteptate mai ales dacă firele de
execuţie aveau controlul unor resurse
public class Fir extends Thread{
public Fir(String nume){
super(nume);
}
public void run(){
for (int i=0;i<10;i++){
System.out.println(i+" "+getName());
try {
sleep(100);
}
catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("Gata! "+getName());
}
}

class Main{
public static void main(String []args){
Fir f1=new Fir("La");
Fir f2=new Fir("Sol");
f1.start();
f2.start();
System.out.println("Numarul de fire de executie " +Thread.activeCount());
} 4
}
 Programul precedent are 3 fire de execuție, firul main-ului și cele două fire create

 La rulări diferite se obțin ieșiri diferite în funcție de cum se alocă procesorul pentru cele 3 fire

 Dacă firul de execuţie este derivat din clasa Thread şi pornirea lui se face imediat după instanţiere
atunci poate fi modificat constructorul clasei în felul următor:
class FirExecutie1 extends Thread{
public FirExecutie1 (String str){
super(str);
start();
}
}

 Dacă există mai multe fire de execuţie care se execută pe un singur procesor se pune problema
cum se alege firul de execuţie care se va executa la un moment dat

 Alegerea firului de execuţie care se execută la un moment dat se face pe baza priorităţii sale.
Prioritatea unui fir de execuţie este cuprinsă între valorile:

Thread.MIN_PRIORITY //valoarea 1
Thread.MAX_PRIORITY //valoarea 10

 La creare un fir de execuţie are o prioritate egală cu cel care l-a creat

 Modificarea priorităţii unui fir de execuţie se face cu ajutorul metodei setPriority()

5
 Dacă există mai multe fire de execuţie cu aceeaşi prioritate atunci acestea se execută într-o
ordine care depinde de modul în care sistemul de operare tratează firele (cu sau fără divizarea
timpului)

 În orice variantă execuţia este preemtivă adică sistemul poate să întrerupă firul de execuţie curent
pentru a executa un fir cu prioritate mai mare

 În sistemele care asigură divizarea timpului un fir de execuţie rulează pentru un interval de timp
apoi se trece la alt fir de execuţie şi aşa mai departe. Firul de execuţie pierde procesorul în
următoarele situaţii:
 Se termină cuanta de timp alocată
 Firul execută un apel de metodă de blocare (sleep, wait)
 Firul execută un apel explicit de cedare (yield)

 În sistemele fără divizarea timpului, firele pierd procesorul în următoarele situaţii:


 Firul execută un apel de metodă de blocare (sleep, wait)
 Firul execută un apel explicit de cedare (yield)

 Dacă un fir de execuţie vrea să permită execuţia altor fire va executa metoda yield

6
Stările firelor de execuţie:

 Creat - un obiect de tip fir de execuţie este creat cu ajutorul operatorului new()

 Gata de execuţie – după execuţia metodei start() un fir de execuţie poate să fie executat.
Execuţia lui depinde de disponibilitatea procesorului şi de prioritatea firului

 Suspendat – dacă un fir de execuţie a apelat metoda sleep() sau wait() atunci acesta trece în
starea suspendat. Revenirea în starea gata de execuţie se poate face în următoarele moduri:
 Dacă firul a fost blocat prin sleep (durata) firul revine în starea gata de execuţie după
trecerea timpului specificat

 Dacă firul a fost blocat prin wait() atunci în momentul în care se execută una din funcţiile
notify () sau notifyall()

 Dacă firul a fost blocat prin wait(durata) atunci în momentul în care se execută una din
funcţiile notify() sau notifyall() sau la epuizarea intervalului de timp menţionat

 Terminat – dacă metoda run() îşi încheie execuţia sau dacă se execută metoda stop() . Execuţia
metodei stop generează o excepţie de tip ThreadDeath() care extinde clasa Error().

 Dacă în momentul în care se opreşte firul datorita metodei stop() sunt necesare unele prelucrări
atunci obiectul trebuie să fie prins

7
 După terminarea prelucrărilor obiectul trebuie să fie aruncat din nou (throw) altfel prelucrările nu
se mai termină niciodată

 Definiţia clasei Thread permite testarea stării unui fir de execuţie utilizând metoda isAlive().
Valoarea returnată este:
 True dacă firul este în starea gata de execuţie sau suspendat.
 False dacă firul este în starea creat sau terminat
package FireExecutie;

class FirDeExecutie extends Thread{


public void xx()
{
System.out.println("XX");
}

public void run(){


try{
for (int i=1;i < Integer.MAX_VALUE;i++)
if((i/100)*100==i)
System.out.println("S"+i);
}catch(ThreadDeath t){
System.out.println("Am prins");
throw t;
}
for (int i=1;i < Integer.MAX_VALUE;i++)
if((i/100)*100==i)
System.out.println("T"+i);
}
}
8
class Exemplu{
public static void main(String[] args) {
FirDeExecutie f=new FirDeExecutie();
f.xx();

f.start();
System.out.println("Fir gata de executie");

for (int i=0;i<2;i++){


try{
Thread.sleep((int)(Math.random()*1000));
}
catch(InterruptedException e){
System.out.println("Exceptie aici");
}
}

f.stop();
f.xx();
f=null;

for (int i=0;i<2;i++){


try{
Thread.sleep((int)Math.random()*1000);
}
catch(InterruptedException e){}
}
System.out.println("Gata Main");
}
}

9
10.2 Sincronizarea firelor de execuţie
 În cazul unor fire de execuţie care se execută simultan problema sincronizării apare dacă relaţia
dintre ele este de:

 concurenţă – firele încearcă să utilizeze (modifice) aceeaşi resursă


 cooperare –firele schimbă informaţii între ele

 Concurenţa se referă la faptul că la un moment dat cel mult un fir trebuie să aibă acces la resursa
comună. Rolul sincronizării în acest caz este de a asigura accesul exclusiv la resursele comune.
O secvenţă de cod pentru care trebuie să se asigure accesul exclusiv se numeşte regiune critică.

 Schimbul de informaţii în cazul cooperării trebuie să se facă atunci când firele au ajuns la un
anumit moment în execuţia lor. Sincronizarea în acest caz se referă la faptul că procesele, care
pot să se execute cu viteze diferite trebuie să aştepte unele după altele până când îşi pot trimite
informaţiile de care au nevoie

10
Fire de execuţie nesincronizate
 exemplul de mai jos ilustrează că cele doua variabile nu sunt actualizate în mod corect
 la rulări diferite se vor obţine valori diferite pentru cele 2 variabile
 Exemplul se referă la situaţia în care mai multe fire de execuţie modifică o resursă comună
package FireExecutie;

public class FirNesincronizat extends Thread{


static int a=0,b=0;
FirNesincronizat(String nume){
super(nume);
}

void metoda(){
System.out.println(getName()+" a= "+a+" b= "+b);
a++;
try{
sleep((int)(Math.random()*1000));
}
catch(InterruptedException e){}
b++;
}
public void run(){
for (int i=0;i<3;i++)
metoda();
System.out.println( getName()+" GATA a="+ a+"; b="+b);
}
}

class TestSincronizare extends Thread{


public static void main(String[] args) {
new FirNesincronizat("1").start();
new FirNesincronizat("2").start();
System.out.println("GATA main");
}
}
11
 în implementarea propusă metoda în care variabilele care formează resursa comună sunt
modificate (metoda()) nu funcţionează ca o regiune critică şi la un moment dat mai multe fire de
execuţie care o conţin pot să fie în execuţia ei

 Datorită faptului că metoda nu este executată în acces exclusiv, există pericolul ca pe parcursul
execuţiei, un fir de execuţie care a început să execute metoda respectivă să piardă controlul
procesorului şi un alt fir de execuţie care execută aceeaşi metodă dar din alt obiect să înceapă să
se execute

 În exemplul următor acelaşi obiect este accesibil din mai multe fire de execuţie, care execută în
paralel metode ale acestui obiect

//Exemplul 2 cu fire nesincronizate


package FireExecutie;

class AccesComun{
int a=0,b=0; //variabilele nu mai sunt statice

void metoda(String nume){


Thread t=Thread.currentThread();
System.out.println(nume+" a= "+a+" b= "+b);
a++;
try{
t.sleep((int)(Math.random()*1000));
}
catch(InterruptedException e){}
b++;
}
}
12
 În al doilea caz există un obiect de tip accesComun care este transmis ca argument pentru cele
două fire de execuţie care se creează. Cele două fire de execuţie pot să facă acces la aceeaşi
resursă şi să o modifice într-un mod inconsistent

class FirNesincroAltfel extends Thread{


AccesComun a;
FirNesincroAltfel(String nume,AccesComun a){
super(nume);
this.a=a;
}

public void run(){


for (int i=0;i<3;i++)
a.metoda(getName());
System.out.println( " GATA "+ getName());
}
}

class TestSincronizare2 extends Thread{


public static void main(String[] args) {
AccesComun a=new AccesComun();
new FirNesincroAltfel("1",a).start();
new FirNesincroAltfel("2",a).start();
System.out.println("GATA main");
}
}

13
Fire de execuţie sincronizate
 La nivelul limbajului este posibil să se declare secvenţe de cod care vor fi executate în excludere
mutuală, cu ajutorul lui synchronized

 Când începe execuţia unei zone de cod sincronizate, nici un alt fir de execuţie nu poate să
execute zona respectivă de cod. Se creează o coadă de aşteptare.

package FireExecutie; class TestSincronizare3 extends Thread{


public static void main(String[] args) {
public class FirSincronizat extends Thread{ new FirSincronizat("1").start();
static int a=0,b=0; new FirSincronizat("2").start();
System.out.println("GATA main");
FirSincronizat(String nume){
super(nume); }
}
}
void metoda(){
System.out.println(getName()+" a= "+a+" b= "+b); Rularea x: Rularea y:
a++;
try{
sleep((int)(Math.random()*1000));
}
catch(InterruptedException e){}
b++;
}
public void run(){
for (int i=0;i<3;i++) //analizati executia pentru i de la 0 la 99
synchronized(getClass()){
metoda();
}
System.out.println( getName()+" GATA a="+ a+"; b="+b);
}
}
14
 Sau metoda în care se produce modificarea resursei comune are atributul synchronized

 În acest mod execuţiile metodei din acelaşi obiect dar invocate din fire de execuţie diferite sunt
secvenţializate, şi un singur fir de execuţie poate poate să fie în execuţia metodei respective
package FireExecutie; class TestSincronizare4 extends Thread{
public static void main(String[] args) {
class AccesComun2{ AccesComun2 a=new AccesComun2();
int a=0,b=0; new FirSincroAltfel("1",a).start();
new FirSincroAltfel("2",a).start();
synchronized void metoda(String nume){ System.out.println("GATA main");
Thread t=Thread.currentThread(); }
System.out.println(nume+" a= "+a+" b= "+b); }
a++;
try{
t.sleep((int)(Math.random()*1000)); Rularea x: Rularea y:
}
catch(InterruptedException e){}
b++;
}
}

class FirSincroAltfel extends Thread{


AccesComun2 a;
FirSincroAltfel(String nume, AccesComun2 a){
super(nume);
this.a=a;
}

public void run(){


for (int i=0;i<3;i++)// incercati i de la 0 la 99
a.metoda(getName());
System.out.println( " GATA "+ getName());
}
} 15
Modelul producător consumator

 Atât producătorul cât şi consumatorul accesează aceeaşi zonă de memorie

 producătorul depune o valoare în zona de memorie, iar consumatorul extrage o valoare din zonă
de memorie

 producătorul nu poate depune o valoare dacă zona de memorie este ocupată, trebuie să aştepte
eliberarea ei

 consumatorul nu poate extrage o valoare dintr-o zonă de memorie goală, în acest caz trebuie să
aştepte umplerea ei

package FireExecutie;
public synchronized void aTransmis(int valoare){
class ZonaTampon{ if(disponibil){
private int valoare; try{
private boolean disponibil=false; wait();
}catch(InterruptedException e){}
public synchronized int aPreluat(){ }
if(!disponibil){ this.valoare=valoare;
try{ disponibil=true;
wait(); System.out.println("+ A fost depusa
}catch(InterruptedException e){} valoarea "+valoare+" in resursa");
} notify();
disponibil=false; }
notify(); }//end class
System.out.println("- A fost extrasa
valoarea "+valoare+" din resursa");
return valoare;
}
16
class Producator extends Thread{ class Main4{
private ZonaTampon z; public static void main(String[] args) {
ZonaTampon r=new ZonaTampon();
public Producator(ZonaTampon z){ Producator p=new Producator(r);
this.z=z; Consumator c=new Consumator(r);
} p.start();
c.start();
public void run(){ }
for(int i=0;i<10;i++){ }
z.aTransmis(i);

try{
sleep((int)(Math.random()*1000));
}
catch(InterruptedException e){}
}
}
}

class Consumator extends Thread{


private ZonaTampon z;

public Consumator(ZonaTampon z){


this.z=z;
}

public void run(){


int valoare=0;
for(int i=0;i<10;i++){
valoare=z.aPreluat();
}
}
}

17
Utilizare Join
 Dacă un fir de execuţie trebuie să aştepte un alt fir de execuţie ca să preia un rezultat de la el se
poate utiliza Join
 În exemplul de mai jos se creează şi se trece în starea gata de execuţie firul c. Firul aşteaptă
terminarea firului de execuţie care face calcule. Terminarea firului de execuţie se face prin
terminarea metodei run(). Pentru a prelua rezultatul se apelează metoda soluţie ().
package FireExecutie;

class Calcul extends Thread{


private double rezultat;

public void run(){


rezultat=calculeaza();
}

public double solutie(){


return rezultat;
}

public double calculeaza(){


return Math.random();
}
}

class UtilizareJoin{
public static void main(String []args){
Calcul c=new Calcul();
c.start();
try{
c.join();
System.out.println("rezultat="+c.solutie());
}
catch(InterruptedException e){
System.out.println("Eroare");
}
} 18
}
10.3 Utilizarea priorităţilor
 La pornire un fir de execuţie are asociată o prioritate egală cu cea a firului de execuţie care l-a
pornit
 Dacă un fir de execuţie care are o prioritate mai mare decât firul de execuţie care se execută
devine gata de execuţie atunci el va prelua controlul
 Dacă avem două fire de execuţie cu aceeaşi prioritate felul în care vor fi tratate depinde de politica
sistemului de operare
 Dacă exemplul de mai jos afişează doar “unu”, înseamnă că trecerea de la un fir de execuţie la
altul se face doar când firul de execuţie curent se blochează în mod explicit (execută wait(),
sleep()) sau cedează controlul
 Dacă exemplul de mai jos afişează şi “unu” şi “doi”, înseamnă că sistemul de operare atribuie pe
rând cuante de timp celor două fire de execuţie
class Exemplu2 extends Thread{
String mesaj;
public Exemplu2(String mesaj){
this.mesaj=mesaj;
}
public void run(){
while(true)
System.out.println(mesaj);
}
}

class UtilizareExemplu2{
public static void main(String []args){
new Exemplu2("unu").start();
new Exemplu2("doi").start();
}
}
19
 Dacă firului de execuţie “doi” i se va atribui o prioritate mai mare decât firului de execuţie “unu”
atunci vor fi afişaţi mai mulţi “doi” pe ecran
class UtilizareExemplu2{
public static void main(String []args){
new Exemplu2("unu").start();
Exemplu2 e=new Exemplu2("doi");
Thread curent=Thread.currentThread();
e.setPriority(curent.getPriority()+1);
e.start();
}
}

20
Caracteristicile limbajului Java
Se traduce în cod executabil printr-un proces în două faze, ce cuprinde, mai întâi, o compilare,
iar apoi, o interpretare. Compilarea se face de la textul Java la un cod de maşină virtuală Java,
iar interpretarea -de la codul de maşină virtuală Java la codul de maşină de execuţie efectivă,
fizică. Prin termenul “maşină virtuală” se desemnează un program translator de limbaj, de tip
interpretor, emulând un procesor ipotetic. Dezavantaje ale limbajului Java în raport cu C++: 1.
necesitatea ca pe maşina de execuţie să fie întotdeauna prezent traducătorul cod intermediar – cod
maşină –adică: maşina virtuală Java-; 2. lungirea timpului total necesar rulării unui program cu
timpul de interpretare a expresiei lui în cod intermediar.
Are un mare grad de independenţă de platformă. Mai exact, este total independent de platformă,
în ceea ce priveşte compilarea de la text Java la byte code şi dependent în privinţa interpretării de
la byte code la cod maşină. Fisierele byte code sunt executate pe calculatoare ce nu au instalat
mediul de programare Java, ci doar o maşină virtuală Java
Este orientat pe obiecte. Zestrea de clase îmbibliotecate ale mediului Java este extrem de bogată
şi complexă.
Este concurent. Concurenţa în Java este una de tip multithreading. Reamintim că prin thread sau
fir de execuţie înţelegem un modul de program ce reuneşte activităţi autonome sau independente şi
are proprietatea că o sesiune de rulare a sa se poate desfăşura în paralel sau cvasiparalel cu sesiunile
de rulare ale altor module delimitate pe acelaşi principiu sau chiar cu alte sesiuni de rulare ale lui
însuşi.
Prezintă câteva simplificări în raport cu C++, şi anume: nu admite moştenirea multiplă, nu
operează cu pointeri, ci cu referinţe, asupra cărora se pot efectua doar operaţii de atribuire, nu şi
operaţii aritmetice, efectuează alocarea / dezalocarea memoriei implicit (există, totuşi, un mecanism
de eliberare a memoriei şi gestiune a ei raţională, numit “garbage collection”), este mai puternic
tipizat.
Este distribuit. Este distribuit în sensul că permite programarea de aplicaţii în care procesarea
este distribuită. Programele Java pot îngloba, alături de obiecte locale, obiecte aflate la distanţă, în
mod normal în reţea, inclusiv în Internet. În Java, sunt respectate protocoalele de reţea FTP, HTTP,
etc.
Este performant. Dezavantajul că fiecare execuţie a unui program Java incumbă şi un proces de
traducere –interpretarea- este diminuat prin faptul că maşinile virtuale Java sunt realizate excelent –
mai nou: cu atributul JIT (Just In Time)- şi apoi compensat prin aceea că programele pot fi
organizate multithreading.
Este dinamic şi robust. În Java, alocarea memoriei este prin excelenţă dinamică –adică: nu se
face la compilare / linkeditare, ci în execuţie- şi orice alocare este precedată de verificări.
Este sigur. Înainte ca interpretorul Java să execute codul intermediar, acesta este supus unor
verificări de tipul: nedepăşirea stivei, etc.

Interfete(scurta descriere,declararea interfetei ,implementare,utilizarea ca


referinta)
Limbajul Java implementeaza notiunea de interfata din terminologia poo. Printr-o
analogie cu limbajul c++, o interfata poate sa fie privita ca o clasa virtuala pura, adica o clasa
formata numai din metode abstracte si constante.
O interfata defineste un tip, reprezinta o promisiune pe care orice clasa care
implementeaza clasa trebuie sa o respecte. Metodele care apar intr-o interfata sunt in mod implicit
publice, pt ca ar fi contrar notiunii de interfata. Variabilele declarate sunt de fapt constante.
Declararea unei interfete:
interface Exemplu{
int versiune=3;
void prelucrare1();
void prelucrare2(Floare f);
}
Clasa care implementeaza interfata poate specifica codul implementarii metodelor
prelucrare1() si prelucrare2(), dar poate sa declare variabile si metode care nu apar in interfata.
Implementarea interfetei Exemplu:
class Implementare1 implements Exemplu{
/*campurile clasei implementare */ ….....
public void prelucrare1(){ …..........}
public void prelucrare2(Floare f){ …........}
void altaPrelucrare(){ ….........}
}
class Implementare2 implements Exemplu{
…......la fel......
void prelucrareDiferita(){........}
}

Metodele prelucrare1() si prelucrare2() au fost definite cu atribut public pt ca


implementeaza metode din interfata, deci implicit au atributul public. O variabila poate fi declarata
de tipul interfetei. Val variabilei poate fi orice referinta la un obiect care apartine oricarei clase ce
realizeaza implementarea interfetei sau care extinde o astfel de clasa.
Utilizarea unei variabile referinta la o interfata:
Exemplu e; …..
Implementare1 i1 = new Implementare1();
Implementare2 i2 = new Implementare2(); ….........
if (….)
e = i1;
else
e = i2;
e.prelucrare1();

O interfata poate sa reprezinte extensia unei alte interfete, adica sa adauge noi modele
de metode la o interfata care exista. O clasa poate implementa mai mult decat o singura interfata,
adica sa defineasca modul de implementare a metodelor ale caror modele sunt continute in mai
multe interfete. Ex. DataInput si DataOutput, continute in biblioteca Java.io descriu modelele citirii
respectiu scrierii intr-un format independent de masina.

Variabile si metode statice


Pornind de la o definitie de clasa se potobtine instantieri pt mai multe obiecte. Fiecare
dintre aceste obiecte contin cate un exemplar din fiecare variabila pe baza carora a fost definita
clasa. O variabila statica este locala cls de obiecte adica este creata o sg data la incarcarea clasei
respective. O metoda poate sa fie declarata cu atributul static. O astfel de metoda este considerata ca
apartine clasei si nu instantierilor clasei. Metoda main(), care repr pct de plecare pt orice prg Java,
este declarata ca fiind statica si publica si deci poate sa fie referinta fara instantierea unui obiect.
Class Aplicatie{
static final int versiune = 3; static int numarObiecte; …... }
A fost declarata o clasa Aplicatie.Orice obiect istantiat din clasa Aplicatie va avea acces
la var globala versiune. Valoarea var versiune nu poate fi modificata. In schimb var numarObiecte
poate sa fie actualizata de orice obiect instantial, a.i sa poata fi utilizata pt a contoriza nr de obiecte
instantiate din clasa Aplicatie.
Limbajul Java permite declararea unei secv de cod ca fiind statica in modul urmator:
static { secventa de cod }
Astfel de secv se pot declara numai in afara metodelor. In mom in care clasa respectiva
ajunge sa fie incarcata se vor executa toate secventele de cod declarate in acest mod la nivelul
clasei.

Formele instructiunii Try-Catch


Tratarea exceptiilor se face utilizand instructiuni try catch. O astfel de instructiune
indica: secventa de instructiuni in care se considera ca poate sa apara o exceptie, tipul exceptiei(ilor)
si modul in care se face tratarea exceptiei(ilor).
Formele generale ale lui try-catch:
try{ …. } catch(Argument) {…......}
try{} catch (Argument){......} ….... catch(Argument) { ….... } finally{ …... }
try{ …. } catch(Argument){ …... } …. catch(Argument) { …...}
try{ …..} finally { …....}

Despre instructiune:
Intructiunea try are cel mult 3 componente:
1) o secventa de instructiuni prin executia carora ar putea sa apara exceptia. Intre
instructiuni sau o met invocata de acestea, apare o instructiune throw sal se executa o instructiune
care poate produce o eroare recunoscuta de interpretorul masinii virtuale care va “arunca” un obiect
al carui tip este o subclasa pt Exception.
2) perechi de argumente catch(tipExceptie) si secventa de cod care trateaza exceptia. Argumentul
primeste ca valoare un obiect care este o instanta a unei subclase a tipului Exception. Se executa
secv de cod asociata. Secventa de cod repr domeniu de valabilitate pt argument.
3) optional - o secventa de instructiuni precedata de cuv cheie finally. Intotdeauna se va executa
secv de instructiuni precedata de finally.

Cum extragem un subsir dintr-un sir(cod)


O altă metodă a clasei String -metoda substring()-, permite obţinerea unui subşir dintr-
un şir dat. Ea are două forme: una care necesită drept parametri indexul în şir al caracterului de
început al subşirului, respectiv indexul în şir al caracterului imediat următor ultimului caracter al
subşirului, şi o alta care necesită un singur parametru, şi anume: indexul caracterului de început al
subşirului, caracterul de sfârşit al acestuia fiind considerat însuşi ultimul caracter al şirului.
Exemplu:
...
String alfa="Niagara. O roar again!";
String beta=alfa.substring(3,7); // şirul cu referinţa beta va lua valoarea
// ”gara” (atenţie: fără “.”, care are indexul 7)

Applet in HTML
Un applet este o colectie de clase Java care se executa prin interpretarea codului obiect de
catre masina virtuala dintr-un program de navigare care "stie" java. Una dintre clase este clasa
"principala" si fisierul care contine codul ei este fisierul care va fi invocat din textul HTML. Clasa
"principala" trebuie intotdeauna sa extinda o clasa speciala numita Applet.
O prima diferenta intre aplicatiile Java si Applet-uri este ca Applet-urile nu contin metoda
statica main(). Executia unui applet se face prin apelarea de catre programul de navigare a unor metode
cu nume standard ale clasei principale. In urma apelarii acestor metode se vor crea obiecte, fire de
executie sau clase principale.
O a doua diferenta intre aplicatiile Java si Applet-uri este ca un applet are asociata o zona in
fereastra programului de navigare. Tot ce afiseaza un applet se realizeaza prin desenare.
Exemplu applet in HTML:
<HTML><BODY>
<APPLET CODE=HelloWorldApplet.class WIDTH=100 HEIGHT=^)>
</APPLET>
</BODY></HTML>

Marcajul <APPLET> are o serie de atribute obligatorii:


- CODE furnizeaza numele fisierului care contine clasa ce defineste applet-ul
- WIDTH si HEIGHT dau latimea, respectiv inaltimea zonei de eexcran atribuita applet-ului.
Prin atributul CODE al marcajului <APPLET> se specifica numai clasa "principala", restul
claselor fiind aduse de masina virtuala atunci cand este nevoie de ele.
Pe langa aceste atribute obligatorii marcajul mai are: CODEBASE, ALT, ALIGN,
VSPACE, ARCHIVE, OBJECT.
Pe langa aceste atribute se pot specifica si niste parametrii, prin marcajul <PARAM>, insa
doar in blocul <APPLET></APPLET>. Exemple: NUME(numele parametrului), VALUE ( valoarea
parametrului).

Exemplu de applet in HTML care afiseaza informatii despre locul de unde a fost incarcat,
documetul din care este referit si parametrii:
<HTML><BODY>
<APPLET CODE=InfoApplet.class WIDTH=300 HEIGHT=160
CODEBASE='http://gogu.home.ro/APPLET">
<PARAM NAME='info" VALUE="Un sir parametru">
<PARAM NAME="nl" VALUE="6">
<PARAM NAME='nc" VALUE="40">
</APPLET>
</BODY></HTML>
Dupa ce instantiaza un obiect de tipul InfoApplet programul de navigare apeleaza automat
metoda init() care initializeaza applet-ul, prea parametrii si stabileste ce se va afla pe suprafata applet-
ului. Ulterior se apeleaza automat metodele start() - metoda apelata de fiecare data cand este vizitata
pagina careia ii este asociat applet-ul - si stop() - metoda apelata cand este parasita pagina asociata
applet-ului.
Daca urmeaza distrugerea applet-ului se va apela automat metoda destroy() - spre exemplu
atunci cand va fi incarcat din nou- .

CE POT SA FACA APPLET-URILE: deschid ferestre, lanseaza fire de executie, deschid un document
HTML in acelasi program de navigare care le executa, obtin referinte la alte applet-uri
CE NU POT SA FACA APPLET-URILE: nu au acces la fire de executie care nu le apartin, nu pot lansa
in executie programe, nu pot incarca biblioteci cu legare dinamica, nu pot utiliza fisiere, nu pot stabili
conexiuni TCP/IP, nu pot instala un incarcator de clase, nu pot instala un gestionar de securitate.
Lucrarea 1
Concepte de bază ale programării orientate pe obiecte

Cuprins
Concepte fundamentale în programarea orientată pe obiecte ................................................................................. 1
Primul program Java................................................................................................................................................ 2
Pregătirea instrumentelor software necesare………………………………………............................................... 3
Crearea unui proiect nou, compilarea si rularea lui…............................................................................................. 3
Operaţii de intrare/ieşire la nivel de linii de caractere……………………………………………………………..7
Utilizarea mediului de dezvoltare Eclipse .................……………………………………………………………...9
Temă……………………………………………..…………………………………………………………………9

Un program dezvoltat cu ajutorul tehnologiei obiectuale are drept unitate de


construcţie nu subprogramul, ci obiectul. Un obiect înglobează date şi operaţii şi reprezintă o
abstracţiune a unei entităţi din lumea reală. Obiectele componente interacţionează,
determinând transformarea datelor de intrare în date de ieşire, adică rezolvarea problemei.
Într-un program dezvoltat în manieră obiectuală NU mai există date globale (sau, în orice caz,
foarte puţine), datele fiind repartizate şi înglobate în obiecte. Metoda obiectuală de dezvoltare
respectă principii ale ingineriei software precum:
• localizarea şi modularizarea: codul sursă corespunzător unui obiect poate fi scris
şi actualizat independent de alte obiecte;
• ascunderea informaţiei: un obiect are o interfaţă publică pe care celelalte obiecte o
pot utiliza pentru comunicare. Pe lângă interfaţa publică, un obiect poate include
date si operaţii private, „ascunse” faţă de alte obiecte, şi care pot fi modificate
oricând, fără a afecta restul obiectelor;
• reutilizarea codului: clasele de obiecte pot fi definite pe baza claselor existente,
preluând automat (prin moştenire) conţinutul acestora din urmă.

Concepte fundamentale în programarea orientată pe obiecte


Principalele concepte care stau la baza programării orientate pe obiecte sunt:

1) Abstractizarea este una dintre căile fundamentale prin care oamenii ajung să înţeleagă şi
să cuprindă complexitatea. Ea scoate în evidenţă toate detaliile semnificative pentru
perspectiva din care este analizat un obiect, suprimând sau estompând toate celelalte
caracteristici ale obiectului. În contextul programării orientate pe obiecte, toate acestea se
transpun astfel:
• obiectele şi nu algoritmii sunt blocurile logice fundamentale;
• fiecare obiect este o instanţă a unei clase. Clasa este o descriere a unei mulţimi de
obiecte caracterizate prin structură şi comportament similare;
• clasele sunt legate între ele prin relaţii de moştenire.

2) Încapsularea este conceptul complementar abstractizării. Încapsularea este procesul de


compartimentare a elementelor care formează structura şi comportamentul unei
abstracţiuni; încapsularea serveşte la separarea interfeţei „contractuale” de implementarea
acesteia. Din definiţia de mai sus rezultă că un obiect este format din două părţi distincte:
• interfaţa (protocolul);
• implementarea interfeţei.
Programare Java, Laborator 1

Abstractizarea defineşte interfaţa obiectului, iar încapsularea defineşte reprezentarea


(structura) obiectului împreună cu implementarea interfeţei. Separarea interfeţei unui obiect
de reprezentarea lui permite modificarea reprezentării fără a afecta în vreun fel pe nici unul
dintre clienţi, deoarece aceştia depind de interfaţă şi nu de reprezentarea obiectului-server.
3) Modularizarea constă în divizarea programului într-un număr de subunităţi care pot fi
compilate separat, dar care sunt cuplate (conectate) între ele. Gradul de cuplaj trebuie să
fie mic, pentru ca modificările aduse unui modul să afecteze cât mai puţine module. Pe de
altă parte, clasele care compun un modul trebuie să aibă legături strânse între ele pentru a
justifica gruparea (modul coeziv). Limbajele care suportă conceptul de modul fac
distincţia între:
• interfaţa modulului, formată din elementele (tipuri, variabile, funcţii etc.)
„vizibile” în celelalte module;
• implementarea sa, adică elemente vizibile doar în interiorul modulului respectiv.

4) Ierarhizarea reprezintă o ordonare a abstracţiunilor. Cele mai importante ierarhii în


paradigma obiectuală sunt:
• ierarhia de clase (relaţie de tip is a);
• ierarhia de obiecte (relaţie de tip part of).
Moştenirea defineşte o relaţie între clase în care o clasă foloseşte structuri şi
comportamente definite în una sau mai multe clase (după caz vorbim de moştenire simplă sau
multiplă). Semantic, moştenirea indică o relaţie de tip is a („este un/o”). Ea implică o ierarhie
de tip generalizare/specializare, în care clasa derivată specializează structura şi
comportamentul mai general al clasei din care a fost derivată. Agregarea este relaţia între
două obiecte în care unul dintre obiecte aparţine celuilalt obiect. Semantic, agregarea indică o
relaţie de tip part of („parte din”).

Primul program Java


class PrimulProgram{
public static void main(String[ ] args) {
System.out.print(“Hello world!”);
}
}

Referindu-ne strict la structura acestui program, trebuie spus că programele Java sunt
constituite ca seturi de clase (în cazul nostru avem o singură clasă). Pentru descrierea unei
clase se foloseşte o construcţie sintactică de forma:

class nume_clasa {
//continut clasa
}

Aproape orice program, indiferent că este scris într-un limbaj procedural sau obiectual,
are o rădăcină sau un punct de plecare. Astfel, în programele Pascal avem ceea ce se numeşte
program principal, iar în C avem funcţia main(). În programele Java vom avea o clasă
rădăcină care se caracterizează prin faptul că include o funcţie al cărei prototip este:

public static void main(String[ ] args)

2
Concepte de bază ale programării orientate pe obiecte

Elementele desemnate prin cuvintele public şi static vor fi lămurite mai târziu. Numele
clasei rădăcină este un identificator dat de programator. Parametrul funcţiei main() este de tip
tablou de elemente String (şiruri de caractere). Prin intermediul acestui parametru putem
referi şi utiliza în program eventualele argumente specificate la momentul lansări în execuţie a
programului. Acţiunile executate de programul de mai sus presupun două operaţii de afişare:
print() şi println(). Prefixul System.out care însoţeşte numele celor două operaţii reprezintă
numele unui obiect: este vorba despre un obiect predefinit care se utilizează atunci când
destinaţia unei operaţii de scriere este ecranul monitorului. Deoarece metodele sunt incluse în
clase şi, majoritatea, şi în instanţele claselor, apelul presupune precizarea numelui clasei (dacă
e vorba de metode statice) sau al obiectului „posesor”. Cu alte cuvinte, în Java, apelul unei
operaţii se realizează folosind una din notaţiile:

nume_obiect.nume_metoda(parametri_actuali)

sau:

nume_clasa.nume_metoda(parametri_actuali)

Într-un program Java vom lucra cu 2 tipuri de clase:


• clase definite de programator;
• clase predefinite, furnizate împreună cu mediul de dezvoltare Java, care formează
aşa numita API (Application Programming Interface), adică interfaţa pentru
programarea aplicaţiilor.

Revenind la primul nostru program Java, vom spune că metodele print() / println() pot
primi ca parametru un şir de caractere dat, fie sub formă de constante, aşa ca în exemplul
prezentat, fie sub formă de expresii al căror rezultat este de tip String. Metodele print() /
println() mai pot primi ca parametru şi valori ale tipurilor primitive sau referinţe de clase, dar
acestea sunt convertite tot la tipul String înainte de afişare. Println() poate fi apelată şi fără
parametri, caz în care execută doar un salt la linie nouă: System.out.println( );

Pregătirea instrumentelor software necesare


Înainte de editarea codului şi de rularea primei aplicaţii este necesară instalarea
instrumentelor software necesare. Acestea sunt:

• JDK (Java Development Kit) JRE (Java Runtime Environment) for Windows:
http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html

• Eclipse IDE for Java EE Developers:


www.eclipse.org/downloads

Crearea unui proiect nou, compilarea si rularea lui


La prima rulare a aplicaţiei Eclipse trebuie selectat spaţiul de lucru (workspace), loc în
care vor fi salvate toate proiectele aflate în lucru. După pornire va apărea fereastra „Welcome”
care poate fi închisă. De asemenea, la prima rulare se alge ca aspectul mediului Eclipse să fie
adecvat pentru realizarea aplicaţiilor Java SE (Java Standard Edition) (vezi figura 1.1).

3
Programare Java, Laborator 1

Fig. 1.1 – configurare Eclipse Juno pentru aplicaţii Java SE

Următorul pas presupune crearea unui nou proiect din meniul File (File > New > Java
Project). În fereastra care se deschide (New Java Project – vezi figura 1.2) se atribuie un
nume proiectului şi se apasă comanda „Finish”.

Fig 1.2 - Crearea unui nou proiect Java

În continuare proiectul creat poate fi vizualizat în fereastra „Package Explorer” (vezi


figura 1.3).

Fig 1.3 - Vizualizarea proiectului nou creat în fereastra Package Explorer

În următoarea etapă trebuie adăugat un pachet nou în proiectul creat (vezi figura 1.4).

Un pachet reprezintă un grup de tipuri de date (clase, interfeţe, enumerări etc.) care au
legătură între ele. Pachetele oferă protecţie la acces şi gestionarea spaţiului de nume. Câteva
dintre avantajele utilizării pachetelor sunt următoarele:

4
Concepte de bază ale programării orientate pe obiecte

• Pot fi grupate anumite tipuri şi astfel pot fi determinate cu uşurinţă care sunt
tipurile care au legătură între ele.
• Sunt evitate conflictele dintre numele tipurilor aflate în pachete diferite (deoarece
un pachet creează un spaţiu de nume).

Fig 1.4- Adăugarea unui pachet nou

Se introduce un nume pachetului şi se apasă comanda „Finish” (vezi fig 1.5).

Fig 1.5 – crearea unui pachet si a directoarelor corespunzatoare

Următoarea etapă constă în crearea unei noi clase (vezi fig 1.6), în pachetul selectat.
:

Fig 1.6 – lansarea comenzii de creare a unei noi clase în pachetul selectat

În fereastra pentru crearea unei noi clase se introduce numele acesteia (se recomandă
introducerea unei denumiri care începe cu literă mare) şi se menţionează dacă clasa conţine
metoda main (vezi fig 1.7).

5
Programare Java, Laborator 1

Fig 1.7 – crearea unei noi clase

Odată cu crearea clasei se va crea un fişier care conţine numele clasei şi extensia java
(în cazul nostru PrimulProgram.java). Fişierele java sunt amplasate în următoarea cale:

director_lucru > director_proiect > src > director_pachet > fisier.java

Fig 1.8 – Completarea codului clasa creată

Compilarea şi executarea programului se face apăsând butonul Run (vezi figura 1.9)
sau apăsând combinaţia de taste Ctrl+F11.

În urma compilării vor rezulta un număr de fişiere egal cu numărul claselor conţinute
de sursă. Fiecare dintre fişierele rezultate are extensia .class şi numele identic cu numele

6
Concepte de bază ale programării orientate pe obiecte

clasei căreia îi corespunde (în cazul nostru PrimulProgram.class). Fişierele class vor fi
amplasate pe următoarea cale:

director_lucru > director_proiect > bin > director_pachet > fisiere.class

Un fişier .class conţine cod maşină virtual (Java Byte Code). Acesta este codul maşină
al unui calculator imaginar. Pentru a putea fi executat pe o maşină reală, este necesar un
interpretor (sau executiv) care să execute fiecare instrucţiune a codului virtual în termenii
operaţiilor maşină ai calculatorului real. După compilare, programul în cod virtual obţinut
poate fi transportat pe orice maşină pentru care există executivul corespunzător, iar scrierea
unui executiv este mult mai uşoară decât a unui compilator întreg.

Fig 1.9 – rularea programului

Ieşirea programului este afişată în Consolă.

Fig 1.10 – execuţia programului

Operaţii de intrare/ieşire la nivel de linii de caractere


Pachetul java.io este un set de clase cu ajutorul cărora se realizează operaţiile de
intrare/ieşire într-un program Java. În Java, operaţiile de intrare / ieşire se bazează pe
conceptul de flux de date (stream). Un flux de date este o secvenţă de date care se deplasează
dinspre o sursă externă spre memorie (flux de intrare — input stream) sau din memorie spre o
destinaţie externă (flux de ieşire — output stream). Pentru operaţiile de intrare / ieşire cea mai
frecventă sursă externă este tastatura, iar destinaţia este ecranul monitorului. Acestea se mai
numesc şi suporturi standard de intrare, respectiv ieşire. Corespunzător suporturilor standard,
în Java exista două obiecte predefinite: System.in pentru tastatură şi System.out pentru
monitor. O linie de caractere este o secvenţă terminată cu un caracter special, numit
terminator de linie, care, la tastatură, are drept corespondent tasta Enter. Pentru a realiza citiri

7
Programare Java, Laborator 1

la nivel de linie vor fi utilizate două subclase ale clasei Reader (BufferedReader şi
InputStreamReader). Pentru scrierea de linii, se va folosi clasa PrintStream.

Operaţii de citire a liniilor de text

Clasa care modelează citirea unei linii dintr-un flux de intrare este BufferedReader,
prin operaţia readLine(). Această operaţie nu are parametri, iar execuţia ei are ca efect citirea
din fluxul de intrare a unei secvenţe de caractere până la întâlnirea terminatorului de linie.
Operaţia returnează o referinţă la un obiect String care conţine caracterele citite, dar fără a
include şi terminatorul. Cu alte cuvinte, şirul returnat conţine doar caracterele utile
(semnificative) ale liniei. Dacă s-a ajuns la sfârşitul fluxului de intrare, operaţia returnează
valoarea null. Dacă citirea nu se poate desfăşura, operaţia emite o excepţie de tip IOException.
De aceea, semnătura unei funcţii care apelează metoda readLine(), dar nu tratează eventualele
erori de citire, trebuie sa conţină clauza throws IOException. Una dintre cele mai frecvente
erori într-un program Java este omiterea clauzelor throws din antetul funcţiilor utilizatorului
care apelează funcţii predefinite dotate cu această clauză. Pentru a crea un obiect al clasei
BufferedReader este necesar să i se furnizeze constructorului acesteia o referinţă la un obiect
al clasei InputStreamReader. Constructorul acesteia din urmă necesită:
• referinţă la un obiect FileInputStream, dacă citirea se face dintr-un fişier de pe disc
• referinţă System.in, dacă citirea se face de la tastatură.
Deci, dacă urmează o citire dintr-un fişier al cărui nume este dat de o variabilă String
nume_fis, va trebui să se creeze un obiect BufferedReader ca în una din cele două secvenţe de
cod de mai jos:

BufferedReader flux_in;
flux_in = new BufferedReader(new InputStreamReader(new FileInputStream(nume_fis)));

//SAU

flux_in = new BufferedReader(new FileReader(nume_fis));

Dacă citirea se va face de la tastatură, obiectul BufferedReader se creează astfel:

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

În continuare, citirea se va realiza cu apelul:

linie = flux_in.readLine();

unde linie este o referinţă String. În urma apelului, ea va indica spre un obiect care
conţine caracterele citite. Când se ajunge la sfarsitul fișierului funcția readLine() returnează
null. În funcţie de natura datelor reprezentate de aceste caractere, uneori pot fi necesare
conversii de la String la alte tipuri, în vederea utilizării datelor respective în diverse calcule.
Toate clasele înfășurătoare, asociate tipurilor primitive conțin metode de conversie. Spre
exemplu clasa înfășurătoare a tipului primitiv int se numește Integer și conține metoda statica
parseInt(String) care realizeză conversie de la String la tipul primitiv asociat:

int x=Integer.parseInt(“123”);

8
Concepte de bază ale programării orientate pe obiecte

Operaţii de citire a tipurilor primitive

Utilizarea obiectelor BufferdReader la citirea intregilor, numerelor reale, etc,


presupune citirea unui șir de caractere si apoi conversia lui în tipul primitiv dorit. Citirea
directă a tipurilor primitive se poate realiza cu ajutorul obiectelor Scanner în felul următor:

Scanner scanner=new Scanner(System.in);


int x = scanner.nextInt();

Pentru citire din fișier instanțierea se face în felul următor:

Scanner scanner=new Scanner(new File(“in.txt”));


Se poate testa dacă mai sunt elemente de citit din fișier cu ajutorul funcției hasNext().

Operaţii de scriere a liniilor de text

Afişarea unei linii pe ecran se realizează apelând metodele print() / println() definite în
clasa PrintStream, pentru obiectul System.out. Pentru a scrie o linie într-un fişier de pe disc, se
vor folosi aceleaşi metode, dar va trebui să se creeze un obiect separat al clasei PrintStream.
Pentru aceasta, trebuie furnizată constructorului clasei PrintStream, ca parametru, un String
care reprezintă numele fişierului, ca în secvenţa de mai jos:

PrintStream flux_out = new PrintStream (nume_fis);

unde nume_fis este o referinţă la un obiect String ce conţine numele fişierului.

Utilizarea mediului de dezvoltare Eclipse

Facilitatea de autocompletare

Eclipse dispune de o facilitate de autocompletare a denumirilor de obiecte, clase, clase


abstracte, interfețe, enumerări, etc. Pentru a utiliza această facilitate se completează primele
litere ale numelui și apoi se apasă Ctrl+Space. În situația în care există o singură entitate a
cărei denumire începe cu literele completate, Eclipse va completa automat denumirea acesteia.
În cazul în care există mai multe entități a căror denumire începe cu literele tastate se va
deschide o listă cu acestea din care trebuie aleasă cea dorită. Autocompletarea ajută la editarea
rapidă și corectă a codului.
Exemplu de utilizare: Să presupunem că doriți să declarați un obiect de tip
BufferedReader. Scrieți Buf apoi apăsați Ctrl+Space și din lista selectați clasa dorită.

Indentarea codului sursă

Pentru a crește lizibilitatea codului, acesta trebuie scris indentat. Liniile de cod ale
unei funcții se scriu cu un tab spre interiorul funcției. Dacă codul respectiv conține o
instrucțiune repetitivă (for, while, do…while) atunci codul care aparține de acea instrucțiune
repetitivă se pune la un tab de începutul ei, etc. Eclipse dispune de o facilitate de

9
Programare Java, Laborator 1

autoindentare a codului sursă, Ctrl+A – selectează codul sursă si Ctrl+I – autoindentează


codul sursă.

Depanarea programelor

Prima acțiune care trebuie realizată atunci când se dorește depanarea unui program
este modificarea persepctivei din Eclipse. Se alege opțiunea de meniu Window > Open
perspective > Debug sau se lansează aceeași comandă prin accționarea butonului Open
Perspective de pe bara de unelte din colțul dreapta sus. În continuare se pune un Break Point
pe linia pe care se dorește întreruperea execuției programului. Break pointul se introduce cel
mai rapid prin dublu click în partea stangă a cifrei care indică numărul liniei de cod. Pentru ca
programul să ruleze până la break point trebuie lansată comanda Debug (opțiunea de meniu
Run > Debug) sau și mai rapid se apasă tasta F11). Programul va rula până la break point și în
continuare poate fi rulat pas cu pas urmărind evoluția variabilelor în memorie. Se poate utiliza
comanda F5 (Step into) ca să intre în metodele apelate și să permită executarea acestora pas
cu pas, sau F6 (Step over) ca să execute metodele apelate cu totul fără a intra în ele. Valorile
tuturor variabilelor active în zona de cod care se depanează este afișat în secțiune Variables, a
perspecțivei Debug. După ce depanarea programului s-a încheiat se revine la persepctiva
adecvată dezvoltării de aplicații Java Standard Edition.

Temă
1. Se cere să se scrie un program Java care să calculeze şi să afişeze perimetru şi aria unui
dreptunghi. Valorile pentru lungime şi lățime se citesc de la tastatura. Sa se adauge un break
point pe prima linie care citește valoarea unei laturi si din acel punct să se ruleze programul
linie cu linie urmărind valorile variabilelor în memorie.

2. Să se scrie un program care citește un set de numerele din fișierul de intrare in.txt, care are
conținutul din figura 1.11. Programul va determină suma lor, media aritmetică, valoarea
minimă, valoarea maximă, va afișa aceste valori pe ecran și le va scrie în fișierul de ieșire
out.txt. Media aritmetică va fi afișată ca un număr real.

Fig. 1.11 –fișierul in.txt

3. Să se scrie un program care citește un număr n natural de la tastatură și afișează toți


divizorii acestuia pe ecran. Dacă numărul este prim se va afișa un mesaj corespunzător.

4. Să se determine cmmdc a două numere naturale, a căror valoare maximă este 30. Numerele
vor fi generate aleatoriu cu ajutorul unui obiect de tip Random și metodei nextInt();

5. Să se scrie un program care generează aleatoriu un număr întreg cuprins între 0 și 20.
Progamul va determina dacă numărul aparține șirului lui Fobonacci.

10
Lucrarea 2
Componenţa unei clase. Crearea şi iniţializarea obiectelor.
Tablouri

Cuprins
Componenţa unei clase............................................................................................................................................. 1
Crearea obiectelor.................................................................................................................................................... 1
Modificatori de acces............................................................................................................................................... 2
Iniţializarea câmpurilor unui obiect. Constructori .................................................................................................. 3
Membri statici ai claselor......................................................................................................................................... 5
Iniţializarea variabilelor…....................................................................................................................................... 6
Simbolul this.............................................................................................................................................................6
Colectorul de reziduuri (Garbage Collector) ...........................................................................................................8
Tablouri în Java ........................................................................................................................................................9
Temă......................................................................................................................................................................... 9

Componenţa unei clase

În limbajele de programare orientate pe obiecte, clasele pot fi considerate ca fiind


mecanisme prin care programatorul îşi construieşte propriile sale tipuri de date, pe care, apoi,
le va folosi aproape la fel cum foloseşte tipurile predefinite. Fiind un tip de date, unul dintre
rolurile fundamentale ale clasei este acela de a servi la declararea variabilelor. Valorile unui
tip clasă se numesc obiecte sau instanţe ale clasei respective.
O clasă reprezintă descrierea unei mulţimi de obiecte care au aceeaşi structură şi
acelaşi comportament. Prin urmare, o clasă va trebui să conţină definiţiile datelor şi ale
operaţiilor ce caracterizează obiectele acelei clase. Datele definite într-o clasă se mai numesc
date-membru, variabile-membru, atribute sau câmpuri, iar operaţiile se mai numesc metode
sau funcţii membru. Pentru a arăta felul în care se definesc membrii unei clase, va fi utilizat
următorul exemplu:

class Punct{
private int x;
private int y;
public void init(int xx, int yy) {
x = xx; y = yy;
}
public void move(int dx, int dy) {
x += dx; y += dy;
}
public int getX( ) { return x; }
public int getY( ) { return y; }
}

Clasa modelează un punct şi are ca date-membru două variabile de tip întreg: x şi y,


reprezentând coordonatele punctului, iar ca metode:
• funcţia init(), iniţializează coordonatele unui punct
• funcţia move(), deplasează un punct pe o anumită distanţă
• funcţiile getX() şi getY(), returnează coordonatele curente ale unui punct.
Programare Java, Laborator 2

Crearea obiectelor
În continuare se va considera un mic program Java pentru a vedea cum se creează
obiectele clasei Punct descrisă anterior:

class Punct{
private int x;
private int y;
public void init(int xx, int yy) {
x = xx; y = yy;
}
public void move(int dx, int dy) {
x += dx; y += dy;
}
public int getX( ) { return x; }
public int getY( ) { return y; }
}

class MainApp {
public static void main(String[ ] args) {
Punct p1 = new Punct( ); //se creează o instanţă a clasei Punct
Punct p2 = new Punct( ); // şi încă una
p1.init (10,20); p2.init (30,40); //se apeleaza metodele init ale instantelor
p1.move(5,5); p2.move(6,-2); //se apeleaza metodele move
System.out.println("(x1,y1) = ("+p1.getX()+","+p1.getY( )+ ")"); //se afiseaza
//coordonatele curente ale primului punct
System.out.println("(x2,y2) = ("+p2.getX()+","+ p2.getY( )+ ")"); //se afiseaza
//coordonatele curente ale celui de-al 2-lea punct
}
}

În Java toate obiectele se creează în mod dinamic. Până nu se iniţializează variabila


respectivă cu adresa unei zone de memorie alocată pentru valori ale tipului indicat de pointer,
practic nu se poate folosi variabila în nici un fel. În programul de mai sus se observă aplicarea
operatorului de alocare dinamică new.

Modificatori de acces
Se poate observa că definiţiile datelor şi ale funcţiilor din clasa Punct sunt prefixate de
anumite cuvinte cheie (public, private) care se numesc modificatori de acces. Aceştia
stabilesc drepturile de acces ale clienţilor la membrii unei clase. Când se discută despre
drepturile de acces la membrii unei clase trebuie să se abordeze acest subiect din două
perspective:
• Interiorul clasei sau, mai concret, metodele clasei. În cadrul metodelor unei clase
există acces nerestrictiv la toţi membrii, date sau funcţii. De exemplu, în metodele
clasei Punct se face referire la câmpurile x şi y. În interiorul clasei nu se foloseşte
notaţia cu punct pentru a referi membrii, aceştia fiind pur si simplu accesaţi prin

2
Componenţa unei clase. Crearea şi iniţializarea obiectelor. Tablouri

numele lor. Când o metodă face referire la alţi membri ai clasei, de fapt sunt accesaţi
membrii corespunzători ai obiectului receptor, indiferent care ar fi el. De exemplu,
când se apelează metoda init() a obiectului referit de p1, are loc iniţializarea
membrilor x şi y ai acelui obiect. În legătură cu accesul din interiorul unei clase,
trebuie spus că absenţa restricţiilor se aplică şi dacă este vorba despre membrii altui
obiect din aceeaşi clasă, dar diferit de cel receptor. De exemplu, dacă în clasa Punct
am avea câte o metodă de calcul a distanţei pe verticală/orizontală dintre 2 puncte,
unul fiind obiectul receptor, iar celălalt un obiect dat ca parametru, atunci am putea
scrie:

class Punct{
//. . .
public int distV(Punct p) {
return y - p.y;
}
public int distH(Punct p) {
return x - p.x;
}
//. . .
}

Se observă că din interiorul metodelor distV() / distH() putem accesa liber membrii
privaţi ai obiectului p dat ca parametru. La fel ar sta lucrurile şi dacă p ar fi o variabilă locală
a unei metode din clasa Punct.
• Exteriorul sau clienţii clasei. Clienţii unei clase pot accesa doar acei membri care au
ca modificator de acces cuvântul public. Membrii declaraţi cu modificatorul private
NU sunt vizibili în afară, sunt ascunşi. Dacă s-ar încerca folosirea, în metoda main()
din exemplul considerat, o referinţă de genul:

p1.x

compilatorul ar raporta o eroare. Structura unei clase, sau modul ei de reprezentare,


care este dat de variabilele membru, de regulă se ascunde faţă de clienţi. Dacă este
necesar ca aceştia să poată consulta valorile datelor membru, se va opta pentru
definirea unor metode de genul getValoare(), iar nu pentru declararea ca publice a
datelor respective.

Iniţializarea câmpurilor unui obiect. Constructori


În exemplul considerat anterior, după crearea obiectelor p1 şi p2, s-a apelat pentru ele
metoda init() care avea ca scop iniţializarea câmpurilor x şi y pentru cele două puncte. Acesta
este un exemplu de iniţializare a câmpurilor unui obiect, însă are unele dezavantaje printre
care şi acela că programatorul ar putea omite apelul metodei, caz în care câmpurile vor avea
valori implicite. În acest sens, programatorul poate prevedea la definirea unei clase una sau
mai multe metode speciale, numite constructori, care servesc tocmai la iniţializarea datelor
unui obiect imediat ce el a fost creat. Exemplul următor, descris tot folosind clasa Punct, va
reliefa acest lucru:

3
Programare Java, Laborator 2

class Punct{
//. . .
public Punct(int xx, int yy) {
x = xx; y = yy;
}
public Punct(Punct p) {
x = p.x; y = p.y;
}
//. . .
}
Caracteristicile unui constructor sunt:
• este o metodă care are acelaşi nume cu clasa în care este definită
• nu are tip returnat (nici măcar void)
• se apelează în mod automat la crearea unui obiect

Astfel, în locul metodei init(), în funcţia main() a clasei MainApp, se poate scrie:

class MainApp {
public static void main(String[ ] arg) {
Punct p1 = new Punct(10,20 ); //se creează o instanţă a clasei Punct
Punct p2 = new Punct(30,40 ); // şi încă una
//. . .
}
}
O clasă poate avea mai mulţi constructori, datorită facilităţii de supraîncărcare a
funcţiilor existentă în limbajul Java. Se oferă, astfel, posibilitatea de a defini, în acelaşi
domeniu de vizibilitate, mai multe funcţii care au acelaşi nume, dar parametrii diferiţi ca tip şi
număr. Al doilea constructor definit anterior poate fi apelat astfel:

Punct p3 = new Punct(p1);

Dacă programatorul nu prevede nici un constructor, atunci compilatorul va prevedea


clasa respectivă cu un constructor implicit de tip no-arg, al cărui corp de instrucţiuni este vid.
Aşa au stat lucrurile în cazul primei definiţii a clasei Punct când la crearea unui obiect a fost
apelat constructorul implicit Punct(). De asemenea, pentru a putea crea obiecte ale claselor
descrise, constructorii trebuie să aibă modificatorul de acces public. Pe lângă constructori, mai
există şi alte metode de iniţializare a câmpurilor unei clase:

• Iniţializatori la declarare, care pot fi impliciţi sau expliciţi:

class Punct{
private int x; //iniţializator implicit
private int y = 1; //iniţializator explicit
//se presupune că nu există constructori
}
//. . .
Punct p1 = new Punct(); //în acest caz p1.x = 0 si
p1.y=1

4
Componenţa unei clase. Crearea şi iniţializarea obiectelor. Tablouri

Iniţializatorii impliciţi depind de tipul câmpului respectiv: sunt 0 pentru tipurile


numerice, false pentru tipul boolean şi null pentru referinţe la obiecte.
• Blocuri de iniţializare: sunt secvenţe de cod cuprinse între acolade, plasate imediat
după declaraţia câmpului pe care îl iniţializează, şi care pot fi asimilate cu nişte
constructori no-arg. Ele se folosesc atunci când valoarea de iniţializare a unui câmp nu
este o constantă simplă, ci trebuie obţinută prin calcule. Ca exemplu, se va presupune
că variabila x din clasa Punct trebuie iniţializată cu valoarea sumei primelor 6 numere
Fibonacci:

class Punct{
private int x=2; //iniţializator explicit
{ //bloc de iniţializare
int a=1,b=1;
for(int i=3;i <= 6;i++)
{
b+=a; a=b-a;
x+=b;
}
//. . .
}

Deosebirea esenţială dintre constructori şi celelalte tipuri de iniţializări este aceea că,
în cazul constructorilor, se putea iniţializa fiecare instanţă cu valori diferite care se transmit ca
parametri. Celelalte mecanisme de iniţializare presupun ca toate instanţele clasei respective să
aibă aceleaşi valori. Dacă se doreşte crearea şi iniţializarea unui câmp cu comportare
echivalentă unei constante din limbajul C++, atunci câmpul este marcat ca final, cuvânt cheie
plasat între modificatorul de acces şi tip.

Membri statici ai claselor


Până acum, în clasa Punct s-au definit doar membri non-statici. Aceştia se mai numesc
şi membri ai instanţelor. Limbajul Java permite definirea unei categorii speciale de membri,
numiţi statici sau membri de clasă. Aceşti membri vor exista în exemplare unice pentru
fiecare clasă, fiind accesaţi în comun de toate instanţele clasei respective. Mai mult, membrii
statici pot fi referiţi chiar şi fără a instanţia clasa, ei nedepinzând de obiecte. Pentru a defini
un membru static se utilizează cuvântul cheie static, plasat după modificatorul de acces:

modificator_acces static tip_membru nume_membru;

Referirea unui membru static nu se va face prin intermediul numelui obiectului, ci prin
intermediul numelui clasei:

nume_clasa.nume_membru_static

Pentru exemplificare, se va modifica clasa Punct astfel încât să se poată calcula


numărul de apeluri ale metodei move() pentru toate obiectele create într-un program. Pentru
aceasta, se va defini o variabilă statică contor şi o metodă statică getContor():

5
Programare Java, Laborator 2

class Punct{
private int x;
private int y;
private static int contor=0; //iniţializator implicit
public Punct(int xx,int yy ) { x=xx; y=yy; }
public void move(int dx, int dy) { x+=xx; y+=yy;contor++;}
public static int getContor() {return contor;}
//. . .
}
//. . .
//metoda main din clasa rădăcină
Punct p1 = new Punct(10,20);
Punct p2 = new Punct(15,13);
p1.move(8,-2); p2.move(6,7);
//. . .
System.out.println(”S-au executat “+Punct.getContor()+”mutari.”);

În legătură cu membrii statici ai unei clase trebuie făcută următoarea observaţie: într-o
metodă statică nu este permisă referirea simplă a unui membru non-static. Acest lucru se
explică prin aceea că un membru non-static există doar în interiorul unui obiect, pe când
membrii statici există independent de obiecte. Dacă în exemplul dat s-ar fi declarat variabila
contor ca non-statică, atunci ea ar fi existat în atâtea exemplare, câte obiecte s-ar fi creat şi ar
fi contorizat pentru fiecare obiect în parte numărul de apeluri ale metodei move().

Iniţializarea variabilelor

În Java, variabilele al căror tip este o clasă sunt reprezentate ca referinţe (adrese) spre
obiecte ale clasei. Pentru a putea iniţializa o asemenea referinţă, trebuie generat un obiect nou
sau folosită o referinţă la un obiect deja existent. Unei referinţe îi poate fi atribuită valoarea
specială null, care se traduce prin faptul că referinţa respectivă nu indică nici un obiect.
Valoarea null nu este atribuită automat tuturor variabilelor referinţă la declararea lor, ci
conform următoarei reguli: dacă referinţa este o dată membru a unei clase şi ea nu este
iniţializată în nici un fel, la crearea unui obiect al clasei respective referinţa va primi implicit
valoarea null. Dacă referinţa este o variabilă locală a unei metode, iniţializarea implicită nu
mai funcţionează. De aceea, se recomandă ca programatorul să realizeze ÎNTOTDEAUNA o
iniţializare explicită a variabilelor.

Simbolul this
Simbolul this este o referinţă care poate fi utilizată doar în cadrul funcţiilor membru
non-statice ale unei clase. Din punctul de vedere al unei funcţii membru, acesta este referinţa
spre obiectul receptor, „posesor“ al acelei funcţii. Se poate spune că this reprezintă „conştiinţa
de sine“ a unui obiect, în sensul că obiectul îşi cunoaşte adresa la care este localizat în
memorie. Practic, orice referire a unei date membru nonstatice v în interiorul unei metode,
poate fi considerată ca echivalentă cu this.v. De exemplu, metoda move() din clasa Punct,
poate fi scrisă sub forma:

6
Componenţa unei clase. Crearea şi iniţializarea obiectelor. Tablouri

class Punct {
private int x;
private int y;
//. . .
public void move(int dx, int dy) {
this.x += dx;
this.y += dy;
}
//. . .
}
Referinţa this este folosită explicit în cazul în care ar putea exista conflicte de nume cu
datele membru ale unui obiect. De exemplu:
class Punct {
private int x,y;
//. . .
public Punct(int x, int y){
// parametrii constructorului au nume
// identice cu cele ale datelor membru
this.x = x;
this.y = y;
}
}
O altă situaţie când se foloseşte explicit referinţa this este atunci când referinţa la
obiectul receptor trebuie transmisă ca parametru la apelul unei alte metode.

class Context {
private int x;
private Algoritm a;
// obiectul Algoritm va executa anumite calcule pentru un obiect Context

public Context(Algoritm a, int x) {


this.a = a;
this.x = x;
}
public int Calcul() {
x = a.Calcul(this);
return x;
}
public int getX() { return x; }
}

class Algoritm {
public int Calcul(Context c) {
return c.getX()*c.getX();
}
}

7
Programare Java, Laborator 2

În fine, simbolul this reprezintă mijlocul prin care poate fi apelat un constructor al unei
clase din interiorul altui constructor al aceleiaşi clase.

class OClasa {
private int x,y;
public OClasa(int x, int y) {
this.x = x;
this.y = y;
}
public OClasa(OClasa p) {
this(p.x, p.y); // se apelează constructorul cu 2 parametri int
}
//. . .
}

Apelul unui constructor din interiorul altui constructor al clasei în cauză NU înseamnă
crearea unui nou obiect, ci, pentru obiectul receptor curent, se va executa codul
constructorului apelat, la fel ca în cazul apelării unei funcţii-membru obişnuite. De asemenea,
un constructor nu poate fi apelat cu ajutorul simbolului this DECÂT din interiorul altui
constructor şi nu al altor metode. În felul acesta se asigură îndeplinirea condiţiei ca pentru un
obiect iniţializarea datelor prin constructor să se execute O SINGURĂ dată. Referinţa this NU
poate fi utilizată în interiorul unei funcţii-membru statice. Referinţa this nu poate fi modificată
(nu poate să apară ca membru stâng într-o atribuire) şi nu poate să apară în afara corpului de
instrucţiuni al metodelor non-statice ale unei clase sau în afara blocurilor de iniţializare
asociate cu variabilele membru ale unei clase.

Colectorul de reziduuri (Garbage Collector)


Spre deosebire de limbaje precum Pascal sau C, unde programatorul trebuie să
elibereze memoria ocupată de obiecte după ce acestea nu mai sunt folosite, în Java
programatorul este scutit de această sarcină de care se ocupă o componentă a maşinii virtuale
numită Garbage Collector. Principiul de lucru al acestuia este următorul: dacă spre un anumit
obiect nu mai există nici o referinţă externă, în nici o funcţie activă, acel obiect devine
candidat la eliminarea din memorie.
Pentru utilizator, acţiunea Garbage Collector-ului este transparentă. Există
posibilitatea de a „surprinde“ momentul în care un anumit obiect este eliminat, deoarece
Garbage Collector-ul nu acţionează „fără preaviz“. Astfel, dacă în clasa de care aparţine
obiectul este definită o metodă numită finalize(), cu prototipul:

protected void finalize() throws Throwable;

această metodă va fi executată chiar înainte ca obiectul să dispară. Dacă metoda este
definită astfel încât să tipărească pe ecran un anumit mesaj, poate fi detectat momentul în care
un obiect este eliminat din memorie.

8
Componenţa unei clase. Crearea şi iniţializarea obiectelor. Tablouri

Tablouri în Java
Limbajul Java oferă tipul tablou (tip predefinit), cu ajutorul căruia se pot crea colecţii
de variabile de acelaşi tip. Componentele tabloului pot fi accesate cu ajutorul unui index.
Acesta trebuie să aparţină unui tip întreg, iar numerotarea elementelor într-un tablou începe
întotdeauna de la 0, la fel ca şi în C. Un tablou se declară astfel:

tip_element[ ] nume_tablou;

În Java, elementele unui tablou se alocă DINAMIC, specificându-se dimensiunea


tabloului, iar numele unui tablou este considerat ca o REFERINŢĂ DE OBIECT:
tip_element[ ]

nume_tablou = new tip_element [numar_elemente];

La crearea unui tablou se memorează dimensiunile sale, programatorul putând în orice


moment să le cunoască apelând la atributului length:

static void oFunctie(int[ ] tabp) {


//. . .
for(int i=0; i < tabp.length; i++) {
// tabp.length returnează dimensiunea tabloului
// prelucrează tabp[ i ]
}

}
public static void main(String[ ] arg) {
int [ ] tab = new int[10];
//. . .
oFunctie(tab);
//. . .
}
}

Dacă elementele unui tablou sunt, la rândul lor, de tip clasă, elementele respective vor
fi referinţe la obiecte ale acelei clase. La crearea tabloului se rezervă spaţiu DOAR pentru
acele referinţe şi ele sunt iniţializate cu valoarea null.

Java acceptă şi declaraţii de tablouri bidimensionale:

tip_element[ ] [ ] nume_matrice;

9
Programare Java, Laborator 2

Temă
1. Să se parcurgă exemplele prezentate în laborator şi să se testeze practic (acolo unde
este cazul).

2. Funcţia f ( x) = ax 2 + bx + c are ca grafic o parabolă cu vârful de coordonate


 b − b 2 + 4ac 
 − ;  .
 2a 4a 

Se cere să se definească o clasă Parabola ai cărei membri vor fi:


• 3 variabile de tip double care reprezintă coeficienţii a, b şi c
• un constructor cu 3 parametrii de tip double
• un constructor cu un parametru de tip Parabola
• o metodă de afişare a funcţiei sub forma: f(x) = a x^2 + b x + c
• o metodă pentru calculul coordonatelor vârfului
• o metodă statică ce primeşte ca parametri două parabole şi calculează coordonatele
mijlocului segmentului de dreptă care uneşte vârfurile celor două parabole astfel:
x + x2 y + y2
x= 1 , y= 1 , unde ( x1 , y1 ) sunt coordonatele vârfului primei parabole,
2 2
iar ( x 2 , y 2 ) descriu vârful celei de a doua parabole. Pe lângă clasa Parabola, se va mai
defini o clasă MainApp care va exemplifica utilizarea metodelor clasei Parabola.

2. Să se scrie un program care citeşte dintr-un fişier text informaţii referitoare la


produsele dintr-un magazin. Pe fiecare linie se află: denumirea produsului, preţul (număr real)
şi cantitatea (număr real). Cele trei elemente sunt separate prin caracterul ";" (zahăr;1.5;50).
Programul va afişa, sub forma unui tabel, conţinutul fişierului şi va determina denumirea,
preţul şi cantitatea produsului cu preţ minim, respectiv maxim pe care le va scrie într-un fişier
destinaţie. Programul va afişa pe ecran produsele pentru care cantitatea este mai mică decât o
valoare citită de la tastatură.
Se va defini o clasă Produs care conţine ca date membru cele trei elemente ce descriu
un produs. Pentru a tipări informaţiile cerute se va prevedea o metodă toString() în clasa
Produs. Variabilele membre din clasa Produs vor fi private iar clasa va fi prevăzută cu cel
puţin un constructor. Pentru fiecare produs din fişier se va crea un obiect de tip Produs.

10
Lucrarea 3

Şiruri de caractere. Compararea obiectelor. Clase înfăşurătoare

Cuprins
Clasa String ..............................................................................................................................................................1
Obiecte String şi tablouri de caractere......................................................................................................................2
Compararea obiectelor..............................................................................................................................................2
Compararea șirurilor de carcatere.............................................................................................................................3
Conversii între tipul String şi tipurile primitive........................................................................................................4
Clase înfăşurătoare ...................................................................................................................................................5
Temă.........................................................................................................................................................................8

Clasa String
Clasa String este una dintre clasele predefinite, fundamentale, care modelează lucrul
cu şiruri de caractere. Modul cel mai simplu de manipulare a obiectelor de tip String îl
constituie constantele String, care sunt şiruri de caractere încadrate de ghilimele. Orice
referire a unei asemenea constante în cadrul unui program Java are ca efect crearea unui
obiect String care conţine secvenţa de caractere respectivă.

Clasa String are mai mulţi constructori:


 un constructor care are ca parametru o referinţă la String. Noul obiect creat va avea
acelaşi conţinut ca şi obiectul indicat de parametru:

String s1 = "un sir";


String s2 = new String(s1);

 un constructor no-arg, care crează un şir vid:

String svid = new String( );

 un constructor care are ca parametru un tablou cu elemente de tip char:

char[ ] tc = {'a','b','c'};
String stc = new String(tc);

Clasa String oferă un set de metode pentru prelucrarea unor şiruri existente:

String toUpperCase();
String toLowerCase();
String substring(int);
String substring(int, int);

Metodele clasei String includ şi operaţii care permit consultarea şirurilor de caractere,
respectiv, obţinerea de informaţii despre conţinutul lor:

int length();
Programare Java, Laborator 3

char charAt(int);
int indexOf(char);
int indexOf(String);
int lastIndexOf(char);
int lastIndexOf(String);

O proprietate foarte importantă a metodelor clasei String este aceea că ele nu modifică
NICIODATĂ obiectul receptor, ci creează obiecte noi în care este inclus şirul de caractere
modificat. În Java, operaţia de concatenare se realizează cu ajutorul operatorului „+“ . Va fi
creat unu obiect nou, al cărui conţinut va fi şirul rezultat prin concatenare.

String s1 = "codul ";


String s2 = "penal";
String s3 = s1 + s2;

Operaţia de concatenare suportă următoarele precizări:


 clasa String este singura asupra căreia se poate aplica operatorul „+“. Tipurile
primitive numerice şi tipul char mai au definit acest operator (cu altă semnificaţie);
 într-o expresie de concatenare pot să apară şi valori ale tipurilor primitive,acestea fiind
automat convertite la String;

String s2 = "x = " +8.5;

 într-o expresie de concatenare pot să apară ca operanzi şi obiecte ale altor clase decât
String, cu condiţia ca în clasele respective să fie definită metoda toString care să
genereze obiecte String.

Obiecte String şi tablouri de caractere


Deşi, aparent, un şir de caractere şi un tablou de caractere (tipul char[]) modelează
acelaşi lucru (o secvenţă de caractere), cele două tipuri se comportă diferit. Unui obiect String
nu i se poate modifica conţinutul, unui tablou de caractere i se poate modifica oricare element.
Pentru a obţine caracterul aflat la o anumită poziţie pos într-un String, s, nu putem folosi
notaţia s[pos] ca pentru tablouri, ci trebuie utilizată metoda charAt(pos). Metoda
toCharArray() creează un tablou ale cărui elemente sunt caracterele şirului de caractere sursă.
Operaţia inversă se realizează cu ajutorul unui constructor al clasei String.

Compararea obiectelor
Una dintre operaţiile cele mai frecvente de comparare a obiectelor este testarea
egalităţii. În Java această operaţie poate avea două sensuri:
 testarea identităţii: în acest caz ceea ce se compară sunt două referinţe de obiect, iar
testul presupune a verifica dacă cele două referinţe indică unul şi acelaşi obiect.
Testarea identităţii se realizează cu ajutorul operatorului ==

 testarea echivalenţei: în acest caz operanzii sunt două obiecte propriu-zise şi testul
presupune a verifica dacă cele două obiecte au conţinut asemănător. De regulă, pentru
testarea echivalentei se utilizează o metodă denumită equals(), operanzii fiind un

2
Şiruri de caractere. Compararea obiectelor. Clase înfăşurătoare

obiect receptor şi unul dat ca parametru metodei. Această metodă este definită în clasa
Object şi este moştenită de toate clasele.

Definiţia metodei equals(), aşa cum apare ea în clasa Object, pentru majoritatea
claselor utilizatorului nu este satisfăcătoare. Ca urmare, programatorul va trebui să-şi
definească propriile variante de equals(), acolo unde este cazul. Pentru clasa Punct, o posibilă
implementare a metodei equals() ar fi:

class Punct{
private int x,y;
public Punct(int x, int y){
this.x = x;
this.y = y;
}
public boolean equals(Punct p) {
return ((x==p.x) && (y==p.y));
}
}
class ClientPunct {
public static void main(String []args ) {
Punct p1 = new Punct(1,2);
Punct p2 = new Punct(1,2);

Punct p3 = p1;
boolean t1 = (p1==p2); //test de identitate; rezultat: false
boolean t2 = p1.equals(p2); //test de echivalenta; rezultat: true
boolean t3 = (p1==p3); //test de identitate; rezultat: true

System.out.println(t1);
System.out.println(t2);
System.out.println(t3);.
}
}

Compararea șirurilor de caractere


Regulile prezentate mai sus se aplică şi asupra clasei String. Ca atare, se va utiliza
operatorul == pentru a testa identitatea obiectelor String şi metoda equals() pentru a testa
egalitatea şirurilor de caractere conţinute de obiectele String.

class oClasa {
public void oMetoda( ) {
String s1 = new String("ababu");
String s2 = new String("ababu");
String s3 = s1;
boolean t1 = (s1==s2); //test de identitate; rezultat: false
boolean t2 = s1.equals(s2); //test de echivalenta; rezultat: true

3
Programare Java, Laborator 3

boolean t3 = (s1==s3); //test de identitate; rezultat: true

String s4 = "ababu";
String s5 = "ababu";
boolean t4 = (s4==s5); //test de echivalenta; rezultat: true
//. . .
}
}

Se observă că referinţele s1 şi s2 indică obiecte distincte, deşi ele au fost iniţializate cu


acelaşi şir de caractere. Se observă că dacă nici unul din şirurile de caractere nu este alocat cu
new (şirurile s4 şi s5), testul devine de echivalenţă.
Pe lângă metoda equals(), clasa String mai conţine şi alte metode de comparare:
 metoda equalsIgnoreCase(), care este similară cu equals(), dar la comparare nu
diferenţiază literele mari de cele mici.
 metoda compareTo(), care primeşte ca parametru o referinţă String şi returnează ca
rezultat un număr întreg a cărui valoare este:
o negativă, dacă şirul din obiectul receptor este mai mic decât şirul din obiectul
parametru;
o nulă, dacă cele două şiruri sunt egale;
o pozitivă dacă şirul din obiectul receptor este mai mare decât şirul din obiectul
parametru;
Metoda compareTo() poate fi folosită pentru realizarea sortării alfabetice a şirurilor de
caractere.

Conversii între tipul String şi tipurile primitive


Într-un program Java, conversiile între tipul String şi tipurile primitive sunt foarte
necesare, în primul rând pentru că datele de intrare ale programului sunt receptate în
majoritatea cazurilor sub formă de şiruri de caractere, deoarece în Java nu există ceea ce în C
se numea funcţie de citire formatată (scanf). În ceea ce priveşte conversiile, în Java există o
regulă care spune că întotdeauna tipul spre care se face conversia trebuie să aibă metoda
necesară conversiei. În acest sens, clasa String este dotată cu metode numite valueOf() care
asigură conversiile de la tipurile primitive spre String.

class oClasa {
public void oMetoda( ){
int a = 4;
double b = 13.2;
boolean c = true;
String s1 = String.valueOf(a);
String s2 = String.valueOf(b);
String s3 = String.valueOf(c);
//. . .
}
}

4
Şiruri de caractere. Compararea obiectelor. Clase înfăşurătoare

valueOf() este o metodă statică, supraîncărcată astfel încât să admită ca parametri valori ale
tuturor tipurilor primitive. Efectul metodei este crearea unui obiect String al cărui conţinut
este obţinut prin conversia la şir de caractere a valorii parametrului. Ca rezultat, metoda
returnează o referinţă la obiectul String creat. Pentru a realiza conversiile în sens invers, adică
de la String spre tipurile primitive se utilizează metode ale unor clase speciale, numite clase
înfăşurătoare (wrapper classes).

Clase înfăşurătoare
Tuturor tipurilor primitive din limbajul Java le corespunde câte o clasă înfăşurătoare.
Aceste clase au fost definite din două motive:
 să înglobeze metode de conversie între valori ale tipurilor primitive şi obiecte ale altor
clase, precum şi constante speciale legate de tipurile primare;
 să permită crearea de obiecte care să conţină valori ale tipurilor primare, şi care apoi
să poată fi utilizate în cadrul unor structuri unde nu pot să apară decât obiecte, nu date
simple.

Clasele înfăşurătoare sunt definite în pachetul java.lang şi ele sunt:

Integer pentru int


Short pentru short
Byte pentru byte
Long pentru long
Float pentru float
Double pentru double
Boolean pentru boolean
Character pentru char
Void pentru void

Exceptând clasa Void, restul claselor înfăşurătoare conţin următoarele metode:


 câte un constructor care acceptă ca parametru o valoare a tipului primitiv corespondent.

Integer a = new Integer(5);


Double b = new Double(12.13);

 câte un constructor care acceptă ca parametru o referinţă String (excepţie face clasa
Character care nu are acest constructor). Se presupune că şirul respectiv conţine o
secvenţă compatibilă cu tipul primitiv corespondent.

Integer a = new Integer("5");


Boolean b = new Boolean("true");

 o metodă statică valueOf() care acceptă ca parametru un String şi returnează un obiect


al clasei înfăşurătoare, convertind conţinutul şirului; apelul acestei metode este
echivalent cu a executa un new cu constructorul descris mai înainte.

Integer a = Integer.valueOf("5");
Double b = Double.valueOf("-8.12");

5
Programare Java, Laborator 3

 o metodă toString() care realizează practic conversia de la clasa înfăşurătoare la String

De multe ori este necesar ca datele conţinute de obiecte să fie convertite la şiruri de
caractere în vederea participării obiectelor respective ca operanzi în expresii de manipulare a
şirurilor. De exemplu, funcţiile print() / println() necesită ca parametru o referinţă String.
Convertind un obiect la String, am putea să afişăm acel obiect cu ajutorul funcţiilor
print()/println(). Programatorul trebuie să-si definească în clasele sale câte o metodă
toString(), dacă doreşte să convertească obiectele respective la şiruri.

class Punct {
//. . .
public String toString( ) {
String s = "("+String.valueOf(x)+","+String.valueOf(y)+")";
return s;
}
}

 o metodă typeValue(), unde type este numele tipului primitiv corespondent; această
metodă returnează conţinutul obiectului ca valoare a tipului primitiv.

Integer a = new Integer("5");


int x = a.intValue( );
Double b = new Double("3.14");
double y = b.doubleValue( );

 o metodă equals() care realizează testul echivalenţei obiectelor.


 o metodă compareTo() care acceptă ca parametru un obiect al aceleiaşi clase
înfăşurătoare ca şi obiectul receptor. Metoda returnează o valoare de tip int pozitivă,
nulă sau negativă după cum conţinutul obiectului receptor este mai mare, egal sau mai
mic decât cel al parametrului.

Integer a = new Integer(7);


Integer b = new Integer(10);
int x = b.compareTo(a); //val. x va fi pozitivă

Pentru conversiile de la String la tipurile primitive se utilizează metode statice ale


claselor înfăşurătoare, si anume:
 în cazul claselor care reprezintă tipuri numerice există metodele parseType(), unde
Type este numele tipului primitiv corespondent scris cu prima literă majusculă;
metoda acceptă ca parametru şirul care trebuie convertit şi returnează valoarea
corespunzătoare, în reprezentarea tipului primitiv respectiv;
 în cazul tipului boolean exista metoda getBoolean() care funcţionează similar metodei
parseType().

În cazul tipurilor numerice, dacă şirul care se converteşte nu conţine o secvenţă în


concordanţă cu tipul primitiv spre care se face conversia, programul va fi întrerupt cu un
mesaj de eroare corespunzător (excepţie NumberFormatException).

6
Şiruri de caractere. Compararea obiectelor. Clase înfăşurătoare

În continuare, se va prezenta un program în care va fi ilustrat conceptul de


introducere interactivă de date, care presupune că utilizatorul trebuie să introducă anumite
date de la tastatură, Pentru aceasta, este necesar ca înaintea operaţiilor de citire, în program să
existe operaţii de afişare a unor mesaje de genul: „Introduceţi denumirea:“ sau „Daţi
cantitatea:“. Programul considerat realizează completarea unui tablou de numere întregi,
citind numerele de la tastatură, apoi afişează pe ecran tabloul ordonat crescător.

import java.io.*;

public class Ordonare{


public static void main (String [] args) throws IOException{
BufferedReader flux_in=new BufferedReader (new
InputStreamReader(System.in));
int[] tab = new int[10];
int i, aux;

String linie;
boolean sw = true;

for(i=0;i<tab.length;i++){
System.out.println("Dati elementul de pe pozitia "+(i+1));
linie=flux_in.readLine();
tab[i]=Integer.parseInt(linie);
}

while(sw){
sw=false;
for(i=o;i<tab.length-1;i++)
if(tab[i]>tab[i+1]){
aux=tab[i];
tab[i]=tab[i+1];
tab[i+1]=aux;
sw=true;
}
}
System.out.println("Tabloul ordonat este:");
for(i=0;i<tab.length;i++)
System.out.println(tab[i]+" ");
}
}

7
Programare Java, Laborator 3

Temă
1. Fişierul cantec_in.txt conţine versurile unui cântec la alegere. Să se scrie un
program care creează fişierul cantec_out.txt, care conţine versurile cântecului original dar în
plus în dreptul fiecărui rând sunt afişate numărul de cuvinte de pe rând şi numărul de vocale
de pe fiecare rând. În dreptul rândurilor care se încheie cu o grupare de litere aleasă se va
pune o steluţă (*). Rândurile pentru care un număr generat aleator este mai mic decât 0.1 vor
fi scrise cu majuscule (se vor genera aleator numere între 0 şi 1). Se va defini o clasă Vers,
care are o variabilă membră privată un șir de caractere reprezentând versul și se va dezvolta
câte un operator pentru fiecare cerință de mai sus (o metodă care returnează numărul de
cuvinte, o metodă care returnează numărul de vocale, etc).

2. Să se insereze într-o anumită poziție a unui șir de caractere, un alt șir. Datele vor fi
preluate de la tastatură sau din fișier. Să se șteargă o porțiune a unui șir de caractere care
începe dintr-o anumită poziție și are un anumit număr de caractere. Se recomandă utilizarea
clasei StringBuilder.

3. Fișierul judete_in.txt, conține lista neordonată a județelor din țară. Să se încarce


datele din fișier într-un tablou de String-uri și să se ordoneze acest tablou cu ajutorul metodei
sort() din clasa Arrays. Să se returneze pe ce poziție se află în vectorul oronat un județ
introdus de la tastatură. Se va utiliza metoda de căutare binară din clasa Arrays.

4. Fişierul in.txt conţine o listă cu melodiile preferate de un student. Pentru fiecare


melodie se reţine: nume_melodie; nume_artist; an_lansare; numar_vizualizări_youtube. Să
se realizeze un program care:
a) încarcă informaţiile despre melodii în program, într-un vector de obiecte. Se va crea
clasa Melodie cu variabile membre private, constructor cu parametri, gettere pentru
accesul variabilelor membre
b) afişează informaţiile melodiilor încărcate
c) afişează clasamentul melodiilor în ordine descrescătoare al numărului de vizualizări pe
youtube
d) afişează toate melodiile unui artist

8
Lucrarea 4

Moştenire şi polimorfism

Cuprins
Relaţia de moştenire în Java .....................................................................................................................................1
Reguli de vizibilitate în contextul relaţiei de moştenire ...........................................................................................2
Constructorii şi moştenirea........................................................................................................................................4
Operatorul instanceof ...............................................................................................................................................4
Redefinirea metodelor ..............................................................................................................................................5
Legarea dinamică şi constructorii.............................................................................................................................7
Temă.........................................................................................................................................................................8

Tehnologia orientată pe obiecte permite extinderea comportamentului unei clase


existente prin definirea unei clase noi, care moşteneşte conţinutul primei clase, adăugând la
acesta elementele specifice ei. Clasa moştenită se numeşte clasă de bază sau superclasă, iar
clasa care realizează extinderea se numeşte subclasă, clasă derivată, sau clasă descendentă.
Relaţia de moştenire este o relaţie de forma “este un fel de”, adică “subclasa este un fel de
superclasă”. Practic, relaţia de moştenire între clase este o reflectare a ierarhizării existente
între entităţile modelate de clasele respective.
Relaţia de moştenire prezintă două aspecte esenţiale:
• reutilizare de cod
• polimorfism.

Relaţia de moştenire în Java


În Java, relaţia de moştenire dintre o superclasă şi o subclasă se exprimă astfel:

class nume_subclasa extends nume_superclasa


{
//conţinut specific subclasei
}

Se consideră clasa Poligon şi o clasă Dreptunghi care moşteneşte clasa Poligon:

class Poligon {
protected double[ ] laturi;
public Poligon(int n) { laturi = new double[n] }
public double perimetru( ) {
double s=0;
for(int i=0;i<laturi.length;i++) s+=laturi[i];
return s;
}
}
class Dreptunghi extends Poligon {
public Dreptunghi(double L, double h){
super(4);
laturi[0] = laturi[2] = L;
Programare Java, Laborator 4

laturi[1] = laturi[3] = h;
}
public double aria( ){
return laturi[0]*laturi[1];
}
}

Clasa Dreptunghi reutilizează codul definit în clasa Poligon, la care adaugă elemente
proprii. Pe lângă Dreptunghi, am putea defini şi alte clase, cum ar fi Triunghi, Patrat,
Pentagon etc. care să moştenească Poligon. Superclasa este factorul comun al subclaselor sale
sau, altfel spus, codul factorului comun este reutilizat în subclase.
Despre relaţia de moştenire în Java se pot spune următoarele:
• o subclasă poate extinde o singură superclasă (moştenire simplă)
• o superclasă poate fi moştenită de mai multe subclase distincte
• o subclasă poate fi superclasă pentru alte clase
• o clasă de bază împreună cu toate descendentele ei formează o ierarhie de clase.

Reguli de vizibilitate în contextul relaţiei de moştenire

Regulile de vizibilitate vor fi discutate din două perspective:


• accesul funcţiilor unei subclase la membrii moşteniţi de la superclasele ei
• accesul clienţilor unei subclase la membrii ei, moşteniţi sau specifici

Accesul funcţiilor unei subclase la membrii moşteniţi:

Pentru a ilustra drepturile de acces vom lua următorul exemplu:

class SuperClasa {
private int priv;
protected int prot;
public int pub;
}
class SubClasa extends SuperClasa {
private int priv_local;
protected int prot_local;
public int pub_local;
public void metoda(SubClasa p ) {
priv = 1; //eroare
prot = 2; //corect
pub = 3; //corect
priv_local = 4; //corect
prot_local = 5; //corect
pub_local = 6; //corect
p.priv = 1; //eroare
p.prot = 2; //corect
p.pub = 3; //corect
p.priv_local = 4; //corect

2
Moştenire şi polimorfism

p.prot_local = 5; //corect
p.pub_local = 6; //corect
}
}

În metodele specifice ale unei subclase pot fi referiţi acei membri moşteniţi, care în
superclasă au fost precedaţi de modificatorii protected sau public. Membrii moşteniţi, care în
superclasă sunt precedaţi de modificatorul private, deşi fizic fac parte din subclasă, nu sunt
accesibili în metodele acesteia. Referirea membrilor moşteniţi se face direct, folosind numele
lor, la fel ca referirea membrilor specifici.

Acest lucru este foarte normal, deoarece şi membrii moşteniţi sunt conţinuţi în
subclasă, numai că ei nu au fost declaraţi explicit. Includerea lor se face automat, ca efect al
clauzei extends. Acest lucru nu este valabil şi pentru constructori. Revenind la exemplul cu
clasele Poligon — Dreptunghi, trebuie spus că un obiect al clasei Dreptunghi conţine doi
constructori: unul moştenit, responsabil cu iniţializarea porţiunii moştenite, şi unul local,
responsabil cu iniţializarea porţiunii specifice subclasei. Referirea în subclasă la constructorul
moştenit se face folosind cuvântul cheie super, aşa cum se observă în constructorul clasei
Dreptunghi.

Cuvântul super este un omolog al lui this şi reprezintă referinţa la partea moştenită a
unui obiect. În afară de apelul constructorului moştenit, cuvântul super se va utiliza pentru a
distinge între un membru local al subclasei şi un membru moştenit, ambii cu acelaşi nume.

Accesul clienţilor unei subclase la membrii acesteia


Exemplului din paragraful anterior i se adaugă secvenţa următoare:

class Client {
public void oFunctie( ) {
SuperClasa sp = new SuperClasa();
SubClasa sb = new SubClasa();
sp.priv = 1; //eroare
sp.prot = 2; //corect, doar dacă Client şi SuperClasa s-ar afla in acelaşi pachet
sp.pub = 3; //corect
sb.priv = 1; //eroare
sb.prot = 2; //corect, doar dacă Client şi SubClasa s-ar afla în acelaşi pachet
sb.pub = 3; //corect
sb.priv_local = 4; //eroare
sb.prot_local = 5; //corect, doar dacă Client şi SubClasa s-ar afla în acelaşi pachet
sb.pub_local = 6; //corect
}
}

În clienţii unei subclase pot fi referiţi acei membri, moşteniţi sau locali, care sunt
precedaţi de modificatorul public. Membrii declaraţi ca protected sunt accesibili numai în
situaţia în care clientul se află în acelaşi pachet cu subclasa respectivă. La referirea membrilor
unei subclase de către client, nu se face deosebire între membrii moşteniţi şi cei locali.

3
Programare Java, Laborator 4

Constructorii şi moştenirea
În exemplul anterior se observă că cele două clase nu au constructori expliciţi,
constructorul SubClasa() fiind de forma:

public SubClasa() { super( ); }

Constructorul subclasei apelează constructorul no-arg al superclasei. Dacă pentru


SuperClasa am fi definit un constructor no-arg explicit, atunci acesta ar fi fost cel apelat de
constructorul SubClasa. Dacă ar fi existat un constructor oarecare în SubClasa, iar acesta nu
ar fi apelat explicit vreun constructor din SuperClasa, în mod automat se încearcă apelul
constructorului no-arg super(), înainte de a se executa instrucţiunile din constructorul
SubClasa:

public SubClasa(. . .) {
super(); //apel implicit
//alte instrucţiuni prevăzute de programator
}

Dacă SuperClasa nu are constructor no-arg, apelul implicit de mai sus ar genera
eroare. Prin urmare, dacă programatorul defineşte constructori expliciţi într-o subclasă, el
trebuie să aibă grijă ca aceşti constructori să apeleze explicit constructorii adecvaţi ai
superclasei:

super (parametri-actuali)

Un asemenea apel trebuie să fie prima instrucţiune din constructorul subclasei.


Constructorul unei subclase se defineşte astfel încât lista lui de parametri să se compună dintr-
un set de parametri necesari iniţializării câmpurilor moştenite (transmis constructorului super)
şi un alt set, necesar iniţializării câmpurilor locale.

Execuţia constructorului se desfăşoară în 3 etape:


• apelul constructorului superclasei
• iniţializarea câmpurilor cu valori date la declarare şi execuţia blocurilor de iniţializare,
unde este cazul
• execuţia corpului propriu-zis al constructorului.

Operatorul instanceof
Prin intermediul unei referinţe de superclasă putem indica şi utiliza şi obiecte ale
subclaselor ei. Operatorul instanceof este util atunci când trebuie să cunoaştem clasa concretă
de care aparţine un obiect referit prin intermediul unei referinţe a unui supertip. Aplicarea
operatorului se face printr-o expresie de forma:

referinta_obiect instanceof nume_clasa

4
Moştenire şi polimorfism

Redefinirea metodelor
Relaţia de moştenire poate fi utilizată atunci când o anumită clasă (subclasă) extinde
comportamentul altei clase (superclase), în sensul că superclasa înglobează aspectele comune
ale unei ierarhii de abstracţiuni, iar subclasele adaugă la această parte comună funcţiuni
specifice. De asemenea, o referinţă la superclasă poate indica şi manipula obiecte ale oricărei
clase descendente din ea. Manipularea se referă la faptul că, prin intermediul acelei referinţe
se pot apela metodele definite în superclasă, indiferent dacă obiectul concret aparţine
superclasei sau uneia dintre subclase. Această facilitate constituie aşa-numitul polimorfism
parţial. Folosind o referinţă a superclasei nu se poate, însă, apela o metodă locală a subclasei,
decât dacă se realizează o conversie explicită (casting) a referinţei, la tipul subclasei. În Java
relaţia de moştenire poate fi aplicată şi în cazul în care se doreşte ca, pe lângă extinderea
comportamentului, să realizăm şi o adaptare (specializare) a lui, în sensul că unele metode
care în superclasă au o anumită implementare, în subclase să aibă o altă implementare,
adaptată la cerinţele locale ale subclasei.

package exemplu1;

class Angajat {
protected String nume;
protected double sal_ora;

public Angajat(String nume, double sal_ora) {


this.nume = nume;
this.sal_ora = sal_ora;
}

public double calcSalar(int nr_ore) {


return (sal_ora * nr_ore);
}

public String toString() {


return nume;
}
}
class Sef extends Angajat {
private double proc_cond; // procent de indemnizatie conducere

public Sef(String nume, double sal_ora, double proc_cond) {


super(nume, sal_ora);
this.proc_cond = proc_cond;
}

public double calcSalar(int nr_ore) {


return ((1 + proc_cond / 100) * sal_ora * nr_ore);
}
}

5
Programare Java, Laborator 4

class MainApp {
public static void main(String[] args) {
Angajat a1 = new Angajat("Artaxerse Coviltir", 100);
Angajat a2 = new Sef("Al Bundy", 200, 33.2);
final int ore_luna = 160;

System.out.println("Salar " + a1 + "=" + a1.calcSalar(ore_luna));


System.out.println("Salar " + a2 + "=" + a2.calcSalar(ore_luna));
}
// . . .
}

Se observă că aceeaşi metodă, calcSalar() apare atât în clasa Angajat, cât şi în clasa
Sef, dar cu implementări diferite. Atunci când într-o subclasă apare o metodă având
semnătura identică cu o metoda din superclasă, se spune că metoda din subclasă o
redefineşte pe omonima ei din superclasa. În acest caz, un obiect al subclasei practic are două
exemplare ale metodei respective, unul moştenit şi unul propriu. Ce se întâmplă la nivelul
clienţilor ierarhiei? Se observă că în metoda main() din clasa MainApp se folosesc două
referinţe la Angajat: una indică un obiect al clasei Angajat, iar cealaltă un obiect al clasei Sef.
Prin intermediul celor două referinţe se apelează metoda calcSalar(). Se pune problema care
dintre cele două forme ale metodei se apelează efectiv? În Java, regula care se aplică în
asemenea cazuri este următoarea: Se va executa întotdeauna acel exemplar de metodă care
este definit în clasa la care aparţine obiectul concret indicat de referinţa utilizată.

Cu alte cuvinte, clasa obiectului „dictează“ şi nu tipul referinţei. Astfel, deşi ambele
referinţe a1 şi a2 din exemplul considerat sunt de tip Angajat, ele indică, de fapt, obiecte ale
unor clase diferite. Deci, apelul a1.calcSalar(...) va declanşa execuţia metodei calcSalar()
definită în clasa Angajat, iar apelul a2.calcSalar(...) va declanşa execuţia metodei calcSalar()
definită în clasa Sef. Polimorfismul permite interschimbarea obiectelor indicate de o
referinţă a superclasei, într-o manieră transparentă pentru clienţi.

În Java, clasa concretă la care aparţine un obiect indicat de o referinţă se cunoaşte,


însă, abia la execuţia programului. Deci, un apel de metodă nu este „rezolvat“ (adică pus în
corespondenţă cu o implementare anume) la compilare, ci doar la execuţie. Acest procedeu se
numeşte legare dinamică sau amânată (late binding). S-a spus mai sus că un obiect al clasei
Sef „posedă“ două metode calcSalar(): cea moştenită de la Angajat şi cea locală. Din cauză că
în Java se aplică legarea dinamică, înseamnă că, din perspectiva clienţilor clasei Sef varianta
locală o „eclipsează“ pe cea moştenită, neexistând posibilitatea ca un client să forţeze apelul
metodei moştenite (decât aplicând o soluţie „ocolitoare“, adică folosind o metodă cu alt nume
care să apeleze metoda calcSalar() moştenită).

În interiorul subclasei se poate realiza distincţia între cele două variante ale unei
metode redefinite, şi anume folosind simbolul super. Astfel, metoda calcSalar() din clasa Sef
se poate folosi de omonima ei din clasa Angajat:

6
Moştenire şi polimorfism

class Sef extends Angajat {


//. . .
public double calcSalar(int nr_ore) {
return ((1 + proc_cond / 100) * super.calcSalar(nr_ore));
}
}

Practic, aceasta este singura situaţie în care nu se aplică legarea dinamică, ci apelul de
forma super.numeMetoda(...) este pus în corespondenţă exact cu implementarea din
superclasa clasei curente.

Legarea dinamică şi constructorii


Deoarece în Java la apelul metodelor non-statice se aplică legarea dinamică, pe de o
parte, şi ţinând cont de modul în care se apelează constructorii într-o ierarhie de clase, pe de
altă parte, trebuie să avem grijă cum proiectăm constructorii dacă aceştia apelează la rândul
lor metode ale claselor respective.

package exemplu2;

class SuperClasa {
protected int a;
protected int rez;
private int x;

public SuperClasa() {
a = 1;
rez = 2;
x = calcul(); //x=0
}

public int calcul() {


return (rez + a);
}

@Override
public String toString() {
return "SuperClasa [a=" + a + ", rez=" + rez + ", x=" + x + "]";
}

class SubClasa extends SuperClasa {


protected int b;
private int y;

7
Programare Java, Laborator 4

public SubClasa() {
b = 3;
y = calcul();
}

public int calcul() {//asta 2*0 si apoi 2 *3


return (rez * b);
}

@Override
public String toString() {
return super.toString()+" SubClasa [b=" + b + ", y=" + y + "]";
}
}

public class MainApp {


public static void main(String[] args) {
SubClasa ob = new SubClasa();
System.out.println(ob);
}
}
//Output: SuperClasa [a=1, rez=2, x=0] SubClasa [b=3, y=6]

Să urmărim ce se întâmplă la crearea unui obiect al clasei Subclasa: ţinând cont de


ordinea în care au loc iniţializările câmpurilor unui obiect, înseamnă că se execută următorii
paşi:
• câmpurile a, rez, x, b şi y se iniţializează cu valorile implicite corespunzătoare
tipurilor lor, deci, în cazul nostru cu 0;
• se lansează constructorul SubClasa(); ca primă acţiune a acestuia are loc apelul
constructorului no-arg SuperClasa();
• în constructorul SuperClasa() se execută iniţializările lui a şi rez cu 1, respectiv 2,
după care se apelează metoda calcul(). În acest moment, apelul se leagă de varianta
metodei calcul() definită în clasa SubClasa, de care aparţine obiectul în curs de
iniţializare. Efectul este că x se va iniţializă cu valoarea 2 * 0, adică cu 0, deoarece la
momentul respectiv câmpul b încă nu a apucat să primească o altă valoare;
• se revine în constructorul SubClasa(), se iniţializează b cu 3 şi y cu 2*3= 6, cât
returnează metoda calcul() din SubClasa.

Dacă în constructorul unei superclase se apelează o metodă care este redefinită în


subclasă, la crearea unui obiect al subclasei există riscul ca metoda respectivă să refere
câmpuri neiniţializate încă la momentul apelului.

8
Moştenire şi polimorfism

Temă

1. Problema propusă încearcă să dea o mână de ajutor în gestionarea produselor unei firme
care comercializează echipamente electronice. Fiecare echipament este înregistrat cu o
denumire, un număr de inventar nr_inv, are un preţ pret şi este plasat într-o anumită zonă din
magazie zona_mag. Orice echipament poate fi într-una din situaţiile:
• achiziţionat (intrat în magazie);
• expus (expus în magazin);
• vândut (transportat şi instalat la client).
Firma comercializează mai multe tipuri de echipamente. Toate echipamentele care
folosesc hârtia drept consumabil sunt caracterizate de numărul de pagini scrise pe minut ppm.
Imprimantele sunt caracterizate de rezoluţie (număr de puncte per inch dpi) şi număr de
pagini/cartuş p_car. Unei imprimante i se poate seta modul de scriere:
• tipărireColor (selectere a modului color de tipărire);
• tipărireAlbNegru (selectere a modului alb-negru de tipărire).
Copiatoarele sunt caracterizate de numărul de pagini/toner p_ton. Se poate seta formatul
de copiere:
• setFormatA4 (setare a formatului A4);
• setFormatA3 (setare a formatului A3).

Sistemele de calcul au un monitor de un anumit tip tip_mon, un procesor de o anumită viteză


vit_proc, o capacitate a HDD c_hdd şi li se poate instala unul din sistemele de operare:
• instalWin (instalarea unei variante de Windows);
• instalLinux (instalarea unei variante de Linux).

Metodele de mai sus vor seta parametrii corespunzători.


Să se realizeze ierarhia de clase corespunzătoare modelului prezentat; Să se creeze UN
SINGUR VECTOR DE OBIECTE în care să fie preluate datele din fişierul de intrare
electronice.txt. Se va dezvolta un meniu care va oferi următoarele facilităţi:
• Afişarea imprimantelor
• Afişarea copiatoarelor
• Afişarea sistemelor de calcul
• Modificarea stării în care se află un echipament
• Setarea unui anumit mod de scriere pentru o imprimantă
• Setarea unui format de copiere pentru copiatoare
• Instalarea unui anumit sistem de operare pe un sistem de calcul
• Afişarea echipamentelor vândute
• Să se realizeze două metode statice pentru serializarea / deserializarea colecției de
obiecte în fișierul echip.bin

9
Lucrarea 5

Interfeţe

Cuprins
Interfeţe Java............................................................................................................................................................1
Declararea unei interfeţe în Java...............................................................................................................................1
Utilizarea interfeţelor Java........................................................................................................................................2
Exemplu de lucru cu interfeţe Java...........................................................................................................................3
Asocierea operaţiilor cu obiectele............................................................................................................................4
Temă.........................................................................................................................................................................5

Un obiect include date şi metode (operaţii) care permit modificarea datelor. Obiectul
execută o operaţie atunci când primeşte o cerere (un mesaj) de la un client. Mesajele
reprezintă singura cale prin care un obiect este determinat să execute o operaţie, iar operaţiile
sunt singurul mod de a modifica datele interne ale obiectului. Din cauza acestor restricţii,
starea internă a obiectului se spune că este încapsulată: ea nu poate fi accesată direct, iar
reprezentarea ei este invizibilă dinspre exteriorul obiectului. Pentru fiecare operaţie declarată
într-un obiect se precizează numele, parametrii şi valoarea returnată. Aceste elemente
formează semnătura operaţiei.
Mulţimea tuturor semnăturilor corespunzătoare operaţiilor dintr-un obiect, accesibile
clienţilor, reprezintă interfaţa obiectului. Aceasta descrie complet setul mesajelor care pot fi
trimise spre obiectul respectiv. Un tip este un nume utilizat pentru a referi o anumită interfaţă.
Un obiect este de tipul T dacă el acceptă toate mesajele corespunzătoare operaţiilor definite în
interfaţa numită T.
Interfeţele sunt elemente fundamentale în sistemele OO. Obiectele sunt cunoscute doar
prin intermediul interfeţelor lor. O interfaţa nu dă nici un detaliu relativ la implementarea unui
obiect, iar obiecte distincte pot implementa diferit o aceeaşi cerere.

Interfeţe Java
Interfeţele Java reprezintă o colecţie de metode abstracte (doar semnătura, nu şi
implementarea acestora) şi de constante. Utilizarea unei interfeţe este justificată în cazurile în
care se doreşte ca o clasă să poată fi convertită în mai multe tipuri de bază între care nu există
relaţie de moştenire. Deoarece Java nu suportă moştenirea multiplă, interfeţele reprezintă o
variantă de rezolvare a acestei situaţii.
La fel ca şi o clasă, înainte de a putea utiliza o interfaţă ea trebuie declarată. Deoarece
conţine metode abstracte, interfaţa nu poate fi instanţiată. Cu toate acestea, o variabilă poate fi
declarată ca fiind de tipul interfeţei.

Declararea unei interfeţe în Java


O interfaţă Java este declarată prin cuvântul cheie interface. Structura generală a
declaraţiei unei interfeţe este:

[modificator] interface id_interfata [extends lista_de_interfete]{


listă_de_constante;
lista_de_metode;
}
Programare Java, Laborator 5

Modificatorul unei interfeţe poate fi public sau abstract. Având în vedere faptul că o
interfaţă este abstractă prin definiţie, utilizarea modificatorului abstract este redundantă. Spre
deosebire de o clasă, o interfaţă poate moşteni mai multe interfeţe. Moştenirea se face prin
clauza extends urmată de lista interfeţelor moştenite (lista_de_interfete) separate prin virgulă.
Prin derivare, o interfaţă moşteneşte de la interfaţa de bază atât metodele, cât şi constantele
acesteia.

public interface Id1 extends Id2, Id3, Id4

În corpul interfeţei pot exista o listă de declaraţii de metode şi o listă de declaraţii de


constante. Metodele declarate într-o interfaţă sunt implicit abstracte. Constantele(variabilele
membre) declarate în interfaţă primesc atributele static şi final. Unui câmp final, după ce a
fost iniţializat, nu i se mai poate modifica valoarea.

public interface Alergator{


int lg_pista=1000;
int timp_max=3;
public void alearga();
public void accelerează();
}

Metodele dintr-o interfaţă nu pot fi statice. Referirea constantelor se va face astfel:

Alergator.lg_pista;

În urma compilării unui fişier sursă Java care conţine o interfaţă, va rezulta un fişier cu
extensia .class ataşat interfeţei respective.

Utilizarea interfeţelor Java


Interfeţele Java pot fi utilizate în declaraţia unei clase, obligând clasa respectivă să
implementeze metodele declarate în interfaţă. În Java, acest lucru e realizat prin folosirea
cuvântului cheie implements:

class Sportiv extends Om implements Alergator, Saritor{



//implementarea metodelor din interfaţa Alergator;
//implementarea metodelor din interfaţa Saritor;
…\
}

Aşa cum se poate observa, o clasă poate implementa mai multe interfeţe. Dacă o clasă
care implementează o interfaţă nu implementează toate metodele declarate în interfaţa
respectivă, va trebui să fie declarată abstractă, altfel se va semnala eroare la compilare. Dacă
o clasă A implementează o interfaţă X, un obiect al clasei A este de tip X, el putând să fie
atribuit unui alt obiect de tip X. Numele unei interfeţe poate fi utilizat la fel ca şi numele unui
clase, mai puţin la instanţierea prin operatorul new.

2
Interfeţe

Exemplu de lucru cu interfeţe Java


Pentru a mai bună înţelegere a conceptelor prezentate în lucrare, vom recurge, în
continuare, la un exemplu.

interface Cititor {
public void citeste();
}
interface Scriitor{
public void scrie();
}

interface Profesor extends Cititor, Scriitor{


int nota_max=10;
int nota_medie=5;
int nota_min=1;
public void apreciere();
}

interface Scufundator{
int h=10;
public void scufundare();
}

class Inotator implements Scufundator{


static final int l=50;
public void inot(){
System.out.println("Inoata " + l + "metri ");
}
public void scufundare(){
System.out.println("Scufunda " + h + "metri ");
}
}

class ProfesorInot extends Inotator implements Profesor{


public void citeste(){
System.out.println("Citeste revista \"Aventuri la pescuit\" ");
}

public void scrie(){


System.out.println("Nu scrie nimic!");
}
public void apreciere()
{
System.out.println("Slab. Primesti nota " +nota_medie);
}

3
Programare Java, Laborator 5

class ProfesorJava implements Profesor{


public void citeste(){
System.out.println("Citeste Thinking in Java");
}
public void scrie(){
System.out.println("Scrie programe Java");
}
public void apreciere(){
System.out.println("Excelent. Primesti nota " +nota_max);
}
}
public class TestInterfata{
public static void main(String [] args){
ProfesorInot pi = new ProfesorInot();
ProfesorJava pj = new ProfesorJava();
ceCiteste(pi); ceScrie(pi);
ceCiteste(pj); ceScrie(pj);
cumApreciaza(pi); cumApreciaza(pj);
pi.inot(); pi.scufundare(); //corect
pj.inot(); pj.scufundare(); //incorect
}
public static void ceCiteste( Cititor c){
c.citeste();
}
public static void ceScrie( Scriitor s){
s.scrie();
}
public static void cumApreciaza( Profesor p){
p.apreciere();
}
}

Folosind referinţa de interfaţă se pot apela metodele enumerate în interfaţă, iar ceea ce
se execută va fi codul corespunzător clasei "implementatoare", la care aparţine obiectul
concret indicat de referinţă.

Asocierea operaţiilor cu obiectele


Când o cerere este trimisă unui obiect, operaţia care se va executa depinde de:
• cerere;
• obiectul care recepţionează cererea.

Obiecte diferite, care pot recepţiona cereri identice, pot avea implementări diferite ale
operaţiilor care vor satisface cererile respective. Practic, prin cerere se identifică serviciul

4
Interfeţe

dorit, iar prin obiectul receptor se alege o anume implementare a acelui serviciu. Asocierea
dintre o operaţie solicitată şi obiectul care va pune la dispoziţie implementarea concretă a
operaţiei se numeşte legare (binding). În funcţie de momentul în care ea se realizează, legarea
poate fi:
• statică: asocierea se realizează la compilare;
• dinamică: asocierea se realizează la execuţie.

În programele Java, majoritatea apelurilor de metode presupun legare dinamică.


Asocierea dinamică permite scrierea de programe în care:
• la emiterea unei cereri să nu ne preocupe ce obiect o va recepţiona, ştiindu-se că orice
obiect a cărui interfaţă include o semnătură potrivită va fi bun;
• obiecte având interfeţe identice pot fi substituite unul altuia, la execuţie; această
substituire se mai numeşte polimorfism.

Temă

1. Se cere să se modeleze activitatea mai multor ghişee bancare. Sistemul este format
din următoarele entităţi, cu atributele corespunzătoare:

Banca
• denumire_banca (String)
• clienţi (colectie de obiecte de tip List<Client>)

Client
• nume (String)
• adresa (String)
• conturi (colectie de obiecte de tip Set<ContBancar>; un client trebuie să aibă cel
puţin un cont, dar nu mai mult de cinci conturi)

ContBancar
• numarCont (String)
• suma(float)
• moneda (String)
• data_deschiderii (Calendar)
• data_ultimei_operatiuni (Calendar)

Conturile bancare pot fi în RON şi în EUR. Pentru conturile in RON, dobânda se


calculează astfel: 0,3 RON pe zi pentru sume sub 500 RON si 0,8 RON pe zi pentru sume mai
mari.
Conturile in EUR au o dobândă de 0,1 EUR pe zi.

Toate conturile implementează o interfaţa Operatiuni care are metodele:

public float calculeaza_dobanda() – calculează numărul de zile scurs de la data


ultimei operaţiuni asupra contului şi până la data curentă. Numărul de zile obţinut este utilizat
pentru calculul dobânzii după cum este menţionat mai sus
public float actualizare_suma() – adaugă la suma din cont dobânda corespunzătoare
numărului de zile scurs, actualizează data ultimei operaţiuni si returnează suma

5
Programare Java, Laborator 5

public void depunere(float suma) – actualizează suma, data ultimei operaţiuni şi


adună la suma existentă suma depusă
public void extragere (float suma) – actualizează suma, data ultimei operaţiuni şi
scade din suma existentă suma depusă

Toate clasele vor avea constructori şi metode de prelucrare şi de afişare a datelor


membre. Se va redefini metoda toString() în fiecare clasă în aşa fel încât să returneze un
String care reprezintă valorile variabilelor membre din acea clasă.
De asemenea, se va crea o clasă ClientiiBancilor, care conţine programul principal ce
oferă următorul meniu:
1. Adăugare bănci, clienţi, conturi. Băncile vor fi introduse într-o colecţie de obiecte
de tip Vector<Banca> (se va utiliza clasa Vector).
2. Afişare date introduse
3. Depunerea unei sume într-un cont
4. Extragerea unei sume dintr-un cont
5. Transfer de bani între două conturi

Datele pot fi preluate de la tastatură sau din fişier.

2. În fișierul de intrare in.txt se găsesc informații despre cărți, precum în captura de


ecran de mai jos.

Pentru fiecare carte se reține id-ul cărții (număr unic de identificare), titlul cărții,
numele autorului şi anul apariției.

Să se creeze o clasă Carte cu variabile membre titlul, autorul şi anul apariție. Clasa va
dispune de constructor cu parametri, gettere si settere pentru accesarea variabilelor membre şi
va redefini metoda toString() astfel încât aceasta să returneze un String cu valorile variabilelor
membre separate prin virgule.

Să se creeze o colecție de obiecte de tip Map<Integer,Carte> în care se vor adăuga


cărțile citite din fișier. Id-ul cărților reprezintă cheia elementelor din colecția Map, iar
valoarea elementelor din colecție este reprezentată de obiecte de tip Carte. Se va utiliza
implementarea HashMap a interfeței Map. Pentru colecția Map creată să se implementeze
următoarele cerințe:
1. Să se afișeze colecția (se vor afișa atât cheile cât şi valorile).
2. Să se creeze o colecție List<Carte> cu valorile din colecția Map. Să se afișeze
colecția creată.
3. Să se ordoneze elementele din colecția List după titlul cărți şi să se afișeze colecția.

6
Lucrarea 6

Tratarea excepţiilor

Cuprins
Tratarea clasică a excepţiilor ....................................................................................................................................1
Mecanismul de emitere-captare a excepţiilor ...........................................................................................................3
Instrucţiunea throw....................................................................................................................................................4
Clauza throws............................................................................................................................................................5
Care tipuri de excepţie vor apărea într-o clauză throws?..........................................................................................5
Blocurile try-catch.....................................................................................................................................................5
Secvenţa finally ........................................................................................................................................................9
Excepţii predefinite ale limbajului Java .................................................................................................................10
Temă........................................................................................................................................................................11

Pe parcursul execuţiei unui program pot apărea evenimente deosebite care îi modifică
parcursul normal. Asemenea evenimente pot fi situaţii de eroare (de exemplu încercarea de a
accesa un element de tablou aflat dincolo de limitele tabloului) sau pur şi simplu situaţii care
necesită o altă abordare decât cea obişnuită (de exemplu, la parcurgerea secvenţială a unui
fişier de intrare, la un moment dat se atinge sfârşitul fişierului; acest eveniment, fără a fi o
eroare, va schimba cursul de până atunci al programului). Evenimentele de genul celor
amintite se mai numesc excepţii.

O excepţie este un obiect ce aparţine clasei predefinite Throwable sau unei clase
descendente din Throwable. Clasa Throwable este definită în pachetul java.lang. Limbajul
Java dispune, de fapt, de o întreagă ierarhie de clase predefinite pentru reprezentarea
excepţiilor. Programatorul poate utiliza clasele predefinite sau poate să-şi definească propriile
clase de excepţii, cu condiţia ca ele să descindă direct sau indirect din Throwable. Prin
convenţie, clasele definite de utilizatori pentru reprezentarea excepţiilor vor fi derivate din
clasa Exception.
Definirea unui tip de excepţie de către utilizator se justifică de obicei prin:
• necesitatea reţinerii de date suplimentare şi/sau mesaje mai detaliate despre excepţie;
• necesitatea ca dintr-o clasă mai largă de excepţii să fie captate doar o anumită parte..

Tratarea clasică a excepţiilor


Se va considera un exemplu în care se va face abstracţie de faptul că limbajul Java
dispune de un mecanism special de tratare a excepţiilor:

class Tabel{
public static boolean fan_err = false;
public static int Increm(int[ ] tab,int indice,int val) {
if(indice>=0 && indice<tab.length){
tab[indice]+=val;
fan_err = false;
return tab[indice];
}
else{
fan_err = true;
Programare Java, Laborator 6

return 0;
}
}

public static int indexOf(int[ ] tab, int val) {


for(int i = 0;i<tab.length;i++)
if(tab[i]==val) return i;
return -1;
}
}

class ClientTabel{
public static void main(String[ ] arg) {
int[ ] tab = new int[10];
//se completeaza tabloul cu valori
int suma=0,indice,val;
boolean gata=false;
do{
//se citesc de la tastatura un indice si o valoare
suma+=Tabel.Increm(tab,indice,val);
if(Tabel.fan_err) //a fost eroare...
else //merg mai departe
if(Tabel.indexOf(tab,val)<0) //valoarea val nu exista in tablou
else //valoarea val exista
//. . .
}while(!gata);
}
}

Metoda Tabel.Increm() se poate confrunta cu situaţia în care unul dintre parametrii săi
(indice) primeşte o valoare necorespunzătoare. În maniera clasică de tratare a erorilor se poate
adopta una dintre următoarele rezolvări:

1. terminarea forţată a programului - nu reprezintă o tratare propriu-zisă a erorii; de


multe ori o eroare nu este chiar atât de gravă, iar programul poate şi trebuie să se
descurce cu ea într-un mod mai inteligent.
2. returnarea unei valori speciale pe post de „eroare“.
3. poziţionarea unui indicator de eroare şi returnarea unei valori valide, care însă
poate implica coruperea stării programului.
4. apelul unei funcţii prevăzute special pentru cazurile de eroare; această funcţie, la
rândul ei are de ales între cele 3 alternative de mai sus.

În exemplul prezentat, metoda indexOf() aplică varianta 2), iar metoda Tabel.Increm()
— varianta 3). Cu privire la varianta 2) trebuie spus ca ea nu este întotdeauna uşor de aplicat.
Dacă pentru funcţia indexOf(), de exemplu, valoarea -1 ca indicator de eşec este satisfăcătoare
(este clar că un indice de tablou nu poate fi negativ în mod normal), în schimb, pentru Increm()
se pune întrebarea ce valoare a tipului int ar putea fi considerată ca „ieşită din comun“? Pentru

2
Tratarea excepţiilor

asemenea funcţii o soluţie ar putea fi aceea că, în loc de int, tipul returnat să fie o structură
compusă din rezultatul propriu-zis şi un fanion de eroare.
Pe de altă parte, chiar şi acolo unde este aplicabilă, opţiunea 2) implică necesitatea
testării de fiecare dată a rezultatului execuţiei funcţiilor respective. Acest lucru poate duce
uşor la dublarea dimensiunii codului. Alternativa 3) prezintă dezavantajul că apelantul
funcţiei în care a apărut eroarea poate să nu „observe“ că s-a întâmplat ceva. De exemplu,
multe funcţii din biblioteca standard C indică apariţia unei erori prin setarea variabilei globale
errno. Dar prea puţine programe ale utilizatorilor testează valoarea lui errno suficient de
sistematic încât să se obţină o tratare consistentă a erorilor.

În concluzie, metodele clasice de tratare a excepţiilor se caracterizează prin faptul că


porţiunile de cod „normale“ se întrepătrund cu cele de tratare a excepţiilor, iar claritatea
programelor are de suferit din cauza ramificărilor generate de numeroasele teste ale
indicatorilor de eroare..

Mecanismul de emitere-captare a excepţiilor

Mecanismul de emitere-captare a excepţiilor are două avantaje esenţiale:


• permite separarea explicită a secvenţelor de cod care tratează situaţiile normale faţă de
cele care tratează excepţiile.
• rezolvă „dilema“ de la alternativa 2) de tratare clasică a erorilor, prin aceea că oferă
suportul necesar pentru a „forţa“ o funcţie să returneze, în situaţii deosebite, valori ale
altui tip decât cel specificat în antet ca tip returnat.

Se va ilustra în secvenţa de mai jos varianta „cu excepţii“ a metodei Tabel.Increm()


din programul prezentat în paragraful anterior:

class IndiceEronat extends Exception{ //clasa pt. reprezentarea exceptiei


private int lung;
private int index_er;
public IndiceEronat(int l,int i) {//constructor
lung=l; index_er=i;
}
public String toString(){
return "Indicele "+index_er+" eronat pentru tabloul de lungime "+ lung;
}
}

class Tabel{
public static int Increm(int[ ] tab,int indice,int val) throws IndiceEronat{
if(indice>=0 && indice<tab.length){
tab[indice]+=val;
return tab[indice];
}
else //emitere exceptie
throw new IndiceEronat(tab.length, indice);
} //. . .
}

3
Programare Java, Laborator 6

class ClientTabel{
public static void main(String[ ] arg) {
int[ ] tab = new int[Integer.parseInt(arg[0])];
//se completeaza tabloul cu valori
int suma=0,indice,val;
boolean gata=false;
do{
//se citesc de la tastatura un indice si o valoare
try { //secventa sensibila la aparitia unei exceptii
suma+=Tabel.Increm(tab,indice,val);
}
catch(IndiceEronat e){ //tratare exceptie
System.out.println("Exceptie IndiceEronat: "+e);
}
//. . .
}while(!gata);
}
}

Ideea fundamentală care stă la baza mecanismului de emitere-captare a excepţiilor este


aceea că dacă o funcţie oarecare F1 se confruntă cu o situaţie pe care nu o poate rezolva, ea va
anunţa acest lucru apelantului ei, prin emiterea unei aşa-numite excepţii, „sperând“ că
apelantul va putea (direct sau indirect) să rezolve problema. O funcţie F2 care doreşte să
rezolve o situaţie de excepţie trebuie să-si manifeste intenţia de a capta excepţia emisă în
situaţia respectivă. În exemplul de mai sus funcţia F1 este Tabel.Increm(), iar F2 este
ClientTabel.main().

Limbajul Java pune la dispoziţie trei construcţii de bază pentru lucrul cu excepţii:
• instrucţiunea throw pentru emiterea excepţiilor;
• blocul catch pentru definirea secvenţei de captare şi tratare a unei excepţii; o
asemenea secvenţă se mai numeşte handler de excepţii;
• blocul try pentru delimitarea secvenţelor de cod sensibile la apariţia excepţiilor..

Instrucţiunea throw
Ca sintaxă, această instrucţiune este asemănătoare cu return:

throw expresie_exceptie;

unde expresie_exceptie are ca rezultat o referinţă la un obiect al unei clase derivate


(direct sau indirect) din Throwable. De obicei, această expresie constă din aplicarea
operatorului new pentru a se crea un obiect al clasei alese pentru reprezentarea excepţiei.

O instrucţiune throw poate să apară într-o funcţie numai dacă:


• ea se găseşte în interiorul unui bloc try-catch care captează tipul de excepţie generată
de expresia din throw, sau
• definiţia funcţiei este însoţită de o clauza throws în care apare tipul de excepţie
respectiv sau

4
Tratarea excepţiilor

• excepţia generată aparţine claselor RunTimeException sau Error, sau descendenţilor


acestora

Dacă instrucţiunea throw nu apare într-un bloc try, efectul ei este asemănător unui
return: se creează o excepţie care va fi „pasată“ spre apelantul funcţiei respective şi se
produce revenirea din funcţie; funcţia însă NU va transmite NICI UN rezultat chiar dacă tipul
returnat care apare în semnătura funcţiei nu este void. De exemplu, dacă funcţia
Tabel.Increm() execută instrucţiunea throw, ea nu va returna nici un rezultat de tip int.

Clauza throws
Această clauză se ataşează la antetul unei funcţii şi ea precizează tipurile de excepţii
care pot fi generate pe parcursul execuţiei funcţiei respective:

tip_returnat nume_functie(lista_param) throws tip_ex1,


tip_ex2,...,tip_exn

Dacă în lista de tipuri din clauza throws există cel puţin două tipuri, tip_exi şi tip_exj,
derivate dintr-o superclasă comună tip_exsup, limbajul permite înlocuirea celor două cu
tip_exsup. Este adevărat că, până la urmă, toate tipurile din lista throws derivă direct sau
indirect din Exception (se poate spune chiar din Throwable sau Object), deci, teoretic clauza
poate conţine doar această clasă. În practică este, însă, nerecomandat acest lucru, deoarece,
aşa cum tip_returnat reprezintă tipul rezultatului returnat de funcţie la o execuţie normală, tot
aşa, clauza throws oferă informaţii referitoare la situaţiile de excepţie care pot apărea în
funcţia respectivă. Dacă în listă se trece doar Exception, informaţia este foarte vagă pentru
cititorul programului.

Care tipuri de excepţie vor apărea într-o clauză throws?


Dacă utilizatorul îşi defineşte propriile tipuri de excepţie, atunci este foarte probabil ca
el să ştie în ce situaţii urmează să se genereze excepţiile respective şi, ca urmare, le va
prevedea în clauze throws sau în secvenţe catch. În cazul excepţiilor predefinite ale limbajului
Java, acestea trebuie să apară în clauze throws ale funcţiilor care apelează metode din
biblioteca Java care, la rândul lor, generează excepţiile respective. Consultând documentaţia
claselor predefinite ale limbajului Java se poate constata că la fiecare metodă este indicată,
unde e cazul, clauza throws. Pentru excepţiile din clasele RuntimeException şi Error, precum
şi din descendenţii acestora nu sunt necesare clauze throws (v. exceptiile neverificate).

Dacă utilizatorul nu prevede clauza throws, şi nici secvenţe try-catch la o funcţie de a


sa care apelează funcţii predefinite dotate cu clauze throws, compilatorul va sesiza acest lucru
ca eroare. Aceasta este una dintre căile prin care utilizatorul poate afla ce ar fi trebuit să pună
în clauza throws a funcţiei sale. O situaţie frecventă, care constituie exemplu în acest sens este
cea a utilizării funcţiilor de intrare/ieşire din pachetul java.io, fără a prevedea clauze throws
IOException.

Blocurile try-catch

Din punct de vedere sintactic un bloc try-catch are forma de mai jos:

5
Programare Java, Laborator 6

try {
//secventa obisnuita de operatii, care poate fi intrerupta de aparitia unei exceptii
}
catch(tip_ex1 e){
//tratare exceptie de tipul tip_ex1
}
catch(tip_ex2 e){
//tratare exceptie de tipul tip_ex2
}
//. . .
catch(tip_exn e){
//tratare exceptie de tipul tip_exn
}
finally {
//secventa optionala (despre finally vom vorbi intrun paragraf urmator)
}
//. . .(*)

Un bloc try este urmat de 0, 1 sau mai multe blocuri catch şi, opţional, de un bloc
finally. Vom presupune deocamdată că nu avem bloc finally. Modul de funcţionare al unei
asemenea construcţii este următorul:
• dacă în decursul desfăşurării secvenţei de operaţii din blocul try este emisă o excepţie,
fie direct (adică prin execuţia unui throw explicit), fie indirect (adică prin apelul unei
funcţii care emite excepţia), secvenţa se întrerupe şi se baleiază lista de blocuri catch
asociate pentru a-l detecta pe cel corespunzător tipului de excepţie emisă;
• dacă un astfel de bloc există, se va lansa în execuţie secvenţa de tratare respectivă.
• dacă această secvenţă nu trece printr-un return sau un throw, la terminarea ei execuţia
programului continuă cu linia care urmează după ultimul bloc catch sau, după caz,
finally ataşate blocului try curent (cea notata cu (*) în secvenţa de mai sus).
• tot de la acea linie se reia execuţia şi dacă secvenţa try a rulat fără să fi apărut vreo
excepţie.

Când spunem bloc catch corespunzător unei excepţii, aceasta înseamnă că tipul
specificat ca parametru după cuvântul catch este fie identic cu tipul excepţiei, fie este o
superclasă a tipului excepţiei. Dacă nu există nici un bloc catch corespunzător excepţiei emise,
aceasta va fi propulsată spre apelanţii funcţiei curente, căutându-se un eventual bloc try
înconjurător. Practic, un bloc catch poate fi asimilat cu o funcţie anonimă, cu un singur
parametru specificat imediat după cuvântul catch.
Apelul unei asemenea funcţii nu se face explicit, ci automat, dacă în secvenţa try
asociată apare o excepţie corespunzătoare. Un bloc catch se poate termina normal (adică prin
epuizarea instrucţiunilor care îl compun) sau printr-un throw care să emită o nouă excepţie
sau tot pe cea primită. În acest din urmă caz are loc retransmisia excepţiei.
Referitor la drumul parcurs de o excepţie din momentul emiterii sale şi până ajunge să
fie tratată, lucrurile stau astfel:
• stiva de apeluri se baleiază dinspre vârf spre bază în căutarea unui bloc try
înconjurător în raport cu instrucţiunea throw emitentă. Un bloc try este înconjurător
faţă de o instrucţiune throw dacă aceasta se găseşte chiar în interiorul acelui bloc try
sau dacă se află într-o funcţie apelată (direct sau indirect) din acel bloc try.

6
Tratarea excepţiilor

• dacă se găseşte un bloc try înconjurător, se caută între blocurile catch ataşate, unul ce
corespunde tipului excepţiei emise. Dacă există un astfel de bloc catch, acesta preia
excepţia. În caz contrar, excepţia este propagată mai departe, repetându-se procesul de
la pasul anterior. Dacă se ajunge cu căutarea până în funcţia main() şi nici aici nu
există un handler adecvat, excepţia este preluată de handler-ul de excepţii al maşinii
virtuale care determină abandonul programului, cu afişarea unui mesaj corespunzător.
• dacă excepţia este preluată de un bloc catch şi acesta nu o retransmite, excepţia
respectivă se consideră a fi tratată, indiferent de modul în care handler-ul operează
efectiv (în particular un handler poate avea corpul de instrucţiuni vid).

Procesul de baleiere a stivei de apeluri implică, practic, părăsirea tuturor funcţiilor


„întâlnite“ în cale până la găsirea blocului try înconjurător. Cu ajutorul unui exemplu vom
ilustra mecanismul de funcţionare a blocurilor try-catch şi a instrucţiunii throw:

class oExceptie extends Exception{/*...*/}


class altaExceptie extends Exception{/*...*/}

class OClasa {
public static void oFunctie( ) throws oExceptie {
//. . .
if(eroare) throw new oExceptie( );
}
public static void altaFunctie( ) throws oExceptie, altaExceptie {
oFunctie( );
}
public static void siIncaUna ( ) throws oExceptie,altaExeceptie{
//. . .
try{ // 1
altaFunctie( );
}
catch(oExceptie e){
if(poti_sa_rezolvi) fa_o( );
else throw e; // retransmisia exceptiei e
}
catch(altaExceptie e){
if(alta_eroare) throw new altaExceptie();
else tratare_normala( );
}
try{ // 2
//presupunem ca aici nu se apeleaza oFunctie( ) si nici altaFunctie( )
}
catch(Exception e){
//capteaza orice exceptie derivata din Exception aparuta in try 2
}
//instructiunea imediat urmatoare blocului trycatch 2
}//end siIncaUna()
public static void main(String[ ] arg) {

7
Programare Java, Laborator 6

//. . .
}
//. . .
}//end class OClasa
Presupunem că la un moment dat lanţul de apeluri este:

main() -> siIncaUna() -> altaFunctie() -> oFunctie()

şi că oFunctie() execută instrucţiunea throw. Se creează o excepţie de tip oExceptie şi


se iese din oFunctie(), revenindu-se în altaFunctie(). Aici se constată că nu există bloc try şi,
ca urmare, se iese din altaFunctie() ca şi cum aici ar exista un throw, şi se revine în
siIncaUna(). De data aceasta revenirea are loc în interiorul unui bloc try (cel notat cu //1). Se
caută printre ramurile sale catch şi se găseşte cea cu parametru de tip oExceptie. Dacă
excepţia se poate rezolva, după execuţia secvenţei catch, programul continuă cu blocul try
următor (cel notat cu //2). Dacă se ajunge la retransmisia excepţiei, atunci se iese din
siIncaUna() şi se verifică dacă apelul la această funcţie nu se află în interiorul unui bloc try
din apelant (funcţia main(), adică).
La execuţia unui throw din interiorul unui bloc catch nu se rebaleiază lista curentă de
handler-e şi nici nu se trece la următorul bloc try-catch în aceeaşi funcţie, chiar dacă acesta
din urmă ar putea capta excepţia emisă (cum ar fi cazul blocului try //2 din exemplul de mai
sus). Există şi posibilitatea ca un bloc try-catch să fie inclus în secvenţa try a unui alt bloc try-
catch, ca în exemplul următor:

class oExceptie extends Exception{/*...*/}


class altaExceptie extends Exception{/*...*/}
class OClasa {
public static void oFunctie( ) throws oExceptie{
//. . .
if(eroare) throw new oExceptie( );
}
public static void altaFunctie( ) throws oExceptie, altaExceptie{
try { //1
try { //2
oFunctie( );
}
catch (altaExceptie e){
System.out.println("S-a captat altaExceptie"); //aici tratarea
//presupune pur si simplu o afisare de mesaj
}
}
catch(oExceptie e){
if(poti_sa_rezolvi) fa_o( );
else throw e; // retransmisia exceptiei e
}
catch(altaExceptie e){
if(alta_eroare) throw new altaExceptie();
else tratare_normala( );

8
Tratarea excepţiilor

}
}
}
public static void main(String[ ] arg) {
//. . .
}
//. . .
}//end class OClasa

În acest exemplu se observă că blocul try-catch notat cu //2 nu va capta excepţii de


tipul oExceptie. Blocul try-catch notat cu //1 este considerat ca înconjurător pentru blocul //2
şi, deci, excepţia emisă de oFunctie() va ajunge să fie tratată de handler-ul corespunzător
ataşat blocului //1. Astfel, un bloc try este înconjurător pentru un throw dacă secvenţa try
• conţine în mod direct acel throw, sau
• conţine un bloc try-catch în care se află acel throw, sau
• conţine rădăcina apelului la funcţia în care se află acel throw.

Secvenţa finally

Dacă este prezentă, se execută întotdeauna după ce s-a terminat secvenţa try sau
secvenţele catch din blocul try-catch curent. Practic, nu există posibilitatea de a determina
desfăşurarea execuţiei unui bloc try prevăzut cu ramura finally în sensul „şuntării“ acestei
secvenţe. Secvenţa finally reprezintă un mecanism prin care se forţează execuţia unei porţiuni
de cod indiferent dacă o excepţie a apărut sau nu. De obicei, ramura finally are rolul de a
„face curat“, în sensul de a elibera resurse de genul fişiere deschise accesate prin intermediul
unor variabile locale.

public boolean Cautare(String nume_fis,String cuvant ) throws IOException{


BufferedReader flux_in = null;
try{
flux_in = new (new InputStreamReader(new FileInputStream(nume_fis)));
String linie;
while ((linie = flux_in.readLine()) != null)
if(linie.equals(cuvant)) return true;
return false; //nu s-a gasit cuvantul in fisier
}
finally {
if(flux_in != null) flux_in.close();
}

Primul lucru pe care îl constatăm la programul prezentat este absenţa ramurii catch.
Aceasta înseamnă că programatorul nu are intenţia să trateze excepţiile de tip IOException în
cadrul funcţiei Cautare(). Mai mult, acest program este un exemplu de utilizare a blocului try
nu neapărat pentru a rezolva situaţii de eroare, ci, datorită blocului finally, mai mult pentru a
determina o anumită înlănţuire a operaţiilor. De altfel, trebuie să ne obişnuim cu ideea că o
excepţie nu este întotdeauna un indiciu de eroare, ci, uneori, este pur şi simplu un semn că în
desfăşurarea programului a intervenit ceva deosebit care trebuie tratat altfel.

9
Programare Java, Laborator 6

În secvenţa de mai sus dacă fişierul nu ar fi putut fi deschis (de exemplu nume_fis nu
există) atunci referinţa flux_in ar fi rămas pe null. De aceea s-a prevăzut testul din blocul
finally. Presupunând că deschiderea fişierului ar fi mers cum trebuie, în continuare, indiferent
de modul în care se termină blocul try (pe unul dintre cele două return-uri sau prin apariţia
unei excepţii generate de funcţia readLine()) prezenţa ramurii finally asigură închiderea
fişierului înainte de părăsirea efectivă a funcţiei Cautare(). Ramura finally se mai poate utiliza
şi pentru a forţa execuţia unei porţiuni de cod când în try avem bucle ce conţin combinaţii de
break, continue şi return.

Excepţii predefinite ale limbajului Java


Există două clase principale de excepţii predefinite: Error şi Exception. Excepţiile
reprezentate de clasele descendente din Error indică, în general, probleme foarte serioase care
trebuie să ducă la întreruperea programului şi nu se recomandă captarea lor de către utilizator.
Ele sunt generate automat de suportul de execuţie al programelor Java, la întâlnirea situaţiilor
de eroare respective. Clasele Error şi RuntimeException, împreună cu descendenţii lor,
formează categoria excepţiilor neverificate (unchecked), adică excepţii care pot fi generate în
programe, fără obligativitatea ca ele să apară în clauze throws.
Restul excepţiilor sunt verificate (checked), adică la compilare se verifică dacă există
clauze throws corespunzătoare. Un exemplu în acest sens este clasa IOException (definită în
pachetul java.io), care trebuie specificată fie în clauze throws, fie în ramuri catch, acolo unde
se folosesc funcţiile de intrare/ieşire din pachetul java.io.
Programatorul poate să-şi definească anumite clase de excepţii ca descendente din
RuntimeException, făcându-le, astfel, neverificate. Acest lucru trebuie evitat, deoarece lipsa
clauzei throws din declaraţia unei funcţii care totuşi generează excepţii, este, de fapt, o lipsă
de informaţie pentru potenţialii utilizatori ai funcţiei respective.
În exemplul care urmează vom arata cum putem folosi mecanismul excepţiilor pentru
a rezolva problema depăşirii limitelor unui tablou. Practic, vom completa elementele unui
tablou fără ca, la parcurgerea tabloului, să aplicăm comparaţia clasică a indicelui faţă de
lungimea tabloului (i < tab.length). Aici ne vom folosi de o clasă de excepţii predefinită,
IndexOutOfBoundsException, din pachetul java.lang:

import java.io.*;

public class Tablou {


public static void main (String[ ] args) {
int [ ] tab = new int[10];
int i;
try {
for(i=0;;) tab[i++]=i; //parcurg tabloul fara grija
}
catch(IndexOutOfBoundsException e) {
//ajung aici cand i >=tab.length
System.out.println(e);
System.out.println("Gata tabloul!");
}
}
}

10
Tratarea excepţiilor

Se observă că aici nu avem instrucţiune throw, adică nu avem o emitere explicită a


excepţiei. Aceasta, deoarece în anumite situaţii, cum este şi depăşirea limitelor tablourilor,
excepţiile sunt generate automat, de către suportul de execuţie Java. Excepţia
IndexOfBoundException este derivată din RuntimeException, deci este neverificată. Aceasta
înseamnă că şi dacă în programul de mai sus nu am fi prevăzut ramura catch pentru ea, nu am
fi fost obligaţi să ataşăm funcţiei main() o clauză throws IndexOutOfBoundsException.

Temă

1. Să se scrie un program care citeşte de la tastatură o pereche de numere în care primul


număr trebuie să fie mai mic decât al doilea. Dacă această condiţie nu este îndeplinită,
folosind mecanismul excepţiilor, se va semnala eroare şi se va trata această eroare prin
cererea unei alte perechi de numere. Perechea care îndeplineşte condiţia va fi scrisă într-
un fişier primit ca parametru la apel.
2. Să se scrie un program care citeşte două numere de la tastatură şi afişează rezultatul
împărţirii celor două numere. Programul va utiliza mecanismul de tratare a excepţiilor
pentru a cere reintroducerea împărţitorului atunci când se realizează o împărţire la 0, şi
pentru a cere reintroducerea corectă a numerelor dacă în loc de numere s-au introdus
cuvinte.
3. Să se scrie un program care citeşte numele şi CNP-ul unei persoane. Mecanismul de
tratare a excepţiilor va fi utilizat pentru introducerea unui CNP corect. Dacă CNP-ul a fost
introdus corect va fi utilizat pentru a determina data naşterii şi a calcula vârsta.

11
Lucrarea 7

Dezvoltarea aplicațiilor cu interfață grafică în Java


– biblioteca Swing

Aplicațiile cu interfață grafică în Java pot să fie dezvoltate din cod (vezi exemplele din
curs) sau cu ajutorul unui designer grafic. Dezvoltarea aplicațiilor cu interfață grafică din cod
permite construirea de aplicații cu interfață dinamică şi oferă un control deplin asupra a ceea
ce se întâmplă şi cum se întâmplă în cod, dar este lentă. Utilizarea unui designerului grafic
crește viteza de dezvoltare a aplicației prin faptul că permite selectarea şi adăugarea
componentelor în interfața grafică prin click-uri, mutarea componentelor în alte poziţii prin
drag and drop, configurarea proprietăților componentelor într-o interfață grafică, într-un
Property editor. Designer-ul grafic generează codul din spatele acțiunilor realizate în modul
design. Window Builder Pro este un designer grafic care se instalează ca un plugin în Eclipse.

Instalarea Window Builder Pro se realizează urmând instrucțiunile de pe linkul:


http://www.eclipse.org/windowbuilder/download.php

După ce a fost instalat WindowBuilder Pro se creează un proiect nou în Eclipse şi apoi se
creează un JFrame în acesta dând comanda File > New > Other > Window Builder > Swing
designer> JFrame (vezi figura 1).

Figura 1 – crearea unui JFrame

JFrame-ul creat poate fi vizualizat în modul Design sau în modul Source. Comutarea între
cele două moduri se poate face folosind butoanele din partea stânga jos (vezi figura 2).
Adăugarea componentelor în interfață în modul design se face selectând componenta dorită
prin click în secţiunea Pallete (JLabel, JTextField, JButton, etc) şi apoi făcând click în
poziția dorită pe JFrame. Containerele de componente cum ar fi containerul JFrame-ului sau
JPanel-urile pot utiliza gestionari de componente (LayoutManageri) care decid unde
amplasează controalele în cadrul containerelor, ce dimensiuni le dau în funcție de ordinea
Programare Java, Lucrarea 6

adăugării componentelor şi pe baza unor reguli de funcționare. LayoutManager-ul implicit


pentru containerul JFrame-ului este BorderLayout care împarte ecranul in 5 zone, nord, sud,
est, vest centru şi permite adăugarea de componente în aceste zone. LayoutManager-ul
implicit pentru JPanel-uri este FlowLayout care permite adăugarea componentelor una lângă
alta. Când nu mai au loc sunt trecute pe rândul următor. Într-o primă etapă se recomandă
amplasarea controalelor în poziţii absolute. Pentru aceasta se selectează prin click
LayoutManager-ul AbsoluteLayout şi se aplică prin click pe containerul JFrame-ului (sau se
scrie în cod contentPane.setLayout(null)). În continuare pot să fie selectate componente în
secţiunea Pallete şi aplicate în poziţia dorită în interfață. Proprietățile componentelor se pot
modifica din cod sau din modul design (daca se selectează prin click componenta se pot apoi
modifica proprietățile acesteia în Property Editor.

Figura 2- designer-ul grafic Window Builder Pro

Codul generat pentru JFrame-ul creat, fără a face nici o modificare în designer-ul
grafic este prezentat mai jos:

package exemplu;

import java.awt.BorderLayout;
import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;

public class MyTestFrame extends JFrame {


private JPanel contentPane;
/**
* Launch the application.
*/

2
Interfeţe grafice

public static void main(String[] args) {


EventQueue.invokeLater(new Runnable() {
public void run() {
try {
MyTestFrame frame = new MyTestFrame();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}

/**
* Create the frame.
*/
public MyTestFrame() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(contentPane);
}
}

S-a creat clasa MyTestFrame care extinde JFrame, prin urmare are acces la toate
proprietățile pe care aceasta le expune. În constructorul clasei se stabilește acțiunea care se va
întâmpla la rulare când se va face click pe X-ul din colţul din dreapta sus şi anume închiderea
aplicaţiei. Metoda setBounds() stabilește locația ferestrei (primii doi parametri) şi
dimensiunea acesteia (ultimii doi parametri). Se instanţiază JPanel-ul numit contentPane. Se
seteză o bordură goală de 5 pixeli faţă de marginile de sus, stânga, jos şi dreapta. Se setează
pentru JPanel gestionarul de componente BorderLayout cu golurile specificate ca şi
parametru între componente pe orizontală şi pe verticală. Se setează JPanel-ul creat ca şi
containăr al ferestrei.

În programul principal se creează un fir de execuţie folosind expresia unei clase


anonime şi implementând interfaţa Runnable. Codul din metoda run() reprezintă codul firului
de execuţie. În aceasta se creează un obiect de MyTestFrame şi se afişează pe ecran. Metoda
statică invokeLater() din clasa EventQueue determină firul să fie rulat după ce toate
evenimentele care așteaptă sunt procesate.

3
Programare Java, Lucrarea 6

Tema
1. Realizați o aplicație cu interfață grafică de tip calculator, similară cu cea din figura 3.
Situațiile de excepție care pot să apară vor fi tratate prin afișarea de mesaje corespunzătoare
(de exemplu: Împărțire la 0, valoare lipsă, valoare necorespunzătoare - dacă în loc de cifre se
introduc litere). După finalizarea aplicației se va crea fișierul jar rulabil, cu ajutorul comenzii
File > Export > Java > Runnable jar file. Se alege pentru opțiunea Launch configuration
clasa main-ului şi se specifica prin Export destination locaţia şi denumirea fișierului jar care
va fi creat. Fișierul jar creat se rulează prin dublu click.

Figura 3 - Calculator

Indicații de rezolvare:
Creați un proiect nou în Eclipse şi în interiorul acestui proiect un JFrame. Deschideți
JFrame-ul în modul Design. Setaţi pentru containerul JFame-ului LayoutManager
AbsoluteLayout (vezi indicaţiile de mai sus). În continuare puteți amplasa în containerul
JFrame-ului componente în ce poziţii doriţi si puteți să le daţi ce dimensiune doriți.
Calculatorul are în containerul JFrame-ului 3 tipuri de componente: JLabel, JTextField şi
JButton. Pentru toate componentele designer-ul grafic creează variabile membre private în
clasa JFrame-ului. Fiecare componentă trebuie selectată si modificate din secțiunea
Properties proprietățile acesteia. Pentru fiecare componentă se recomandă modificarea
proprietății Variable name prin introducerea unui nume sugestiv. Primele 3 litere ar trebui sa
indice tipul de componenta (lbl – label, btn – buton, txt- caseta de text) şi apoi ar trebui
introdusă o denumire care să indice rolul componentei. Câteva exemple de denumiri
recomandate sunt următoarele: lblOperand, txtOperand1, btnAdunare. Cu excepția casetelor
de text, pentru celelalte componente trebuie editată proprietatea text, care reprezintă textul
afișat de componentă.

Rezultatul operației aritmetice va fi afișat într-un JLabel, de culoare alba.

După construirea interfeței grafice urmează scrierea codului care se va executa când se va
face click pe butoane. Pentru aceasta se face dublu click pe buton în modul Design. Se va
comuta pe modul Source, cursorul fiind poziționat pe metoda actionPerformed(), metodă care
se va executa la rulare atunci când se face click pe buton. Designer-ul grafic generează câte o
clasă anonimă pentru click-ul pe fiecare buton. Extragerea unei valori dintr-o caseta de text
se face cu ajutorul metodei getText().

4
Interfeţe grafice

2. Sa se realizeze o aplicație cu interfață grafică similară cu cea din figura 4:

Figura 4 - formaţii

Numele unei formaţii se introduce în caseta de text şi apoi se apasa tasta enter, acţiune
care determină adăugarea denumirii formaţiei în caseta JList de dedesupt. Acţionarea
butonului de ştergere determină ştergerea formaţiilor selectate în caseta JList (ştergere
multiplă).

Indicaţii de rezolvare:

Interfaţa grafică se va realiza utilizând gestionarul de componente BorderLayout. În


nord se va adauga caseta de text, în centru controlul JList iar în sud butonul de stergere.
Adăugarea unui eveniment pe caseta de text se poate face în modul Design prin click
dreapta pe casetă, apoi Add event handler > action > actionPerformed.
Adăugarea în JList se poate realiza cu ajutorul unui obiect DefaultListModel asociat
JList-ului (vezi exemplul din curs).

3. Să se realizeze o aplicaţie care permite introducerea unor filme lansate în ultimii 5 ani
şi afişarea acestora în format tabelar. Aplicaţia va avea o interfaţă grafică similara cu cea din
figura 5:

Figura 5 - filme

5
Programare Java, Lucrarea 6

Indicaţii de rezolvare:

Contanerul JFrame-ului va utiliza gestionarul de componente AbsolutLayout.

Pentru anul de lansare se va utiliza o componentă JSpinner configurată prin proprietatea


model astfel încat valoarea minimă să fie 2015, valoarea maximă 2020 şi pasul de
incrementare 1. Preluarea valorii selectate se realizeaza cu ajutorul metodei getValue().

Apăsarea butonului Adauga va determina introducerea filmului într-un control JTable.


Acţionarea butonului de ştergere va determina stergerea filmelor selectate în JTable.

Adăugarea, respectiv ştergerea filmelor în JTable se va realiza cu ajutorul unui obiect


DefaultTableModel asociat JTable-ului.

6
Lucrarea 8

Java Database Connectivity


(JDBC) şi MySQL

Să se creeze în baza de date ”test” din MySQL o tabelă ”persoane” cu câmpurile id


(întreg cheie primara), nume (varchar), varsta (întreg). Se va realiza o aplicaţie Java, care se
va conecta la baza de date MySQL cu ajutorul tehnologiei JDBC (suportul teoretic este în curs)
şi va realiza operaţiile primare asupra tabelei persoane: interogare, inserare, căutare, ştergere
şi actualizare.

Aplicaţia va avea interfaţa din figura 8.1:

Fig. 8.1 – Interfaţa principală a aplicaţiei în starea 1

• săgeţile vor permite navigarea printre înregistrările din tabelul persoane (poziţionarea
pe prima înregistrare, pe înregistrarea precedentă, pe înregistrarea următoare, respectiv
pe ultima înregistrare). Dacă suntem pe prima înregistrare, butoanele de navigare la
stânga se vor dezactiva. În mod simetric se va proceda cu butoanele de navigare la
dreapta.

• caseta de text din JToolBar, va afişa numărul înregistrării curente şi numărul total de
înregistrări

• vor fi implementate comenzi de adăugare, editare, ştergere, căutare, salvare şi


renunţare

• vor fi implementate două stări:


o starea 1 – în care se poate naviga printre elemente si se pot da comenzile de
adăugare, editare, ştergere
o starea 2 - în care se ajunge dacă utilizatorul alege adăugare sau editare, în care
singurele opţiuni active sunt salvare sau renunţare (vezi figura 8.2).
Programare Java, Laborator 8

Fig. 8.2 – Interfaţa principală a aplicaţiei în starea 2

• ştergerea se va realiza doar după un mesaj suplimentar de confirmare (vezi figura 8.3)

Fig. 8.3 – Confirmare ştergere

• numele persoanei care se caută va fi introdus într-o fereastră de tip showInputDialog()


precum cel din figura 8.4

â
Fig. 8.4 – Căutarea unei persoane

2
Lucrarea 9

Procesarea documentelor XML

Să se dezvolte o aplicație cu interfață grafică care permite conectarea la documente


XML şi afişarea acestora în formă arborescentă, într-un control JTree (vezi figura 1). Se vor
afișa doar tag-urile şi textele dintre tag-uri, fără atribute. Pentru exploatarea XML-ului se va
folosi tehnologia Document Object Model (DOM). Suportul teoretic pentru utilizarea acestei
tehnologii se găsește în capitolul 9. Procesarea documentelor XML din curs.

Figura 1 – interfața principală a aplicației

Apăsarea butonului Open XML, va determina afișarea unei ferestre de tip JFileChooser,
precum cea din Figura 2. Fereastra va fi configurată astfel încât să nu permită deschiderea
decât a documentelor XML şi la apăsarea butonului Open XML va fi afișat implicit conținutul
folder-ului proiectului. Se vor folosi ca fișiere de test fișierele optionale.xml (din slide-ul 3 al
cursului) şi studenti.xml (din slide-ul 17 al cursului).

Figura 2 – fereastră de tip JFileChooser


Programare Java, Lucrarea 9

În fereastra principală, în colțul din dreapta sus, va fi afișată într-o caseta de tip JLabel,
ora citită din sistem în formatul oo:mm:ss. Afișarea va fi de tipul unui ceas, se vor vedea
secundele cum se schimbă, apoi minutele cum se schimbă etc. Pentru aceasta se va folosi un
Timer configurat să genereze câte o întrerupere la fiecare secundă. În rutina de tratare a
întreruperii se citeşte timpul din sistem şi se actualizează în caseta JLabel.

Pentru exploatarea xml-ului se recomandă o abordare recursivă, precum cea prezentată în


slide-ul 22 al cursului. Nodurile din JTree sunt de tip DefaultMutableTreeNode

2
Lucrarea 10

Java 8

Să se realizeze un program de evidentă a angajaților unei firme. Se va crea clasa


Angajat cu următoarele variabile membre:
• numele (String)
• postul (String)
• data angajarii (LocalDate)
• salariul (float).
Clasa va avea constructor cu parametri, gettere şi settere pentru accesul variabilelor
membre şi va redefini metoda toString din clasa Object astfel încât să returneze un String cu
valorile variabilelor membre separate prin virgule.

Să se realizeze un program care implementează cerințele de mai jos utilizând


facilitățile lui Java 8 (stream-uri, expresii lambda, referințe la metode, etc). Suportul teoretic
este în cursul 9. Caracteristicile Java 8. Programul va realiza următoarele:
1. Citește din fișierul de intrare in.txt datele angajaților și creează o colecție de
angajați de tip List.
2. Afișează colecția creată
3. Afișeză angajații care au salariul peste 2500 ron. Rezolvarea va utiliza stream-uri
şi expresii lambda.
4. Creează o listă cu angajații din luna aprilie, a anului curent, care au funcție de
conducere (postul conține unul din cuvintele şef sau director). Pentru crearea unei
liste dintr-un stream se va utiliza operația terminală collect (Collectors.toList()).
Se vor utiliza steram-uri şi expresii lambda. Noua listă va fi apoi afișată.
5. Afișează angajații care nu au funcție de conducere (postul lor nu conține cuvintele
director sau șef), în ordine descrescătoare a salariilor, folosind stream-uri şi
expresii lambda.
6. Extrage din lista de angajaţi o listă de String-uri care conţine numele angajaţilor
scrise cu majuscule. Lista de String-uri va fi afişată.
7. Afișează salariile mai mici de 3000 de RON (doar salariile, fără alte informaţii)
folosind stream-uri expresii lambda, referinţe la metode.
Lucrarea 11

Fire de execuție

1. Să se realizeze un program care simulează depunerea şi extragerea unor sume de


bani într-un / dintr-un cont bancar. Sumele depuse şi extrase vor fi generate aleatoriu şi vor fi
de maxim 1000 RON. Simularea va afişa în consolă mesaje cu sumele depuse sau extrase şi
suma din cont obținută în urma operaţiei efectuate, precum în figura 1. Se va crea un fir de
execuție pentru operațiile de depunere în cont şi un fir de execuție pentru operațiile de
extragere din cont. Firul de execuţie care realizează extrageri va intra în stare de așteptare
dacă suma care trebuie extrasă la un moment dat nu este disponibilă în cont. Firul de execuție
care realizează depuneri în cont notifică firul care realizează extrageri, scoțându-l din starea
de așteptare. Contul bancar reprezintă resursa comună care va fi modificata de cele două fire
de execuție. Se vor crea 4 clase: clasa ContBancar cu variabila membră suma din cont şi cu
metode pentru depunere şi extragere, clasa Depunere, clasa Extragere şi clasa programului
principal.

Figura 1 – simulare operaţii de depunere şi extragere bani în cont

2. Sa se realizeze un program care simulează intrarea şi ieşirea maşinilor într-o / dintr-


o parcare. Parcarea are 10 locuri, 3 intrări şi o singură ieşire. Programul va afişa mesaje
precum în figura 2.

Figura 2 – Simulare parcare


Programare Java, Lucrarea 11

Indicații de rezolvare: Se pot realiza simultan 4 operații: pot intra 3 mașini şi poate ieşi o
mașină în / din parcare. Fiecare operație se va realiza într-un fir de execuție. Se va utiliza
modelul Producator - Consumator, discutat la curs. Producătorul reprezintă o intrare în
parcare, iar consumatorul o ieșire din parcare. Resursa comună este reprezentată de un obiect
de tip Parcare. Așadar se creează clasa Parcare, cu variabile membre număr total de locuri
şi număr de locuri ocupate, variabile care se inițializează prin constructor. Clasa va avea o
metodă sincronizată pentru simularea intrării unei maşini, care acceptă ca şi parametru de
intrare numărul intrării. Cat timp nu sunt locuri disponibile se așteaptă eliberarea unui loc.
Când sunt locuri disponibile, mașina intră în parcare şi creşte numărul locurilor ocupate. Se
afișează un mesaj corespunzător. De asemenea va exista o metodă sincronizata pentru
simularea ieşirii unei maşini, care decrementează numărul de locuri ocupate şi afișează un
mesaj corespunzător. Toate schimbările de număr de locuri ocupate sunt însoțite de notificări
pe care firele de execuţie le transmit. Se creează clasa Intrare care reprezintă producătorul şi
clasa Ieşire care reprezintă consumatorul. Aceste clase corespund firelor de execuţie deci
extind clasa Thread si redefinesc metoda run. Firul de execuție al main-ului creează cele 4
fire de execuție şi le trece în starea gata de rulare.

S-ar putea să vă placă și