Czy łańcuch Java jest naprawdę niezmienny?

399

Wszyscy wiemy, że Stringjest niezmienny w Javie, ale sprawdź następujący kod:

String s1 = "Hello World";  
String s2 = "Hello World";  
String s3 = s1.substring(6);  
System.out.println(s1); // Hello World  
System.out.println(s2); // Hello World  
System.out.println(s3); // World  

Field field = String.class.getDeclaredField("value");  
field.setAccessible(true);  
char[] value = (char[])field.get(s1);  
value[6] = 'J';  
value[7] = 'a';  
value[8] = 'v';  
value[9] = 'a';  
value[10] = '!';  

System.out.println(s1); // Hello Java!  
System.out.println(s2); // Hello Java!  
System.out.println(s3); // World  

Dlaczego ten program działa w ten sposób? I dlaczego jest wartość s1i s2zmieniane, ale nie s3?

Darshan Patel
źródło
394
Możesz robić różne głupie sztuczki z odbiciem. Ale w zasadzie łamiesz naklejkę „gwarancja nieważności, jeśli zostanie usunięta”, gdy tylko to zrobisz.
cHao
16
@DarshanPatel użyj SecurityManagera, aby wyłączyć odbicie
Sean Patrick Floyd
39
Jeśli naprawdę chcesz zadzierać z rzeczami, możesz to zrobić tak, aby zadzierać z (Integer)1+(Integer)2=42buforowanym autoboxingiem; (Disgruntled-Bomb-Java-Edition) ( thedailywtf.com/Articles/Disgruntled-Bomb-Java-Edition.aspx )
Richard Tingle
15
Być może rozbawi cię ta odpowiedź, którą napisałem prawie 5 lat temu stackoverflow.com/a/1232332/27423 - chodzi o niezmienne listy w C #, ale w zasadzie to samo: jak mogę powstrzymać użytkowników przed modyfikowaniem moich danych? Odpowiedź brzmi: nie możesz; odbicie sprawia, że ​​jest to bardzo łatwe. Jednym z głównych języków, w którym ten problem nie występuje, jest JavaScript, ponieważ nie ma systemu odbicia, który może uzyskać dostęp do zmiennych lokalnych w zamknięciu, więc prywatny naprawdę oznacza prywatny (nawet jeśli nie ma dla niego słowa kluczowego!)
Daniel Earwicker
49
Czy ktoś czyta pytanie do końca? Pytanie brzmi: pozwól, że powtórzę: „Dlaczego ten program działa w ten sposób? Dlaczego wartości s1 i s2 zostały zmienione i nie zostały zmienione dla s3?” Pytanie NIE brzmi: dlaczego zmieniono s1 i s2! Pytanie brzmi: DLACZEGO nie zmieniono s3?
Roland Pihlakas

Odpowiedzi:

403

String jest niezmienny *, ale oznacza to tylko, że nie można go zmienić za pomocą jego publicznego interfejsu API.

To, co tu robisz, to obchodzenie normalnego API za pomocą odbicia. W ten sam sposób możesz zmienić wartości wyliczeń, zmienić tabelę odnośników używaną w autoboksowaniu liczb całkowitych itp.

Powodem s1i s2wartością zmiany jest to, że oba odnoszą się do tego samego łańcucha wewnętrznego. Kompilator to robi (jak wspomniano w innych odpowiedziach).

Powodem s3jest nie był faktycznie nieco zaskakujące dla mnie, jak myślałem, że dzielić valuearray ( miało to miejsce w poprzedniej wersji Java , zanim 7u6 Java). Jednak patrząc na kod źródłowy String, widzimy, że valuetablica znaków dla podłańcucha jest faktycznie kopiowana (za pomocą Arrays.copyOfRange(..)). Dlatego pozostaje niezmieniony.

