Niezmienność String i pula stringów
Poznaj przyczyny niezmienności String, działanie puli stringów oraz sytuacje, w których należy użyć == zamiast equals.
Niezmienność String i pula stringów to bezpłatna lekcja Java Academy na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Java Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Java Academy zawiera 4 lekcji w sumie.
Niezmienność obiektów String i pula Stringów
Łańcuchy znaków są w Javie niezmienne. Zrozumienie niezmienności, puli Stringów oraz różnicy między równością referencji a równością wartości pomaga uniknąć trudnych do wykrycia błędów.
Dlaczego łańcuchy znaków są niezmienne
Obiektów String nie można modyfikować po ich utworzeniu. Każda operacja, która „zmienia” obiekt String, w rzeczywistości tworzy nowy obiekt String. Umożliwia to współdzielenie, bezpieczeństwo wątkowe i buforowanie wartości hashCode.
String s = "hello";
s.toUpperCase(); // creates a new String, does NOT modify s
System.out.println(s); // still "hello"
String upper = s.toUpperCase(); // must capture the result
System.out.println(upper); // HELLOPula Stringów (internowanie)
Literały String są przechowywane w puli Stringów (specjalnej części sterty). Dwa literały o tej samej zawartości współdzielą ten sam obiekt.
String a = "hello"; // goes into pool
String b = "hello"; // same object from pool
String c = new String("hello"); // forces new object outside pool
System.out.println(a == b); // true (same pool reference)
System.out.println(a == c); // false (different objects)
System.out.println(a.equals(c)); // true (same content)== a equals()
Do porównywania zawartości obiektów String należy zawsze używać .equals(). Operator == sprawdza, czy dwie zmienne wskazują ten sam obiekt (równość referencji).
String input = new String("admin"); // from user input, not pooled
String role = "admin";
// Dangerous: fails because different objects
if (input == role) System.out.println("Same"); // not printed!
// Correct: compares content
if (input.equals(role)) System.out.println("Equal!"); // prints
// Null-safe: put constant first to avoid NPE
if ("admin".equals(input)) System.out.println("Admin"); // printsintern(): ręczne umieszczanie String w puli
String.intern() wymusza umieszczenie obiektu String w puli i zwraca referencję do obiektu znajdującego się w puli. We współczesnej Javie jest rzadko potrzebna.
String s1 = new String("interned");
String s2 = s1.intern();
String s3 = "interned"; // already in pool
System.out.println(s2 == s3); // true (both point to pool)
System.out.println(s1 == s3); // false (s1 is off-pool)
// Modern use: prefer String.intern() only when you need
// identity-based caching with thousands of repeated stringsZachowanie konkatenacji String
Konkatenacja za pomocą + tworzy nowe obiekty String. Wewnątrz pętli ma złożoność O(n²). Kompilator optymalizuje pojedyncze wyrażenia, ale nie konkatenację wykonywaną w pętli.
// Compiler optimizes this to a StringBuilder automatically:
String result = "Hello, " + "World" + "!";
// In loops: compiler does NOT optimize — use StringBuilder explicitly!
List<String> words = List.of("a", "b", "c", "d");
String bad = ""; // O(n^2) — new object each iteration
for (String w : words) bad += w;
StringBuilder sb = new StringBuilder();
for (String w : words) sb.append(w); // O(n) — efficient
String good = sb.toString();
System.out.println(good); // abcdPrawidłowe porównywanie łańcuchów znaków
Należy zawsze używać equals(); do porównywania bez uwzględniania wielkości liter należy używać equalsIgnoreCase(). compareTo() służy do porządku leksykograficznego.
String a = "Java";
String b = "java";
System.out.println(a.equals(b)); // false
System.out.println(a.equalsIgnoreCase(b)); // true
System.out.println(a.compareToIgnoreCase(b)); // 0 (equal)
System.out.println(a.compareTo(b)); // negative (J < j in Unicode)Buforowanie hashCode obiektów String
Ponieważ łańcuchy znaków są niezmienne, Java buforuje wartość hashCode po pierwszym wywołaniu. Kolejne wywołania zwracają wartość z bufora, dzięki czemu łańcuchy znaków są bardzo wydajnymi kluczami HashMap.
String key = "product:12345";
// hashCode computed once and cached:
int h1 = key.hashCode();
int h2 = key.hashCode(); // returns cached value
System.out.println(h1 == h2); // true
// Strings are ideal HashMap keys because:
// 1. hashCode is consistent (immutable content)
// 2. hashCode is cached (fast repeated lookups)
// 3. equals is well-defined (content-based)
Map<String, Integer> map = new HashMap<>();
map.put("key", 42);
System.out.println(map.get("key")); // 42String jest final
Klasa String jest oznaczona jako final — nie można po niej dziedziczyć. Gwarantuje to, że żadna podklasa nie naruszy niezmienności ani spójności hashCode.
// String is final — this will not compile:
// class MutableString extends String {} // compile error
// You cannot override String behavior.
// If you need custom string-like behavior, use a wrapper:
record ProductSku(String value) {
ProductSku { if (!value.matches("[A-Z]{3}-[0-9]{4}")) throw new IllegalArgumentException(); }
@Override public String toString() { return value; }
}String.format a konkatenacja
W przypadku łańcuchów znaków składających się z wielu części, ze względu na czytelność i łatwiejszą lokalizację należy preferować String.format() lub bloki tekstowe zamiast konkatenacji.
String name = "Alice";
int age = 30;
double score = 97.5;
// Concatenation — harder to read with many parts
String bad = "User: " + name + ", Age: " + age + ", Score: " + score;
// String.format — clear placeholders
String good = String.format("User: %s, Age: %d, Score: %.1f", name, age, score);
System.out.println(good);
// User: Alice, Age: 30, Score: 97.5Operacje na łańcuchach znaków bez ryzyka null
Wiele operacji na łańcuchach znaków zgłasza NPE, jeśli łańcuch ma wartość null. Aby bezpiecznie obsługiwać wartość null, należy używać wzorców defensywnych lub String.valueOf().
String value = null;
// Null-safe length check
int len = (value != null) ? value.length() : 0;
System.out.println(len); // 0
// Null-safe comparison (put constant first)
boolean isAdmin = "admin".equals(value); // false, no NPE
// String.valueOf converts null to "null" string
System.out.println(String.valueOf(value)); // "null"
System.out.println(Objects.toString(value, "default")); // "default"Szybkie sprawdzenie
Co wypisuje poniższy kod?
String a = "hello";
String b = new String("hello");
System.out.println(a.equals(b));
System.out.println(a == b);Podsumowanie: niezmienność obiektów String i pula Stringów
Najważniejsze informacje:
- Łańcuchy znaków są niezmienne — operacje zwracają nowe obiekty String i nigdy nie modyfikują oryginalnego obiektu
- Literały String są przechowywane w puli — dwa literały o tej samej zawartości współdzielą jeden obiekt
- Do porównywania zawartości obiektów String należy zawsze używać .equals(); == porównuje referencje do obiektów
- Do porównań bez uwzględniania wielkości liter należy używać equalsIgnoreCase()
- Konkatenacja łańcuchów znaków w pętlach ma złożoność O(n²) — dla większej wydajności należy używać StringBuilder
- Łańcuchy znaków buforują wartość hashCode, dzięki czemu są wydajnymi i bezpiecznymi kluczami HashMap
Często zadawane pytania
Czy lekcja „Niezmienność String i pula stringów” jest bezpłatna?
Tak — pełny tekst „Niezmienność String i pula stringów” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Java Academy, przejdź na CoddyKit PRO. Kurs Java Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Niezmienność String i pula stringów”?
Poznaj przyczyny niezmienności String, działanie puli stringów oraz sytuacje, w których należy użyć == zamiast equals. Ćwiczysz Java Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Java Academy?
Nie wymagamy żadnego doświadczenia. Java Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.
Ile czasu zajmuje lekcja „Niezmienność String i pula stringów”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Java Academy?
Tak. Każda lekcja Java Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Niezmienność String i pula stringów
- StringBuilder do wydajnego łączenia
- Bloki tekstowe i String.format
- Wyrażenia regularne z Pattern i Matcher