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.






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:



marți, 10 februarie 2026

Conceptul de încapsularea datelor

Încapsularea este un principiu fundamental al programării orientate pe obiecte (POO).

Ea înseamnă gruparea datelor (atribute) și a metodelor (funcții) într-o clasă, dar mai ales protejarea datelor interne și oferirea unui acces controlat la acestea.

Așa cum se vede și în imagine, ne putem imagina clasa ca o cutie închisă: datele din interior sunt ascunse, dar accesul se face doar prin „uși speciale” controlate  – getter și setter.

Pentru a controla accesul la date și metode, folosim modificatori de acces:

  • public – acces liber, din orice clasă
  • private – acces doar în interiorul clasei
  • protected – acces în interiorul clasei și în același pachet (sau în clase derivate)
  • fără modificator de acces - acces doar în interiorul clasei și în pachetul în care face parte clasa respectivă.

Prin acești modificatori noi decidem cine are voie să vadă sau să modifice datele.

Datele importante sau sensibile trebuie să fie private, pentru a nu fi modificate direct, fie intenționat, fie accidental. Astfel, le protejăm și creștem siguranța informațiilor.

De exemplu:

class ContBancar {
private String numarCont; // acces doar în clasa ContBancar
public String titular; // acces oriunde
protected double sold; // acces în clasa ContBancar și în același pachet
}

Pentru a lucra cu atributele private, folosim metode speciale numite getter și setter pentru a proteja datele și a nu le modifica direct din exteriorul clasei:

  • Getter – citește valoarea unui atribut privat și o returnează.
  • Setter – modifică valoarea unui atribut privat, controlat, numaidecât cu validare.

Astfel:

  • protejăm datele,
  • prevenim valori greșite,
  • menținem codul sigur și corect.

De exemplu:

class ContBancar {
private String numarCont;
public String titular;
protected double sold;

public ContBancar(String numarCont, String titular, double sold) {
this.numarCont = numarCont;
this.titular = titular;
this.sold = sold;
}

// Getter pentru numarCont
public String getNumarCont() {
return numarCont;
}

// Getter pentru sold
public double getSold() {
return sold;
}

// Setter pentru sold cu validare
public void setSold(double suma) {
if(suma >= 0) {
sold = suma;
}
else {
System.out.println("Suma nu poate fi negativa!");
}
}}


Pentru a asigura corectitudinea și siguranța informațiilor, respectăm câteva reguli simple:

  1. Datele importante trebuie să fie private
  2. Accesul se face prin metode publice controlate, getter și setter
  3. Setter-ii permit verificarea și validarea datelor înainte să le modificăm, astfel datele incorecte nu se salvează
  4. Programul poate afișa mesaje de eroare în cazul în care nu este promovată validarea datelor.

Exemplu: 

public class Elev {
    private int nota;

public void setNota(int n) {
if(n >= 1 && n <= 10) {
nota = n;
} else {
System.out.println("Nota trebuie să fie între 1 și 10!");
}
}

public int getNota() {
return nota;
}
}
În acest exemplu, atributul nota este declarat private, deci nu poate fi accesat sau modificat direct din afara clasei. Metoda setNota() este un setter și permite modificarea controlată a notei. Înainte de a salva valoarea, metoda verifică dacă nota este între 1 și 10. Dacă valoarea nu este corectă, nota nu se schimbă și se afișează un mesaj de eroare. Metoda getNota() este un getter și permite citirea valorii notei fără a o modifica. 

Pe scurt, încapsularea ne învață să avem grijă de datele noastre din program, exact ca în viața reală. Nu lăsăm pe oricine să modifice informații importante, ci oferim acces doar prin reguli clare. Folosind modificatori de acces, getter și setter, codul devine mai sigur, mai ordonat și mai ușor de înțeles.

Succes!

❤️