Możesz zainstalować SecurityManager, aby uniknąć złośliwego kodu, aby robić takie rzeczy. Pamiętaj jednak, że niektóre biblioteki zależą od korzystania z tego rodzaju sztuczek refleksyjnych (zazwyczaj narzędzia ORM, biblioteki AOP itp.).

*) Początkowo napisałem, że Stringtak naprawdę nie są niezmienne, tylko „skuteczne niezmienne”. Może to wprowadzać w błąd w obecnej implementacji String, gdzie valuetablica jest rzeczywiście oznaczona private final. Warto jednak zauważyć, że nie ma sposobu, aby zadeklarować tablicę w Javie jako niezmienną, dlatego należy zachować ostrożność, aby nie ujawniać jej poza klasą, nawet przy odpowiednich modyfikatorach dostępu.


Ponieważ ten temat wydaje się niezwykle popularny, oto kilka sugerowanych lektur: Dyskusja Heinza Kabutza Reflection Madness z JavaZone 2009, która obejmuje wiele problemów w OP, wraz z innymi refleksjami ... cóż ... szaleństwem.

Obejmuje to, dlaczego czasami jest to przydatne. I dlaczego w większości przypadków należy tego unikać. :-)

haraldK
źródło
7
W rzeczywistości Stringinternowanie jest częścią JLS ( „literał łańcuchowy zawsze odnosi się do tego samego wystąpienia klasy Łańcuch” ). Ale zgadzam się, że nie jest dobrą praktyką poleganie na szczegółach implementacji Stringklasy.
haraldK,
3
Być może powodem, dla którego substringkopie zamiast używać „sekcji” istniejącej tablicy, jest inaczej, gdybym miał ogromny ciąg si wyjął z niego niewielki podciąg t, a potem porzuciłem, sale utrzymałem t, wtedy ogromna tablica byłaby przy życiu (nie zbierane są śmieci). Więc może bardziej naturalne jest, aby każda wartość łańcucha miała własną powiązaną tablicę?
Jeppe Stig Nielsen
10
Dzielenie się tablicami między ciągiem znaków i jego podciągami oznaczało również, że każda String instancja musiała przenosić zmienne w celu zapamiętania przesunięcia w określonej tablicy i długości. Jest to narzut, którego nie należy ignorować, biorąc pod uwagę całkowitą liczbę łańcuchów i typowy stosunek normalnych łańcuchów do podciągów w aplikacji. Ponieważ musieli być oceniani dla każdej operacji łańcuchowej, oznaczało to spowolnienie każdej operacji łańcuchowej tylko dla korzyści jednej operacji, taniego podłańcucha.
Holger
2
@Holger - Tak, rozumiem, że pole przesunięcia zostało usunięte w ostatnich maszynach JVM. I nawet gdy był obecny, nie był tak często używany.
Hot Licks
2
@ supercat: nie ma znaczenia, czy masz kod macierzysty, czy nie, posiadanie różnych implementacji dla ciągów i podłańcuchów w ramach tej samej maszyny JVM lub posiadanie byte[]ciągów dla ciągów ASCII i char[]innych oznacza, że ​​każda operacja musi sprawdzić, jaki rodzaj ciągu jest wcześniej operacyjny. Utrudnia to wstawianie kodu do metod wykorzystujących ciągi, co jest pierwszym krokiem do dalszych optymalizacji z wykorzystaniem informacji kontekstowej osoby dzwoniącej. To duży wpływ.
Holger
93

W Javie, jeśli dwie pierwotne zmienne łańcuchowe są inicjowane na ten sam literał, przypisuje to samo odwołanie do obu zmiennych:

String Test1="Hello World";
String Test2="Hello World";
System.out.println(test1==test2); // true

inicjalizacja

Z tego powodu porównanie zwraca wartość true. Trzeci ciąg jest tworzony za pomocą, substring()który tworzy nowy ciąg zamiast wskazywać na to samo.

ciąg podrzędny

