miercuri, 25 februarie 2026

Clasa ArrayList. Crearea listelor dinamice de obiecte

După ce am învățat să lucrăm cu vectori de obiecte, este momentul să facem un pas mai departe. Până acum am creat structuri de tipul:  

Elev[] elevi = new Elev[30];

Unde
Elev[] înseamnă un vector care poate stoca obiecte de tip Elev;
30 reprezintă numărul maxim de elemente;
fiecare poziție din vector reține un obiect.

Această metodă este corectă și foarte importantă pentru a înțelege cum funcționează memoria și organizarea datelor. 
Dar în aplicarea în practică, uneori această metodă este ineficientă. 
De ce?  Pentru că așa tip de vector are o dimensiune statică, în sens că dimensiunea acestuia este fixă, iar în cazul exemplului de mai sus nu vom putea adăuga al 31-lea elev, ba mai mult dacă inserăm în listă doar 10 elevi, restul pozițiilor rămân neutilizate. Ștergerea unui element necesită mutarea manuală a celorlalte.

În aplicațiile reale, de cele mai multe ori nu știm din start câte obiecte vom avea. Nu avem cum să cunoaștem câte persoane se vor înscrie la un concurs, câte persoane își vor crea conturi pe platforma educațională a instituției sau câți abiturienți vor depune dosarul pentru a participa la concursul de admitere. Respectiv, este nevoie de o structură dinamică dimensiunea căreia să fie ajustabilă.

Una din soluțiile care le oferă limbajul Java este utilizarea clasei ArrayList din pachetul java.util.  
Un ArrayList este un vector dinamic. Aceasta înseamnă că dimensiunea se modifică automat, avem posibilitatea de a adăuga elemente fără limită fixă, iar ștergerea elementelor este simplificată. 

După cum cunoaștem ca să creăm listă dinamică este necesar de instanțiat clasa ArrayList , deci trebuie să îi cunoaștem constructorii: 
ArrayList() Construiește o listă fără nici un element având capacitatea inițială de 10 elemente. Dacă se încearcă adăugarea a mai mult de 10 elemente, dimensiunea listei se va modifica automat.
ArrayList(Collection c) Construiește o listă ce conține elementele conținute de colecția primită ca argument. Capacitatea inițială este cu 10 procente mai mare decât numărul de elemente din colecția primită ca argument
ArrayList(int intialCapacity) Construiește o listă vidă cu o capacitate specificată de parametrul intialCapacity

Deci pentru a crea un ArrayList respectăm sintaxa: 
ArrayList<Elev> listaElevi = new ArrayList<>();

După cum observați dimensiunea listei nu o mai specificăm, așa cum aceasta crește automat. 

Pentru a putea opera cu elementele din listă vom trebuie să cunoaștem metodele clasei ArrayList și parametrii acestora:
 
1. add(E e)- adaugă la sfârșitul listei obiectul e
ArrayList<String> lista = new ArrayList<>();
lista.add("Ana"); // adaugă "Ana" în listă
lista.add("Ion"); // adaugă "Ion" în listă

2. add(int index, E e) - adaugă obiectul e în listă începând cu poziția index, mutând elementele de după el.
lista.add(1, "Maria"); // inserează "Maria" pe poziția 1

3. get(int index) - returnează elementul de la poziția indicată prin parametrul index
String nume = lista.get(0); // returnează "Ana"

4. set (int index, E e)- Înlocuiește elementul de pe poziția index cu noul element, sau obiect e
lista.set(0, "Andreea"); // înlocuiește "Ana" cu "Andreea" 

5. remove(int index)- șterge elementul de la poziția indicată și mută elementele următoare.
lista.remove(1); // șterge elementul de pe poziția 1 ("Maria")

6. remove(Object o) - șterge prima apariție a obiectului o din listă.
lista.remove("Andreea"); // șterge "Andreea" din listă

7.
size()- returnează numărul de elemente din listă.
int lungime = lista.size(); // returnează 0 dacă lista este goală

8. isEmpty()- returnează true dacă lista este goală.
if (lista.isEmpty()) {
System.out.println("Lista este goală");}

9. contains(Object o) - verifică dacă lista conține elementul dat.
boolean exista = lista.contains("Ion"); // returnează true dacă "Ion" este în listă

10. clear()- șterge toate elementele din listă.
lista.clear(); // lista devine goală

