In limbajele de programare, majoritatea tipurilor de date utilizate pentru variabile sunt valorile booleene, valorile numerice si sirurile de caractere (sau vectori de caractere terminati cu ‘\0’). In contrast cu C sau C++, in Java modul de gestiune a sirurilor de caractere este diferit, deoarece:
- in Java fiecare char reprezinta o valoare Unicode pe 16 biti, si nu 1 octet;
- in Java, valorile de tip siruri de caractere sunt gestionate de obiecte String;
- in Java, sintaxa va permite sa utilizati String asemenea unui tip primitiv de date (puteti folosi operatorul = pentru a le initializa)
- in Java, String sunt obiecte imuabile (immutable), in sensul ca odata ce sunt create, ele nu isi pot schimba valoarea.
Alte subiecte care fac parte din acest tutorial Java sunt accesibile prin intermediul Java Tutorial 6 – Cuprins .
Cum se defineste si initializeaza un String
Deoarece valorile sir de caractere sunt gestionate de obiecte String, acestea pot fi create utilizand sintaxa implicita, bazata pe new:
String s = new String();
String sCuValoare = new String("Salut String!" );
Deoarece sirurile de caractere sunt printre cele mai folosit tipuri de date, sintaxa Java va permite sa le folositi asemenea unor tipuri primitive de date, prin intermediul operatorului =:
String s = "Hello String!";
s = "Hello Java!";
Nu uitati ca in Java, sirurile de caractere sunt obiecte. Atunci cand se utilizeaza = intre 2 referinte String veti copia valoarea referintei si nu valoarea obiectului. Urmatorul exemplu:
String string1 = "Hello Strings";
String string2 = string1;
if( string1 == string2 )
System.out.println("egale");
altceva
System.out.println("NU sunt egale");
va afisa la consola egale, deoarece referintele au aceeasi valoare/adresa si genereaza in memorie aceasta situatie:
Ce inseamna ca String este imuabil (immutable)
Un obiect imuabil (immutable) este un obiect care odata ce este creat nu isi poate modifica valoarea. String este o clasa imuabila si se bazandu-ne pe exemplul anterior, sa vedem ce se intampla atunci cand facem ceva de genul:
String string1 = "Hello String!";
System.out.println(string1);
string1 = "Hello Java!";
System.out.println(string1);
Daca rulati acest exemplu, veti obtine
Hello String!
Hello Java!
La prima vedere putem spune ca variabila string1 si-a schimbat valoarea din Hello String ! in Hello Java !, dar acest lucru este gresit, deoarece:
- String este imuabil (immutable), deci nu isi poate modifica valoarea;
- string1 este o referinta String si nu un obiect;
Deci, in realitate, cele doua instructiuni genereaza un nou obiect String si referinta string1 va primi adresa noului obiect, cum ar fi aceasta imagine a memoriei:
Deoarece, clasa String este una speciala, NU poti schimba functionalitatea acesteia. Nici prin suprascrierea metodelor in cazul unei derivari, deoarece clasa String este definita final (nu poate fi derivata).
Ce este String Constant Pool
In Java, obiectele String sunt speciale (si pentru ca sunt imuabile) deoarece valorile lor sunt tratate intr-un mod special. Pentru o utilizare eficienta a memoriei, JVM gestioneaza valorile String (in special literali String), stocandu-le intr-o zona speciala de memorie numita String constant pool. Cand utilizati o valoare literala pentru a initializa un String, JVM verifica colectia String constant pool pentru a vedea daca valoarea String respectiva este deja in memorie. Daca este, nu va crea o noua valoare, ci va pune adresa ei in referinta.
Bazandu-ne pe aceasta regula, exemplul urmator
String string3 = "Hello String !" ;
String string4 = "Hello String !" :
// compara referinte de abiecte String, NU valorile lor
if(string3 == string4)
System.out.println("EGALE");
else
System.out.println("NU sunt egale");
va printa EQUAL deoarece datele sunt prelucrate astfel:
- (1) creeaza un nou obiect String cu valoarea “String Hello!” si il stocheaza in String constant pool;
- (2) referinta string4 este initializata cu adresa obiectului din String constant pool;
Creare String cu new vs. folosind operatorul =
Dupa cum se vede, exista doua modalitati de a crea un obiect String:
- folosind new: String s1 = new String (“Hello!”);
- folosind operatorul = : String s2 = “Hello”;
Diferenta dintre cele doua abordari este ca atunci cand utilizati new pentru a crea un obiect String acesta se comporta ca un obiect normal, ceea ce inseamna ca valoarea sa este plasata in Heap si nu in zona speciala, numita String constant pool.
Si dovada:
// valoare memorata in String constant pool
String string1 = "Hello String!";
// valoare memorata in String constant pool
String string2 = string1
if(string1 == string2) //compara referinte, NU valori
{
System.out.println("EGALE");
} else {
System.out.println("NU sunt egale");
}
//valoare memorata in Heap
String string3 = new String("Hello String!" );
if(string1 == string3) //compara referinte, NU valori
{
System.out.println("EGALE");
} else {
System.out.println("NU sunt egale");
}
if(string1.equal(string3)) //compara valorile
{
System.out.println("EGALE");
} else {
System.out.println("NU sunt egale");
}
Ruland exemplul anterior, veti obtine:
EGALE
NU sunt egale
EGALE
Cum poti prelucra o valoare String
Clasa String are o multime de metode folosite pentru a prelucra o valoare. Cele mai utilizate sunt:
Metoda | Descriere |
charAt() | intoarce caracterul aflat la un anumit indice; indicele ia valori de la 0 la length()-1; |
concat() | concateneaza un String la sfarsitul celui existent; la fel ca +; |
equals() | compara la nivel de caracter 2 valori String; face diferenta intre litere mici si mari |
length() | returneaza numarul de caractere; NU ESTE atributul length al unui vector. Este o metoda. |
replace() | inlocuieste aparitiile unui caracter cu unul primit |
substring() | returneaza un subsir |
toLowerCase() | converteste toate caractere la litere mici |
toString() | returneaza valoarea obiectului String |
toUpperCase() | converteste toate caractere la majuscule |
trim () | elimina spatiul de la sfarsitul sirului de caractere |
Deoarece String este imuabila, trebuie sa fiti atenti atunci cand se utilizeaza unele dintre aceste functii, deoarece rezultatul lor este un nou obiect de tip String. Aceste metode NU vor afecta obiectul curent. Exemplul urmator explica acest lucru:
String string4 = "TEST" ;
string4.toLowerCase(); //rezultatul este un alt String
System.out.println(string4 );
string4.concat(" Java"); //rezultatul este un alt String
System.out.println(string4);
String string5 = string4 toLowerCase(); //rezultatul este un alt String
System.out.println(string5);
string5 = string5.concat(" Java"); //rezultatul este un alt String
System.out.println(string5);
La consola se obtine:
TEST
TEST
test
test JAVA
Ce sunt StringBuilder si StringBuffer
Aceste doua clase sunt aproape identice (au acelasi API). Diferenta dintre ele este ca StringBuilder (adaugat incepand cu Java 5) NU este thread safe (mai mult pe aceasta tema, in posturile urmatoare) si este mai rapid.
Ambele clase sunt definite deoarece implementeaza intr-un mod mai eficient (memorie) prelucrarea de valori de tip String. Unul dintre motive este ca valorile lor nu sunt stocate in String Constant Pool si se comporta ca orice obiect Java – in cazul in care obiectul nu mai este referit este distrus prin GC si nu va ramane in String Constant Pool.
Ideea din spatele StringBuilder si StringBuffer este de a oferi mijloace eficiente pentru operatii de tip I/O cu fluxuri mari.
Instantele StringBuilder si StringBuffer sunt create folosind numai operatorul new, si nu cu operatorul = ca in cazul String:
StringBuilder sb1 = new StringBuilder("Hello");
StringBuffer sb2 = new StringBuffer("Hello Java !");
//eroare de compilare
sb1 = "Hello World !"; //NU este permis
Daca doriti sa modificati valoarea unei StringBuilder sau StringBuffer puteti utiliza metode din aceste calse. Cele mai utilizate metode sunt:
Metoda | Descriere |
append() | adauga argumentul la sfarsitul obiectul curent |
delete() | sterge caractere intre un index de inceput si indicele de sfarsit |
insert() | introduce un sir de caractere la un anumit offset |
reverse() | inverseaza valoarea obiectului curent |
toString() | returneaza valoarea obiectului StringBuilder sau StringBuffer |
Aceste metode afecteaza valoarea obiectului care face apelul metodei, ca in exemplul urmator:
StringBuilder sb1 = new StringBuilder("12345");
sb1.append("6789");
System.out.println(sb1); //afiseaza 123456789
sb1.delete(0,2);
System.out.println(sb1); //afiseaza 3456789
sb1.reverse();
System.out.println(sb1); //afiseaza 9876543
Un alt fapt despre StringBuilder sau StringBuffer este ca puteti apela metode multiple – metode inlantuite (chained methods) pe acelasi obiect. Daca va pregatiti pentru examenul SCJP, este posibil sa primiti intrebari care sa acopere acest subiect. Pentru a da raspunsul corect tineti cont de faptul ca metodele inlantuite afecteaza obiectul utilizat la apel si sunt executate de la stanga la dreapta.
Urmatoarea secventa metode multiple:
StringBuilder sb2 = new StringBuilder("Test");
sb2.append(" Java ").delete(0, 4).insert(0,"Begin");
System.out.println(sb2); //afiseaza Begin Java
genera aceasta iesire: Begin Java
Alte subiecte care fac parte din acest tutorial Java sunt accesibile prin intermediul Java Tutorial 6 – Cuprins.