Gdy uzyskujesz dostęp do ciągu za pomocą odbicia, otrzymujesz rzeczywisty wskaźnik:

Field field = String.class.getDeclaredField("value");
field.setAccessible(true);

Tak więc zmiana na to spowoduje zmianę łańcucha przytrzymującego do niego wskaźnik, ale tak jak s3został utworzony z nowym ciągiem, ponieważ substring()nie zmieni się.

zmiana

Zaheer Ahmed
źródło
Działa to tylko w przypadku literałów i jest optymalizacją czasu kompilacji.
SpacePrez
2
@ Zaphod42 Nieprawda. Możesz także wywoływać internręcznie na nieliterowym ciągu znaków i czerpać korzyści.
Chris Hayes
Uwaga: chcesz korzystać z internrozsądku. Internowanie wszystkiego nie daje wiele korzyści i może być źródłem chwil pochłaniających uwagę, gdy dodasz refleksji do miksu.
cHao
Test1i Test1są niezgodne z test1==test2konwencjami nazewnictwa Java i nie są z nimi zgodne.
c0der
50

Używasz refleksji, aby obejść niezmienność Sznurka - jest to forma „ataku”.

Istnieje wiele przykładów, które możesz stworzyć w ten sposób (np. Możesz nawet utworzyć instancję Voidobiektu ), ale to nie znaczy, że String nie jest „niezmienny”.

Istnieją przypadki użycia, w których ten rodzaj kodu może być wykorzystany na Twoją korzyść i być „dobrym kodowaniem”, na przykład usuwanie haseł z pamięci w najwcześniejszym możliwym momencie (przed GC) .

W zależności od menedżera bezpieczeństwa wykonanie kodu może nie być możliwe.

Czeski
źródło
30

Używasz refleksji, aby uzyskać dostęp do „szczegółów implementacji” obiektu string. Niezmienność jest cechą publicznego interfejsu obiektu.

Ankur
źródło
24

Modyfikatory widoczności i końcowa (tj. Niezmienność) nie są miarą złośliwego kodu w Javie; są jedynie narzędziami służącymi do ochrony przed błędami i ułatwienia zarządzania kodem (jedna z głównych zalet systemu). Dlatego możesz uzyskać dostęp do wewnętrznych szczegółów implementacji, takich jak tablica charcentów dla Strings poprzez odbicie.

Drugim efektem, który widzisz, jest to, że wszystko się Stringzmienia, podczas gdy wygląda na to, że się zmieniasz s1. Jest to pewna właściwość literałów Java String, które są automatycznie internowane, tj. Buforowane. Dwa literały łańcuchowe o tej samej wartości będą faktycznie tym samym obiektem. Gdy utworzysz z newnim Łańcuch, nie zostanie on automatycznie internowany i nie zobaczysz tego efektu.

#substringdo niedawna (Java 7u6) działał w podobny sposób, co wyjaśniałoby zachowanie w oryginalnej wersji twojego pytania. Nie stworzył on nowej tablicy char, ale użył ponownie tej z oryginalnego String; właśnie utworzył nowy obiekt String, który wykorzystał przesunięcie i długość, aby przedstawić tylko część tej tablicy. Ogólnie działało to, ponieważ ciągi są niezmienne - chyba że je obejdziesz. Ta właściwość #substringoznaczała również, że cały oryginalny Ciąg nie mógł zostać wyrzucony przez śmieci, gdy jeszcze utworzono z niego krótszy podciąg.

W obecnej Javie i twojej obecnej wersji pytania nie ma dziwnego zachowania #substring.