Crearea unei liste de tip ArrayList care să conțină direct tipuri primitive precum int sau double nu este posibilă, așa cum metodele clasei ArrayList operează cu tip de date referință. 

Totuși, încă de la începutul cursului am învățat că fiecărui tip primitiv din Java îi corespunde o clasă de tip Wrapper.
De exemplu:
tipului int îi corespunde clasa Integer
tipului double îi corespunde clasa Double
tipului char îi corespunde clasa Character
tipului boolean îi corespunde clasa Boolean

Astfel, dacă dorim o listă de numere întregi, vom scrie:
ArrayList<Integer> numere = new ArrayList<>();
numere.add(10);
numere.add(25);

Observați că folosim Integer, nu int.
Java face automat conversia dintre tipul primitiv și obiectul corespunzător. Acest mecanism se numește autoboxing (transformarea automată a unui tip primitiv într-un obiect Wrapper) și unboxing (transformarea inversă).

În exemplele de mai sus, elementele stocate în ArrayList sunt de tip String, pentru a simplifica înțelegerea. În practică însă, lista va conține, de cele mai multe ori, obiecte ale claselor create de programator, cum ar fi Client, Produs, Comandă sau alte tipuri definite în aplicație.

Să vedem în următorul exemplu cum este posibil acest lucru. 
Fie ca avem clasa Elev cu următorul cod sursă:
 
class Elev{
private String nume;
private int grupa;
 
public Elev(String nume, int grupa) {
this.nume = nume;
this.grupa = grupa;
}
public String getNume() {
return nume; }
 
public void setNume(String nume) {
this.nume = nume;
}
 
public int getGrupa() { return grupa; }
 
public void setGrupa(int grupa) {
this.grupa = grupa;
}} 

Următorul exemplu va crea o listă de elevi și va parcurge elementele acesteia folosind instrucțiunea for clasică cu index:

import java.util.*;
class TestCollection1{
    public static void main(String args[]){
        ArrayList<Elev> lista=new ArrayList<Elev>();
        lista.add(new Elev("Ana", 1001));
        lista.add(new Elev("Maria", 1002));
        lista.add(new Elev("Ion", 1001));
        lista.add(new Elev("Mihai", 1002));
        for (int i = 0; i < lista.size(); i++) {
            Elev element = lista.get(i);
            System.out.println(element.getNume()+
                    " "+element.getGrupa());}
    }}

O altă modalitate de a parcurge lista este instrucțiunea for îmbunătățit, dar ... noi deja cunoaștem în ce context o folosim 👌

import java.util.*;
class TestCollection1{
    public static void main(String args[]){
         ArrayList<Elev> lista=new ArrayList<Elev>();
         lista.add(new Elev("Ana", 1001));
         lista.add(new Elev("Maria", 1002));
         lista.add(new Elev("Ion", 1001));
         lista.add(new Elev("Mihai", 1002));
         for (Elev element : lista) {
             System.out.println(element.getNume()+
             " "+element.getGrupa());}

    }}


Voi prezenta mai jos o metodă de parcurgere a listei destul de des folosită în proiecte reale: 
import java.util.*;
class TestCollection1{
    public static void main(String args[]){
        ArrayList<Elev> lista=new ArrayList<Elev>();
        lista.add(new Elev("Ana", 1001));
        lista.add(new Elev("Maria", 1002));
        lista.add(new Elev("Ion", 1001));
        lista.add(new Elev("Mihai", 1002));
        lista.forEach(element -> {
            System.out.println(element.getNume()+
                    " "+element.getGrupa());
        });
    }}

Metoda
forEach(element -> { ... }) din ultimul exemplu folosește o expresie lambda pentru parcurgerea listei.  Expresiile lambda sunt o funcționalitate în limbaj introdusă în Java8. Tradus din limbaj Java în limbaj omenesc ar însemna: ,,pentru fiecare element din listă, execută instrucțiunile din bloc”.

Sunt și alte metode de parcurgere și modificare a listelor de obiecte cum ar fi interfața Iterator, Interfața Enumeration, dar vom discuta despre ele într-o altă postare.

Succes!
❤️

luni, 23 februarie 2026

Expresii regulate. Validarea șirurilor de caractere

În Java, o expresie regulată, numită regex, este un model de verificare a unui text. Ea ne permite să controlăm dacă un șir respectă o anumită regulă sau nu. 
De exemplu putem valida dacă șirul introdus de utilizator este: email, număr de telefon, parolă, cod poștal, doar cifre, doar litere, etc

