Programy zwykle zawierają dużo literałów String w swoim kodzie. W języku Java te stałe są zbierane w coś o nazwie Tabela ciągów dla zwiększenia wydajności. Na przykład, jeśli używasz ciągu "Name: "
w dziesięciu różnych miejscach, JVM (zazwyczaj) ma tylko jedną instancję tego ciągu i we wszystkich dziesięciu miejscach, w których jest używany, wszystkie odwołania wskazują na to jedno wystąpienie. To oszczędza pamięć.
Ta optymalizacja jest możliwa, ponieważ String
jest niezmienna niezmienna. Gdyby można było zmienić łańcuch, zmiana go o jedno oznaczałoby zmianę również w pozostałych dziewięciu. Dlatego każda operacja, która zmienia łańcuch, zwraca nową instancję. Dlatego, jeśli to zrobić:
String s = "boink";
s.toUpperCase();
System.out.println(s);
drukuje boink
, nie BOINK
.
Teraz jeszcze jedna trudne bit: wiele wystąpień java.lang.String
może punktu do tego samego bazowego char[]
dla swoich danych znakowych, innymi słowy, mogą one być różne widoki na tym samym char[]
, przy użyciu tylko kawałek tablicy. Ponownie optymalizacja wydajności. Metoda substring()
jest jednym z przypadków, w których tak się dzieje.
s1 = "Fred47";
//String s1: data=[ 'F', 'r', 'e', 'd', '4', '7'], offset=0, length=6
// ^........................^
s2 = s1.substring(2, 5);
//String s2: data=[ 'F', 'r', 'e', 'd', '4', '7'], offset=2, length=3
// ^.........^
// the two strings are sharing the same char[]!
W swojej SCJP pytanie, to wszystko sprowadza się do:
- Ciąg
"Fred"
pobierany jest z tabeli String.
- Ciąg
"47"
pochodzi z tabeli Ciąg.
- Łańcuch
"Fred47"
jest tworzony podczas wywołania metody.// 1
- Łańcuch
"ed4"
jest tworzony podczas wywołania metody mają ten sam układ spodniego "Fred47"
// 2
- Łańcuch
"ED4"
utworzonego podczas wywołania metody. // 3
s.toString()
nie tworzy nowego, po prostu zwraca this
.
Jeden ciekawy przypadek krawędź tego wszystkiego: rozważyć, co się stanie, jeśli masz naprawdę długi ciąg, na przykład, strona internetowa pobrane z Internetu, powiedzmy długość char[]
jest dwa megabajty. Jeśli weźmiesz substring(0, 4)
z tego, otrzymasz nowy Ciąg, który wygląda, jakby miał tylko cztery znaki, ale nadal udostępnia te dwa megabajty danych z kopii zapasowej. To nie jest tak powszechne w świecie rzeczywistym, ale może to być ogromna strata pamięci! W (rzadkim) przypadku, w którym napotkasz to jako problem, możesz użyć new String(hugeString.substring(0, 4))
, aby utworzyć ciąg z nową, małą tablicą backingową.
Wreszcie, możliwe jest wymuszenie String na tabeli strun w czasie wykonywania, dzwoniąc pod numer intern()
. Podstawowa zasada w tym przypadku: nie rób tego. Rozszerzona reguła: nie rób tego, chyba że użyłeś profilera pamięci, aby upewnić się, że jest to użyteczna optymalizacja.
sobie wyobrazić, kompilator będzie inline pierwsze dwa oświadczenia i usunąć ostatni (redundantne) wywołanie metody. To pozostawia Ci "Fred47", "ed4", "ED4". –
@Jared 's' nie jest wyrażeniem stałym * w czasie kompilacji *, więc tak się nie dzieje. (Skompiluj kod i użyj 'javap -c' lub nawet' struny'.) –
możliwy duplikat [Java - ile obiektów typu String?] (Http://stackoverflow.com/questions/17898236/java-how-many -string-objects) –