Hauke ​​Ingmar Schmidt
źródło
2
W rzeczywistości modyfikatory widoczności (a przynajmniej były) przeznaczone do ochrony przed złośliwym kodem - należy jednak ustawić SecurityManager (System.setSecurityManager ()), aby aktywować ochronę. Jak bezpieczne jest to w rzeczywistości jest kolejne pytanie ...
sleske
2
Zasługuje na uznanie, ponieważ podkreślasz, że modyfikatory dostępu nie mają na celu „ochrony” kodu. Wydaje się, że jest to bardzo źle rozumiane zarówno w Javie, jak i .NET. Chociaż poprzedni komentarz zaprzecza temu; Niewiele wiem o Javie, ale w .NET jest to z pewnością prawda. W żadnym języku użytkownicy nie powinni zakładać, że kod ten jest odporny na hakowanie.
Tom W
Nie można naruszyć umowy finalnawet poprzez refleksję. Ponadto, jak wspomniano w innej odpowiedzi, ponieważ Java 7u6 #substringnie udostępnia tablic.
ntoskrnl
W rzeczywistości zachowanie finalzmieniło się z czasem ...: -O Zgodnie z przemówieniem Heinza „Reflection Madness”, które zamieściłem w innym wątku, finaloznaczonym jako ostatecznym w JDK 1.1, 1.3 i 1.4, ale można je modyfikować za pomocą odbicia za pomocą 1.2 zawsze , aw większości przypadków 1,5 i 6 ...
haraldK
1
finalpola mogą być zmieniane za pomocą nativekodu, tak jak robi to środowisko Serialization podczas odczytywania pól serializowanej instancji, a także System.setOut(…)modyfikuje ostatnią System.outzmienną. Ta ostatnia jest najbardziej interesującą funkcją, ponieważ odbicie z zastąpieniem dostępu nie może zmieniać static finalpól.
Holger
11

Niezmienność łańcucha jest z perspektywy interfejsu. Używasz refleksji, aby ominąć interfejs i bezpośrednio zmodyfikować elementy wewnętrzne instancji String.

s1i s2oba zostały zmienione, ponieważ oba są przypisane do tej samej instancji ciągu „intern”. Więcej informacji na temat tej części można znaleźć w tym artykule na temat równości łańcuchów i internowania. Możesz być zaskoczony, gdy dowiesz się, że w przykładowym kodzie s1 == s2powraca true!

Krease
źródło
10

Jakiej wersji Java używasz? Z Java 1.7.0_06 Oracle zmieniło wewnętrzną reprezentację String, szczególnie podłańcuch.

Cytowanie z wewnętrznej reprezentacji ciągów Java Oracle Tunes :

W nowym paradygmacie pola Przesunięcie i policzenie łańcucha zostały usunięte, więc podciągi nie mają już wspólnej wartości char [].

Przy tej zmianie może się zdarzyć bez refleksji (???).

manikanta
źródło
2
Gdyby OP używał starszej wersji JRE Sun / Oracle, ostatnia instrukcja wypisałaby „Java!” (jak przypadkowo napisał). Wpływa to tylko na współużytkowanie tablicy wartości między łańcuchami i łańcuchami podrzędnymi. Nadal nie możesz zmienić wartości bez sztuczek, takich jak odbicie.
haraldK
7

Są tutaj naprawdę dwa pytania:

  1. Czy łańcuchy naprawdę są niezmienne?
  2. Dlaczego s3 się nie zmienia?

Do punktu 1: Z wyjątkiem ROM, nie ma niezmiennej pamięci w twoim komputerze. W dzisiejszych czasach nawet ROM jest czasami zapisywalny. Zawsze gdzieś jest jakiś kod (czy to jądro, czy kod natywny omijający środowisko zarządzane), który może zapisać na adres pamięci. Tak więc w „rzeczywistości” nie, nie są one absolutnie niezmienne.

Do punktu 2: Jest tak, ponieważ podciąg prawdopodobnie przydziela nową instancję ciągu, która prawdopodobnie kopiuje tablicę. Możliwe jest zaimplementowanie podciągów w taki sposób, że nie zrobi ono kopii, ale to nie znaczy, że tak. W grę wchodzą kompromisy.