Pentru aceasta se folosesc clasele din pachetul java.util.regex
import java.util.regex.Pattern;
import java.util.regex.Matcher;
Dar un mod mai simplu de a valida un șire de caractere este metoda matches() a clasei String care are forma:
String.matches("regex");
"regex" este o expresie regulată, adică un model (regulă) după care verificăm textul, cu alte cuvinte anume acesta este șablonul după care validăm șirul de caractere. 
Pentru a construi șablonul trebuie să cunoaștem simbolurile cheie: 

Vom examina în continuare mai multe exemple practice. 

Exemplul următor va valida dacă șirul de caractere conține doar cifre. Expresia regulată în acest caz va conține \\d,  adică doar cifre, și + ceea ce înseamnă una sau  mai multe apariții, deci verificăm dacă în șirul de caractere apare una sau mai multe cifre: 
class Testare{
public static void main(String [] args){
String text = "12345";
if (text.matches("\\d+")) {
System.out.println("Codul este valid!");}
else
System.out.println("Codul trebuie să conțină doar cifre!");
}}

O astfel de verificare este frecvent utilizată în aplicații reale: validarea unui cod numeric, a unui ID, a unui PIN sau a unui număr de ordine.

De exemplu cunoaștem că un code PIN are 4 cifre, deci expresia care va valida dacă șirul de caractere este un cod corecte va conține {} și acesta este: 
class Testare{
public static void main(String [] args){
String pin = "2255";
if (pin.matches("\\d{4}")) {
System.out.println("Codul pin valid");
}
}}

Exemplul următor va valida dacă șirul de caractere conține doar litere. Expresia regulată în acest caz va conține [a-zA-Z],  adică doar litere mari și mici, și semnul + ceea ce înseamnă una sau  mai multe apariții, deci verificăm dacă în șirul de caractere apare una sau mai multe litere, fie litere mari, fie litere mici: 
class Testare{
public static void main(String [] args){
String text = "ProgramareJava";
if (text.matches("[a-zA-Z]+")) {
System.out.println("Valid - doar litere");
}
}}

Următorul exemplu verifică dacă șirul de caractere va conține cel puțin o literă acesta nu va promova validarea: 
class Testare{
 public static void main(String [] args){
     String text = "1F2345";
     if (text.matches(".*[a-zA-Z].*")) {
         System.out.println("Codul este valid!");}
     else System.out.println("Codul trebuie să conțină cel puțin o literă!");
 }}

În cazul condițiilor combinate este necesar de utilizat simbolurile:
^  - începutul șirului
$  - sfârșitul șirului

De exemplu, vom verifica dacă șirul conține litere și cifre: 
class Testare{
public static void main(String [] args){
String text = "Java2026";
if (text.matches("^[a-zA-Z0-9]+$")) {
System.out.println("Valid - doar litere si cifre");
}
else {
System.out.println("Invalid");
}
}}

De asemenea, în condițiile combinate se folosește (?=…), numit lookahead, care verifică o condiție fără să „consume” textul, adică se uită dacă regula este respectată, dar nu modifică șirul și nu utilizează caracterele verificate, nu-i modifică dimensiunea, astfel încât restul expresiei să poată continua să analizeze întreg șirul.

Următoarele două exemple evidențiază utilitatea lookahead-ului în condiții combinate, dorim să validăm dacă parola conține cel puțin o cifră și minim 6 caractere : 

Exemplu fără lookahead
class Testare{
public static void main(String [] args){
String parola = "abc123";
// Regex fără lookahead
// Caută să înceapă cu o cifră, apoi 5 sau mai multe caractere
if (parola.matches("\\d.{6,}")) {
System.out.println("Parola este validă");
}
else { System.out.println("Parola NEVALIDĂ");}
}}


Deși parola conține cel puțin o cifră și minim 6 caractere, aceasta nu trece testul de validare. Este din motiv că regex fără lookahead caută să înceapă cu o cifră, în caz contrar rezultatul este false.

Exemplu cu lookahead
class Testare{
public static void main(String [] args){
String parola = "abc123";
if (parola.matches("^(?=.*\\d).{6,}$")) {
System.out.println("Parola este validă");
}
else { System.out.println("Parola NEVALIDĂ");}
}}

Exemplu în care vom verifica dacă utilizatorul indică un email corect: 

