Tutorial SCJP Java – #11 Cum se utilizeaza String, StringBuilder si StringBuffer

2 comments - This post in english

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:

Equal String References

Equal String References

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:

String is Immutable

String is Immutable

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:

String Constant Pool

String Constant Pool

  • (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.