Na przykład, czy posiadanie odwołania powinno reallyLargeString.substring(reallyLargeString.length - 2)powodować utrzymywanie dużej ilości pamięci przy życiu, czy tylko kilka bajtów?

To zależy od sposobu implementowania podciągów. Głęboka kopia utrzyma mniej pamięci przy życiu, ale będzie działać nieco wolniej. Płytka kopia zachowa więcej pamięci, ale będzie szybsza. Korzystanie z głębokiej kopii może również zmniejszyć fragmentację sterty, ponieważ obiekt łańcucha i jego bufor można przydzielić w jednym bloku, w przeciwieństwie do 2 oddzielnych przydziałów sterty.

W każdym razie wygląda na to, że JVM wybrał głębokie kopie do wywołań podłańcuchów.

Scott Wiśniewski
źródło
3
Prawdziwa pamięć ROM jest tak samo niezmienna jak wydruk fotograficzny zamknięty w plastiku. Wzór jest trwale ustawiony, gdy wafel (lub nadruk) jest chemicznie rozwinięty. Pamięci zmieniane elektrycznie, w tym układy RAM , mogą zachowywać się jak „prawdziwa” pamięć ROM, jeśli sygnałów sterujących niezbędnych do zapisu nie można zasilić bez dodania dodatkowych połączeń elektrycznych do obwodu, w którym jest zainstalowany. W rzeczywistości nierzadko zdarza się, że urządzenia wbudowane zawierają pamięć RAM, która jest ustawiona fabrycznie i utrzymywana przez baterię zapasową, a której zawartość musiałaby zostać ponownie załadowana przez fabrykę, jeśli bateria się nie powiedzie.
supercat
3
@ superupat: Twój komputer nie jest jednak jednym z tych systemów wbudowanych. :) Prawdziwe ROM z wbudowanymi kablami nie były powszechne w komputerach PC od dekady lub dwóch; wszystko jest EEPROM i miga w tych dniach. Zasadniczo każdy widoczny dla użytkownika adres odnoszący się do pamięci odnosi się do pamięci potencjalnie zapisywalnej.
cHao
@ cHao: Wiele układów pamięci flash pozwala na ochronę części przed zapisem w sposób, który, jeśli można go w ogóle cofnąć, wymagałby zastosowania innych napięć niż byłoby to wymagane w przypadku normalnej pracy (do czego płyty główne nie byłyby przystosowane). Spodziewałbym się, że płyty główne będą korzystać z tej funkcji. Co więcej, nie jestem pewien co do dzisiejszych komputerów, ale historycznie niektóre komputery miały obszar pamięci RAM, który był chroniony przed zapisem podczas etapu rozruchu i który mógł być niechroniony tylko przez reset (co wymusiłoby uruchomienie z ROM).
supercat
2
@ superupat Myślę, że nie rozumiesz sedna tematu, a mianowicie, że ciągi przechowywane w pamięci RAM nigdy nie będą naprawdę niezmienne.
Scott Wisniewski
5

Aby dodać do odpowiedzi @ haraldK - jest to hack bezpieczeństwa, który może prowadzić do poważnego wpływu na aplikację.

Pierwszą rzeczą jest modyfikacja stałego ciągu przechowywanego w puli ciągów. Kiedy ciąg znaków jest zadeklarowany jako String s = "Hello World";, jest umieszczany w specjalnej puli obiektów w celu dalszego potencjalnego ponownego wykorzystania. Problem polega na tym, że kompilator umieści odniesienie do zmodyfikowanej wersji w czasie kompilacji, a gdy użytkownik zmodyfikuje ciąg przechowywany w tej puli w czasie wykonywania, wszystkie odniesienia w kodzie będą wskazywać na zmodyfikowaną wersję. Spowodowałoby to następujący błąd:

System.out.println("Hello World"); 

Wydrukuje:

Hello Java!