class Testare{
public static void main(String [] args){
String email = "test@gmail.com";
if (email.matches("^[A-Za-z0-9+_.-]+@(.+)$")) {
System.out.println("Email valid");
}
else { System.out.println("Email nu este valid");}
}}

Exemple de expresii regex și destinația acestora:

.matches("^[a-zA-Z0-9_]+$") - verifică dacă expresia conține litere, cifre și underscore, util pentru validare username


.matches("^\\d{6}$") - verifică dacă șase cifre, util pentru verificarea unui cod poștal

.matches("^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).{8,}$") - verifică dacă șirul de caractere conține cifre, litere mici, litere mari, util pentru verificarea unei parole mai complexe

.matches("^[A-Za-z].*$") - verifică dacă șirul de caractere începe cu o litera

.matches(".*\\d$") - verifică dacă șirul de caractere finisează cu o cifră

.matches("^\\d+(\\.\\d+)?$") - verifică dacă șirul de caractere  este un număr zecimal. Va accepta de exemplu 10, la fel și 10.5. Nu va accepta litere

.matches("^[A-Za-z][A-Za-z0-9_]*$") - verifică dacă șirul de caractere  începe cu o literă după care urmează litere, cifre sau semnul underscore

Expresiile regulate sunt foarte utile în programare, oferind o metodă rapidă și precisă pentru verificarea și manipularea șirurilor de caractere, astfel asigurând securitatea datelor cu care lucrează aplicația. 

Îți dorești un anumit conținut să apară în această postare? 
Vezi meniul 👉 CONTACTE 
Aștept un e-mail 💬

Succes!
❤️

luni, 16 februarie 2026

Set de probleme. Clasa StringBuilder. Clasa StringBuffer


1. Scrieți un program în care creați un obiect StringBuffer și un obiect StringBuilder cu textul "Salut" la care adăugați folosind append() numele dvs și un semn de punctuație. La final, afișați rezultatul pentru ambele obiecte. Rezultat așteptat: "Salut, [numele tău]!"

2. Scrieți un program în care creați un obiect StringBuffer și un obiect StringBuilder cu textul "Salut dragă lume", ștergeți cuvântul "dragă " folosind delete(). Afișați rezultatul pentru ambele clase. Rezultat așteptat: "Salut lume"

3. Scrieți un program în care creați un obiect StringBuffer și un obiect StringBuilder cu textul "Salut lume!". Folosiți deleteCharAt() pentru a elimina semnul de exclamare, aplicând metoda atât pe StringBuffer, cât și pe StringBuilder. Rezultat așteptat: "Salut lume"

4. Scrieți un program în care creați un obiect StringBuffer și un obiect StringBuilder cu textul "Buna ziua". Înlocuiți "ziua" cu "seara" folosind replace(). Afișați rezultatul pentru ambele clase. Rezultat așteptat: "Buna seara"

5. Scrieți un program în care creați un obiect StringBuffer și un obiect StringBuilder cu textul "Java". Folosiți reverse() pentru a inversa șirul, aplicând metoda atât pe StringBuffer, cât și pe StringBuilder. Rezultat așteptat: "avaJ"

6. Scrieți un program în care creați un obiect StringBuffer și un obiect StringBuilder cu textul "Salut lume. Eu programez în Java". Afișați caracterul aflat la poziția pe o anumită poziție folosind metoda charAt() pentru ambele clase.

7. Scrieți un program în care creați un obiect StringBuffer și un obiect StringBuilder cu textul "salut lume". Folosiți metoda setCharAt() pentru a schimba prima literă în majusculă, atât pentru StringBuffer, cât și pentru StringBuilder. Rezultat așteptat: "Salut lume"

8. Scrieți un program în care creați un obiect StringBuffer și un obiect StringBuilder cu textul "Programare Java". Extrageți cu ajutorul metodei substring() doar cuvântul "Java" pentru ambele clase.

9. Scrieți un program în care creați un obiect StringBuffer și un obiect StringBuilder în baza unui text citit de la tastatură. Afișați la ecran, folosind metoda length() câte caractere are textul citit.

10. Scrieți un program în care creați un obiect StringBuffer și un obiect StringBuilder cu textul "Salut". Afișați capacitatea inițială. Apoi adăugați mai multe caractere. Afișați capacitatea după modificarea textului. Observați cum se schimbă capacitatea pentru ambele clase.