Był jeszcze jeden problem, który napotkałem, gdy wdrażałem ciężkie obliczenia w odniesieniu do tak ryzykownych ciągów. Wystąpił błąd, który wystąpił mniej więcej 1 na 1000000 razy podczas obliczeń, co spowodowało, że wynik był nieokreślony. Znalazłem problem, wyłączając JIT - zawsze otrzymywałem ten sam rezultat przy wyłączonym JIT. Domyślam się, że powodem był włamanie do systemu String, które złamało niektóre kontrakty optymalizacyjne JIT.

Andrey Chaschev
źródło
Może to być problem z bezpieczeństwem wątków, maskowany przez wolniejszy czas wykonywania i mniej współbieżności bez JIT.
Ted Pennings
@TedPennings Z mojego opisu może, po prostu nie chciałem zbytnio zagłębiać się w szczegóły. Właściwie spędziłem kilka dni próbując go zlokalizować. Był to algorytm jednowątkowy, który obliczał odległość między dwoma tekstami napisanymi w dwóch różnych językach. Znalazłem dwie możliwe poprawki tego problemu - jedna polegała na wyłączeniu JIT, a druga na dodaniu dosłownie braku działania String.format("")w jednej z wewnętrznych pętli. Jest szansa, że ​​będzie to problem z innym niepowodzeniem niż JIT, ale uważam, że był to JIT, ponieważ ten problem nigdy nie został powtórzony po dodaniu tego braku działania.
Andrey Chaschev
Robiłem to z wczesną wersją JDK ~ 7u9, więc to może być to.
Andrey Chaschev
1
@Andrey Chaschev: „Znalazłem dwie możliwe poprawki dla problemu”… trzecia możliwa poprawka, żeby nie włamać się do Stringwnętrza, nie przyszła Ci do głowy?
Holger
1
@Ted Pennings: problemy z bezpieczeństwem wątków i problemy z JIT są często takie same. JIT może generować kod, który opiera się na finalgwarancjach bezpieczeństwa wątku pola, które psują się podczas modyfikowania danych po zbudowaniu obiektu. Możesz więc postrzegać go jako problem JIT lub MT, tak jak chcesz. Prawdziwym problemem jest włamanie się Stringi modyfikacja danych, które mają być niezmienne.
Holger
5

Zgodnie z koncepcją pulowania wszystkie zmienne String zawierające tę samą wartość będą wskazywały na ten sam adres pamięci. Dlatego s1 i s2, oba zawierające tę samą wartość „Hello World”, będą wskazywały na tę samą lokalizację pamięci (powiedzmy M1).

Z drugiej strony s3 zawiera „Świat”, stąd będzie wskazywał na inny przydział pamięci (powiedzmy M2).

Teraz dzieje się tak, że wartość S1 jest zmieniana (przy użyciu wartości char []). Tak więc wartość w miejscu pamięci M1 wskazanym zarówno przez s1, jak i s2 została zmieniona.

W rezultacie zmieniono lokalizację pamięci M1, co powoduje zmianę wartości s1 i s2.

Ale wartość położenia M2 pozostaje niezmieniona, dlatego s3 zawiera tę samą pierwotną wartość.

AbhijeetMishra
źródło
5

Powodem, dla którego s3 tak naprawdę się nie zmienia, jest to, że w Javie podczas wykonywania podłańcucha tablica znaków wartości podłańcucha jest wewnętrznie kopiowana (przy użyciu Arrays.copyOfRange ()).

s1 i s2 są takie same, ponieważ w Javie oba odnoszą się do tego samego łańcucha wewnętrznego. Jest to zaprojektowane w Javie.

Maurizio In Denmark
źródło
2
Jak ta odpowiedź dodała coś do odpowiedzi przed tobą?
Gray
Zauważ też, że jest to całkiem nowe zachowanie i nie jest gwarantowane przez żadną specyfikację.
Paŭlo Ebermann
Implementacja String.substring(int, int)zmieniona w Javie 7u6. Przed 7u6, JVM będzie tylko utrzymać wskaźnik do oryginału String„s char[]wraz z indeksem i długości. Po 7u6 kopiuje podłańcuch do nowego. StringSą plusy i minusy.
Eric Jabłoń
2