11. Scrieți un program în care creați un obiect StringBuffer și un obiect StringBuilder cu textul "Invat programare". Introduceți cuvântul „Java ” după cuvântul „Invat ” folosind metoda insert(). Adăugați punct la final folosind metoda append(). Afișați rezultatul. Încercați să utilizați apelul în lanț al metodelor.

12. Un elev a stocat o listă de teme pentru acasă într-un șir de caractere:

tema1: matematica, tema2: informatica, tema3: fizica

Scrieți un program în care să utilizați StringBuilder sau StringBuffer pentru a obține la afișare:

TEMA1: MATEMATICA | TEMA2: INFORMATICA | TEMA3: FIZICA

Pentru aceasta:
- Transformă fiecare temă în majuscule.
- Înlocuiește virgulele cu simbolul |.
- Afișează rezultatul final.

13. Un magazin online a stocat titlurile comenzilor într-un șir de caractere:

comanda1: telefon, comanda2: laptop, comanda3: tableta

Scrieți un program în care să utilizați fie StringBuilder, sau StringBuffer pentru a obține la afișare:

Comanda3: TABLETA | Comanda2: LAPTOP | Comanda1: TELEFON

Pentru aceasta:
- Eliminați spațiile inutile de la început și sfârșit folosind trim().
- Inversați ordinea comenzilor folosind reverse().
- Transformați numele produselor în majuscule.
- Înlocuiți virgulele cu simbolul |.


Succes!
💓

duminică, 15 februarie 2026

Șiruri de caractere. Clasa StringBuilder. Clasa StringBuffer

În postările anterioare am discutat despre șiruri de caractere și clasa String, știm deja că această clasă este imutabilă, adică orice modificare creează un obiect nou în memorie. Acest lucru poate deveni ineficient atunci când lucrăm cu volume mari de text sau efectuăm modificări repetate asupra șirurilor de caractere.

Astăzi discutăm despre două clase care rezolvă această problemă: StringBuilder și StringBuffer
Deci și aceste clase Java permit prelucrarea șirurilor de caractere. Să vedem care totuși diferența între ele: 
1. String - este imutabil. Fiecare modificare creează un obiect nou. Este mai lent în concatenări repetate.
2. StringBuilder - este mutabil. Fiecare modificare efectuată asupra șirului modifică obiectul curent. Este mai rapid și mai recomandat în majoritatea aplicațiilor.
3. StringBuffer - este mutabil. Metodele sale sunt sincronizate și este recomandat pentru aplicații multifir. Este mai lent decât StringBuilder.

Deci, clasa String pune accent pe siguranță prin imutabilitate. Clasa StringBuilder pune accent pe performanță, iar clasa StringBuffer pune accent pe siguranță în aplicații concurente. 
Reeșind din contextul problemei de rezolvat, fiecare programator decide care clasă este potrivită de utilizat.

Vom analiza metodele claselor StringBuilder și StringBuffer

a) append(textNou) - adaugă un text la sfârșitul unui șir de caractere.

Exemplu: 
public class StringBufferReader {
public static void main(String[] args) {
String textInitial = "Salut!";
StringBuffer buffer = new StringBuffer();
buffer.append(textInitial);
buffer.append(" Cum ești?");
System.out.println("StringBuffer: " + buffer);

StringBuilder builder = new StringBuilder();
builder.append(textInitial);
builder.append(" Sper că bine!");
System.out.println("StringBuilder: " + builder);
}}

b) insert(index, text) - permite să adaugi text într-o anumită poziție, nu doar la sfârșit.
Exemplu:
public class StringBufferReader {
public static void main(String[] args) {
String textInitial = "Salut lume!";

StringBuffer buffer = new StringBuffer(textInitial);
buffer.insert(6, "frumoasă ");
System.out.println("StringBuffer: " + buffer);

StringBuilder builder = new StringBuilder(textInitial);
builder.insert(6, "dragă ");
System.out.println("StringBuilder: " + builder);
}}

c) delete(start, end) - permite ștergerea unei secvențe de caractere dintre două poziții, începând de la start până la end - 1.
Exemplu:
public class StringBufferReader {
public static void main(String[] args) {
String textInitial = "Salut frumoasă lume!";

StringBuffer buffer = new StringBuffer(textInitial);
buffer.delete(6, 15); // șterge caracterele de la index 6 până la 14
System.out.println("StringBuffer după delete: " + buffer);

StringBuilder builder = new StringBuilder(textInitial);
builder.delete(5, 14); // șterge caracterele de la index 5 până la 13
System.out.println("StringBuilder după delete: " + builder);
}}