String jest niezmienny, ale poprzez odbicie możesz zmienić klasę String. Właśnie zdefiniowałeś klasę String jako zmienną w czasie rzeczywistym. Jeśli chcesz, możesz przedefiniować metody, aby były publiczne, prywatne lub statyczne.

SpacePrez
źródło
2
Zmiana widoczności pól / metod nie jest przydatna, ponieważ w czasie kompilacji są one prywatne
czeski
1
Możesz zmienić dostępność metod, ale nie możesz zmienić ich statusu publicznego / prywatnego i nie możesz ustawić ich jako statycznych.
Gray
1

[Uwaga: jest to celowo wyrażony styl odpowiedzi, ponieważ uważam, że odpowiedź „nie rób tego w domu” jest uzasadniona]

Grzech jest linią, field.setAccessible(true);która mówi, że narusza publiczny interfejs API, umożliwiając dostęp do prywatnego pola. To gigantyczna dziura w zabezpieczeniach, którą można zamknąć, konfigurując menedżera bezpieczeństwa.

Zjawiskiem w pytaniu są szczegóły implementacji, których nigdy nie zobaczysz, gdy nie użyjesz tego niebezpiecznego wiersza kodu do naruszenia modyfikatorów dostępu poprzez odbicie. Oczywiście dwa (normalnie) niezmienne ciągi mogą dzielić tę samą tablicę znaków. To, czy podciąg dzieli tę samą tablicę, zależy od tego, czy może i czy deweloper pomyślał o udostępnieniu go. Zwykle są to niewidoczne szczegóły implementacji, których nie powinieneś znać, chyba że strzelisz do modyfikatora dostępu za pomocą tego wiersza kodu.

Po prostu nie jest dobrym pomysłem poleganie na takich szczegółach, których nie można doświadczyć bez naruszenia modyfikatorów dostępu za pomocą odbicia. Właściciel tej klasy obsługuje tylko normalny publiczny interfejs API i może wprowadzać zmiany implementacyjne w przyszłości.

Powiedziawszy wszystko, że linia kodu jest naprawdę bardzo przydatna, gdy masz pistolet, który trzyma cię za głowę, zmuszając cię do robienia tak niebezpiecznych rzeczy. Korzystanie z tych tylnych drzwi jest zwykle zapachem kodu, który należy uaktualnić do lepszego kodu biblioteki, w którym nie trzeba grzeszyć. Innym powszechnym zastosowaniem tego niebezpiecznego wiersza kodu jest napisanie „frameworka voodoo” (orm, kontener wstrzykiwania, ...). Wielu ludzi wierzy w takie ramy (zarówno za, jak i przeciw nim), więc uniknę zaproszenia do wojny z płomieniami, mówiąc, że znaczna większość programistów nie musi tam iść.

simbo1905
źródło
1

Ciągi są tworzone w stałym obszarze pamięci sterty JVM. Tak, to jest naprawdę niezmienne i nie można go zmienić po utworzeniu. Ponieważ w JVM istnieją trzy typy pamięci sterty: 1. Młoda generacja 2. Stara generacja 3. Generacja stała.

Po utworzeniu dowolnego obiektu przechodzi on w obszar sterty młodego pokolenia i obszar PermGen zarezerwowany dla puli ciągów.

Oto więcej szczegółów, z których możesz przejść i pobrać więcej informacji: Jak działa Garbage Collection w Javie .

Yasir Shabbir Choudhary
źródło
0

Ciąg jest niezmienny z natury, ponieważ nie ma metody modyfikowania obiektu String. Dlatego wprowadzili klasy StringBuilder i StringBuffer

Pratik Sherdiwala
źródło