d) deleteCharAt(int index) - șterge un singur caracter aflat la poziția indicată.
Exemplu:
public class StringBufferReader {
public static void main(String[] args) {
String textInitial = "Salut lume!";

StringBuffer buffer = new StringBuffer(textInitial);
buffer.deleteCharAt(5); // șterge caracterul de la index 5 (spațiul)
System.out.println("StringBuffer după deleteCharAt: " + buffer.toString());

StringBuilder builder = new StringBuilder(textInitial);
builder.deleteCharAt(0); // șterge primul caracter 'S'
System.out.println("StringBuilder după deleteCharAt: " + builder.toString());
}}

e) replace(start, end, textNou) - adaugă text la sfârșitul unui șir de caractere.

Exemplu:
public class StringBufferReader {
public static void main(String[] args) {
String textInitial = "Salut lume!";

StringBuffer buffer = new StringBuffer(textInitial);
buffer.replace(6, 10, "univers"); // înlocuiește "lume" cu "univers"
System.out.println("StringBuffer după replace: " + buffer.toString());

StringBuilder builder = new StringBuilder(textInitial);
builder.replace(0, 5, "Bună"); // înlocuiește "Salut" cu "Bună"
System.out.println("StringBuilder după replace: " + builder.toString());
} }

f) reverse() inversează toate caracterele din obiectul respectiv.
Exemplu: 
public class StringBufferReader  {
public static void main(String[] args) {

StringBuffer buffer = new StringBuffer("Salut lume");
buffer.reverse();
System.out.println("StringBuffer după reverse: " + buffer.toString());

StringBuilder builder = new StringBuilder("Java este frumos");
builder.reverse();
System.out.println("StringBuilder după reverse: " + builder.toString());
}}

g) length() returnează numărul de caractere din obiectul respectiv. 
Exemplu: 
public class StringBufferReader{
public static void main(String[] args) {

StringBuffer buffer = new StringBuffer("Salut lume!");
int lungimeBuffer = buffer.length();
System.out.println("Lungimea StringBuffer: " + lungimeBuffer);


StringBuilder builder = new StringBuilder("Java este frumos");
int lungimeBuilder = builder.length();
System.out.println("Lungimea StringBuilder: " + lungimeBuilder);
}}

h) capacity() - arată cât spațiu intern este alocat pentru caractere, adică cât
text poate conține fără să fie nevoie să realoce memorie. Nu este lungimea textului,
spațiul intern disponibil. Dacă capacitatea este depășită, obiectul mărește automat
spațiul intern pentru a stoca caracterele suplimentare, fără a pierde date.

Exemplu: 
public class StringBufferReader{
public static void main(String[] args) {

StringBuffer buffer = new StringBuffer("Salut lume!");
System.out.println("Capacity StringBuffer: " + buffer.capacity());

StringBuilder builder = new StringBuilder("Java este frumos");
System.out.println("Capacity StringBuilder: " + builder.capacity());
}}
j) charAt(int index) - returnează caracterul aflat la poziția specificată în șir.
Exemplu:
public class StringBufferReader {
public static void main(String[] args) {
StringBuilder builder = new StringBuilder("Salut lume!");

char c = builder.charAt(0); // primul caracter
System.out.println("Caracterul de la index 0: " + c);

char d = builder.charAt(6); // caracterul de la index 6
System.out.println("Caracterul de la index 6: " + d);
}}
k) setCharAt(int index, char c) - permite să înlocuiți caracterul de la o poziție
specifică cu un alt caracter.
Exemplu:

public class StringBufferReader{
public static void main(String[] args) {
StringBuffer buffer = new StringBuffer("Salut lume!");
buffer.setCharAt(0, 's'); // schimbăm 'S' în 's'
buffer.setCharAt(6, 'L'); // schimbăm 'l' în 'L'
System.out.println("StringBuffer după setCharAt: " + buffer);

StringBuilder builder = new StringBuilder("Java este frumos");
builder.setCharAt(0, 'j'); // schimbăm 'J' în 'j'
builder.setCharAt(5, 'E'); // schimbăm 'e' în 'E'
System.out.println("StringBuilder după setCharAt: " + builder);
}}


k)
substring(int start) și substring(int start, int end) - permite
să extragi o porțiune de text din obiectul respectiv, fără să îl modifici.
Exemplu:
public class StringBufferReader {
public static void main(String[] args) {

StringBuffer buffer = new StringBuffer("Salut lume!");
String subBuffer = buffer.substring(6); // din index 6 până la final
String subBuffer2 = buffer.substring(0, 5); // din index 0 până la 4
System.out.println("StringBuffer substring 6: " + subBuffer);
System.out.println("StringBuffer substring 0-5: " + subBuffer2);

StringBuilder builder = new StringBuilder("Java este frumos");
String subBuilder = builder.substring(5); // din index 5 până la final
String subBuilder2 = builder.substring(0, 4); // din index 0 până la 3
System.out.println("StringBuilder substring 5: " + subBuilder);
System.out.println("StringBuilder substring 0-4: " + subBuilder2);
}}
l) toString() - returnează conținutul obiectului ca un String normal, permițând să îl folosești oriunde este nevoie de un șir de caractere.
Exemplu:
public class StringBufferReader {
public static void main(String[] args) {

StringBuffer buffer = new StringBuffer("Salut lume!");
String textBuffer = buffer.toString();
System.out.println("StringBuffer toString: " + textBuffer);

StringBuilder builder = new StringBuilder("Java este frumos");
String textBuilder = builder.toString();
System.out.println("StringBuilder toString: " + textBuilder);
}}


Nu pot să nu subliniez că, datorită caracterului lor mutabil, clasele StringBuilder și StringBuffer permit apelul în lanț al metodelor, astfel încât mai multe operații de manipulare a textului pot fi combinate într-un singur bloc de cod, obținând rapid și elegant rezultatul dorit.

Exemplu:
public class StringBufferReader {
public static void main(String[] args) {

StringBuffer buffer = new StringBuffer("Buna");
buffer.append(" dragi") // adaugă text
.insert(5, " ziua ") // inserează " ziua" la poziția 5
.append(" elevi!") // adaugă la final
.replace(0, 4, "Salut"); // înlocuiește "Buna" cu "Salut"

System.out.println("StringBuffer: " + buffer);

StringBuilder builder = new StringBuilder("Buna");
builder.append(" dimineața") // adaugă text
.insert(4, " tuturor") // inserează " tuturor" la poziția 4
.append("!") // adaugă semn de punctuație
.replace(0, 4, "Salut"); // înlocuiește "Buna" cu "Salut"

System.out.println("StringBuilder: " + builder);
}
}

În același timp, clasa String nu permite apelul în lanț în același mod ca StringBuilder sau StringBuffer, și iată de ce:
a) Obiectele
String sunt imutabile.
b) Orice metodă care modifică textul (ex:
replace, substring, concat) nu schimbă obiectul original, ci returnează un obiect String nou.

Deși poți scrie apeluri în lanț cu String, fiecare apel creează un nou obiect în memorie, ceea ce este mai puțin eficient comparativ cu StringBuilder sau StringBuffer.

Exemplu
public class StringBufferReader {
public static void main(String[] args) {
String text = "Salut lume";
text.replace("Salut", "Buna")
.toUpperCase()
.concat("!");
System.out.println(text); // Salut lume
}}

Observați că, deși am încercat să aplicăm metodele în lanț pe un obiect String, rezultatul afișat nu s-a schimbat, deoarece String este imutabil. În schimb, clasele StringBuilder și StringBuffer, fiind mutabile, permit modificarea directă a obiectului și suportă apelul în lanț al metodelor, oferind rezultate imediate și eficiente.

Vom reface exemplul de mai sus astfel încât, totuși să vedem cum este posibil apelul în lanț în cazul clasei String.

Exemplu:
public class StringBufferReader {
public static void main(String[] args) {
String text = "Salut lume";
String textNou = text.replace("Salut", "Buna")
.toUpperCase()
.concat("!");
System.out.println(textNou);
}
}


După cum observăm în acest exemplu, pentru a obține efectul dorit în cazul clasei String, trebuie să salvăm rezultatul fiecărei metode într-o variabilă nouă, deoarece obiectul original rămâne neschimbat.

Clasele StringBuilder și StringBuffer oferă funcții diverse pentru manipularea șirurilor de caractere, fiecare cu avantajele sale, iar pentru o înțelegere rapidă și clară, tabelul comparativ de mai jos sintetizează caracteristicele principale ale acestor două clase și ne vor ajuta să înțelegem care este clasa potrivită de prelucrare a șirurilor de caractere pentru o anumită situație:


Succes!
❤️