Proszę o odpowiedź na to pytanie. Ciąg a = „Java”; Ciąg b = „Java”; System.out.println (a == b); true // ale System.out.println („a == b?” + a == b); // false ...
Energy
nie rozumiem, kiedy dodałem jakiś komentarz („a == b?) => mój wynik staje się FAŁSZ. dlaczego?
Energy
@Energy Wynik jest taki, falseże kolejność operacji dyktuje, że operator + idzie pierwszy, konkatenując „a == b?” za pomocą, aby utworzyć ciąg „a == b? Java”. Następnie wyrażenie ma "a==b?Java" == bwartość false.
Allison B
@AllisonB mam to, dziękuję bardzo!
Energia
Odpowiedzi:
187
new String("text");
wyraźnie tworzy nową i referencyjnie odrębną instancję Stringobiektu; String s = "text";może ponownie użyć instancji z stałej puli ciągów, jeśli jest ona dostępna.
Jesteś bardzo rzadko kiedykolwiek chcesz użyć new String(anotherString)konstruktora. Z interfejsu API:
String(String original): Inicjuje nowo utworzonyString obiekt, aby reprezentował tę samą sekwencję znaków co argument; innymi słowy, nowo utworzony ciąg jest kopią ciągu argumentu. O ile nie jest wymagana wyraźna kopia oryginału, użycie tego konstruktora nie jest konieczne, ponieważ ciągi znaków są niezmienne.
==na dwóch typach referencyjnych jest porównanie tożsamości referencyjnej. Dwa obiekty, które equalsniekoniecznie są ==. Zwykle niewłaściwe jest użycie ==w odniesieniu do typów referencyjnych; przez większość czasu equalsnależy użyć zamiast tego.
Niemniej jednak, jeśli z jakiegoś powodu musisz utworzyć dwa equals, ale nie ==łańcuch, to można użyć new String(anotherString)konstruktora. Należy jednak powtórzyć, że jest to bardzo dziwne i rzadko jest to zamierzenie.
Jeśli napiszę: String s = new String ("abc"); A teraz piszę: String s = "abc"; Will String s = "abc"; utworzyć nowy literał String w puli String?
Kaveesh Kanwal
Dlaczego nikt nie odpowiada na poprzednie pytanie?
zeds
2
@KaveeshKanwal Nie, literał nie zostanie skopiowany. Jak widać są 2 "abc"s. Tylko jeden z nich przejdzie do puli Ciągów, a drugi będzie się do niego odnosił. Potem sjest odpowiedni nowy obiekt.
Kayaman
1
@Kaveesh Kanwal - String s = new String („abc”) utworzy tylko nowy obiekt String o wartości „abc”. A druga instrukcja sprawdzi, czy dosłowny ciąg „abc” jest już obecny w puli ciągów, czy nie. Jeśli jest już obecny, to zwracane jest odwołanie do istniejącego, a jeśli nie, to tworzona jest nowa literał („abc”) w puli ciągów. Mam nadzieję, że to rozwiązuje twoje zapytanie !!
user968813
Nie ma w tym nic „może”. Kompilator musi pula literałów łańcuchowych. JLS 3.10.5 .
Poniższa migawka może pomóc ci zrozumieć to wizualnie, aby zapamiętać go na dłużej.
Tworzenie obiektu linia po linii:
String str1 =newString("java5");
Używając w konstruktorze dosłownego ciągu „java5”, nowa wartość ciągu jest przechowywana w stałej puli ciągów. Za pomocą nowego operatora tworzony jest nowy obiekt ciągu na stercie o wartości „java5”.
String str2 ="java5"
Odwołanie „str2” wskazuje na już zapisaną wartość w stałej puli ciągów
String str3 =newString(str2);
Nowy obiekt ciągu jest tworzony na stercie o tej samej wartości co odwołanie przez „str2”
String str4 ="java5";
Odwołanie „str4” wskazuje na już zapisaną wartość w stałej puli ciągów
Dobra odpowiedź .. ale chcemy wiedzieć, że teraz jestem giong, aby zmienić wartość str1 = "java6", to zmieni wartość str4?
CoronaPintu
2
tak, miałem sprawdzenie, że nie zmieni wartości str4
CoronaPintu
@Braj Czy możesz przedstawić dokumentację dotyczącą twierdzenia, na które udzielono odpowiedzi?
Basil Bourque,
@Braj: Czy nagłówki „Sterty” i „puli” w tabeli powinny być odwrócone?
Rahul Kurup,
Niepoprawne. Stała pula jest tworzona w czasie kompilacji, a nie w czasie wykonywania. Nie używaj formatowania cytatów dla tekstu, który nie jest cytowany.
drugi tworzy ciąg w stałej puli ( "text"), a drugi ciąg w normalnej przestrzeni sterty ( s). Oba ciągi będą miały tę samą wartość, co „tekst”.
String s =newString("text");
s jest następnie tracony (kwalifikuje się do GC), jeśli później nie będzie używany.
Z drugiej strony literały łańcuchowe są ponownie wykorzystywane. Jeśli używasz "text"w wielu miejscach swojej klasy, w rzeczywistości będzie to jeden i tylko jeden Ciąg (tj. Wiele odniesień do tego samego ciągu w puli).
Co więcej, literał łańcuchowy zawsze odnosi się do tego samego wystąpienia klasy Łańcuch. Wynika to z tego, że literały łańcuchowe - lub, bardziej ogólnie, łańcuchy, które są wartościami wyrażeń stałych (§ 15.28) - są „internowane”, aby dzielić unikalne instancje, przy użyciu metody String.intern.
Przykład 3.10.5-1. Literały smyczkowe
Program składający się z jednostki kompilacyjnej (§7.3):
Literał łańcuchowy jest odwołaniem do instancji klasy Łańcuch i pochodzi ze struktury CONSTANT_String_info (§4.4.3) w binarnej reprezentacji klasy lub interfejsu. Struktura CONSTANT_String_info podaje sekwencję punktów kodowych Unicode stanowiących literał ciągu.
Język programowania Java wymaga, aby identyczne literały łańcuchowe (tj. Literały zawierające tę samą sekwencję punktów kodowych) musiały odnosić się do tej samej instancji klasy String (JLS §3.10.5). Ponadto, jeśli metoda String.intern zostanie wywołana na dowolnym ciągu, wynikiem jest odwołanie do tej samej instancji klasy, która zostałaby zwrócona, gdyby ten ciąg pojawił się jako literał. Dlatego poniższe wyrażenie musi mieć wartość true:
("a"+"b"+"c").intern()=="abc"
Aby uzyskać literał ciąg, wirtualna maszyna Java sprawdza sekwencję punktów kodu podaną przez strukturę CONSTANT_String_info.
Jeśli metoda String.intern została wcześniej wywołana na instancji klasy String zawierającej sekwencję punktów kodu Unicode identycznych z podanymi przez strukturę CONSTANT_String_info, to wynik wyprowadzenia literału łańcucha jest odniesieniem do tej samej instancji klasy String.
W przeciwnym razie tworzona jest nowa instancja klasy String zawierająca sekwencję punktów kodu Unicode podaną przez strukturę CONSTANT_String_info; odwołanie do tej instancji klasy jest wynikiem pochodnej literału łańcuchowego. Na koniec wywoływana jest metoda intern nowej instancji String.
Kod bajtowy
Warto również przyjrzeć się implementacji kodu bajtowego w OpenJDK 7.
Jeśli dekompilujemy:
publicclassStringPool{publicstaticvoid main(String[] args){String a ="abc";String b ="abc";String c =newString("abc");System.out.println(a);System.out.println(b);System.out.println(a == c);}}
str1nie bierze udziału w wartości str2lub str3czy str4w jakikolwiek sposób.
Markiz Lorne
1
Pomyśl o "bla"byciu magiczną fabryką jak Strings.createString("bla")(pseudo). Fabryka posiada pulę wszystkich utworzonych w ten sposób łańcuchów.
Jeśli zostanie wywołany, sprawdza, czy w puli jest już ciąg o tej wartości. Jeśli true, zwraca ten obiekt łańcucha, a zatem łańcuchy uzyskane w ten sposób są rzeczywiście tym samym obiektem.
Jeśli nie, tworzy nowy obiekt ciągu wewnętrznie, zapisuje go w puli, a następnie zwraca. W ten sposób, gdy następnym razem zapytanie o tę samą wartość ciągu jest zwracane, zwraca tę samą instancję.
Ręczne tworzenie new String("")zastępuje to zachowanie, omijając pulę literałów łańcuchowych. Dlatego zawsze należy sprawdzać równość za pomocą, equals()która porównuje sekwencję znaków zamiast równości odniesienia do obiektu.
„Magiczna fabryka”, o której mówisz, jest niczym więcej niż kompilatorem Java. Błędem jest pisanie o tym procesie, tak jakby miał miejsce w czasie wykonywania.
Markiz Lorne
1
Jeden prosty sposób na zrozumienie różnicy znajduje się poniżej:
String s ="abc";String s1="abc";String s2=newString("abc");if(s==s1){System.out.println("s==s1 is true");}else{System.out.println("s==s1 is false");}if(s==s2){System.out.println("s==s2 is true");}else{System.out.println("s==s2 is false");}
wyjście jest
s==s1 is true
s==s2 is false
Zatem nowa String () zawsze utworzy nową instancję.
Dowolny literał łańcuchowy jest tworzony w puli literału łańcuchowego, a pula nie pozwala na żadne duplikaty. Zatem jeśli dwa lub więcej obiektów łańcuchowych zostanie zainicjowanych z tą samą wartością literału, wówczas wszystkie obiekty będą wskazywały na ten sam literał.
String obj1 ="abc";String obj2 ="abc";
„obj1” i „obj2” będą wskazywać ten sam literał łańcuchowy, a pula literałów łańcuchowych będzie miała tylko jeden literał „abc”.
Kiedy tworzymy obiekt klasy String za pomocą nowego słowa kluczowego, utworzony w ten sposób ciąg jest przechowywany w pamięci sterty. Wszelkie literały łańcuchowe przekazywane jako parametr do konstruktora klasy String są jednak przechowywane w puli łańcuchów. Jeśli tworzymy wiele obiektów przy użyciu tej samej wartości z nowym operatorem, nowy obiekt będzie tworzony na stercie za każdym razem, ponieważ tego nowego operatora należy unikać.
Teraz w powyższym przypadku:
Wiersz 1: literał „abc” jest przechowywany w puli ciągów.
Wiersz 2: literał „abcdef” zostaje zapisany w puli ciągów.
Wiersz 3: Nowy literał „xyz” jest przechowywany w puli ciągów, a „str1” zaczyna wskazywać na ten literał.
Wiersz 4: Ponieważ wartość jest generowana przez dołączenie do innej zmiennej, wynik jest przechowywany w pamięci sterty, a dosłownie dodawane „ghi” zostanie sprawdzone pod kątem istnienia w puli ciągów i zostanie utworzone, ponieważ nie istnieje w powyższy przypadek.
Sprawdza, czy stała pula ciągów zawiera już ciąg „witaj”? Jeśli jest obecny, nie doda wpisu w puli stałej String. Jeśli nie jest obecny, doda pozycję w stałej puli ciągów.
Obiekt zostanie utworzony w obszarze pamięci sterty i strpunktami odniesienia do obiektu utworzonego w lokalizacji pamięci sterty.
jeśli chcesz strodwoływać się do obiektu punktowego zawierającego w stałej puli Ciąg, to musisz jawnie wywołaćstr.intern();
String str ="world";
Sprawdza, czy stała pula ciągów zawiera już ciąg „witaj”? Jeśli jest obecny, nie doda wpisu w puli stałej String. Jeśli nie jest obecny, doda pozycję w stałej puli ciągów.
W obu powyższych przypadkach strodwołanie wskazuje na ciąg "world"znaków w puli stałej.
„It” jest kompilatorem Java. Dosłowny ciąg tworzy unikalny wpis w stałej puli w czasie kompilacji. Błędem jest opisanie tego procesu tak, jakby miało to miejsce w czasie wykonywania.
Markiz Lorne z
Czy możesz wyjaśnić jasno, co jest nie tak w tym poście?
Jayesh
To, co jest złe w tym poście, to to, że literał ciągów jest zbierany w czasie kompletowania, jak już powiedziałem. Nie podczas wykonywania kodu, jak w twojej odpowiedzi.
Markiz Lorne
@EJP Doceniam twoją odpowiedź. Czy możesz wskazać dokładną linię, która jest błędna w odpowiedzi? Widzę, że wszystkie powyższe odpowiedzi są takie same, jak napisałem. Proszę o pomoc, chcę poprawić moje zrozumienie. Dzięki.
Jayesh
Pisałeś o całym procesie, tak jakby wszystko miało miejsce, gdy wiersz kodu jest wykonywany, co, jak wielokrotnie powtarzałem, nie jest prawdą. Nie możesz zredukować tego wszystkiego do jednej „dokładnej linii”, która jest błędna w twojej odpowiedzi.
Markiz Lorne
0
Gdy przechowujesz ciąg znaków jako
String string1 ="Hello";
bezpośrednio, następnie JVM tworzy obiekt String o podanej cenie podczas oddzielnego bloku pamięci o nazwie Pula stałych String.
I ilekroć mamy tendencję do próby wyprodukowania kolejnego ciągu jako
String string2 ="Hello";
JVM sprawdza, czy istnieje jakiś obiekt String ze stałą ceną w puli stałej String, jeśli tak, zamiast tworzyć nowy obiekt JVM przypisuje odwołanie istniejącego obiektu do nowej zmiennej.
A kiedy przechowujemy String jako
String string =newString("Hello");
przy użyciu nowego słowa kluczowego powstaje nowy obiekt o podanej cenie bez względu na zawartość stałej puli Ciąg.
false
że kolejność operacji dyktuje, że operator + idzie pierwszy, konkatenując „a == b?” za pomocą, aby utworzyć ciąg „a == b? Java”. Następnie wyrażenie ma"a==b?Java" == b
wartość false.Odpowiedzi:
new String("text");
wyraźnie tworzy nową i referencyjnie odrębną instancjęString
obiektu;String s = "text";
może ponownie użyć instancji z stałej puli ciągów, jeśli jest ona dostępna.Jesteś bardzo rzadko kiedykolwiek chcesz użyć
new String(anotherString)
konstruktora. Z interfejsu API:Powiązane pytania
Co oznacza rozróżnienie referencyjne
Sprawdź następujący fragment kodu:
==
na dwóch typach referencyjnych jest porównanie tożsamości referencyjnej. Dwa obiekty, któreequals
niekoniecznie są==
. Zwykle niewłaściwe jest użycie==
w odniesieniu do typów referencyjnych; przez większość czasuequals
należy użyć zamiast tego.Niemniej jednak, jeśli z jakiegoś powodu musisz utworzyć dwa
equals
, ale nie==
łańcuch, to można użyćnew String(anotherString)
konstruktora. Należy jednak powtórzyć, że jest to bardzo dziwne i rzadko jest to zamierzenie.Bibliografia
class Object
-boolean Object(equals)
Powiązane kwestie
źródło
"abc"
s. Tylko jeden z nich przejdzie do puli Ciągów, a drugi będzie się do niego odnosił. Potems
jest odpowiedni nowy obiekt.Literały ciągów przejdą do puli stałej ciągów .
Poniższa migawka może pomóc ci zrozumieć to wizualnie, aby zapamiętać go na dłużej.
Tworzenie obiektu linia po linii:
Używając w konstruktorze dosłownego ciągu „java5”, nowa wartość ciągu jest przechowywana w stałej puli ciągów. Za pomocą nowego operatora tworzony jest nowy obiekt ciągu na stercie o wartości „java5”.
Odwołanie „str2” wskazuje na już zapisaną wartość w stałej puli ciągów
Nowy obiekt ciągu jest tworzony na stercie o tej samej wartości co odwołanie przez „str2”
Odwołanie „str4” wskazuje na już zapisaną wartość w stałej puli ciągów
Wszystkie obiekty: Kupa - 2, Pula - 1
Dalsza lektura na temat społeczności Oracle
źródło
Jeden tworzy ciąg w puli stałej ciągów
drugi tworzy ciąg w stałej puli (
"text"
), a drugi ciąg w normalnej przestrzeni sterty (s
). Oba ciągi będą miały tę samą wartość, co „tekst”.s
jest następnie tracony (kwalifikuje się do GC), jeśli później nie będzie używany.Z drugiej strony literały łańcuchowe są ponownie wykorzystywane. Jeśli używasz
"text"
w wielu miejscach swojej klasy, w rzeczywistości będzie to jeden i tylko jeden Ciąg (tj. Wiele odniesień do tego samego ciągu w puli).źródło
JLS
JLS nazywa tę koncepcję „internowaniem”.
Odpowiedni fragment z JLS 7 3.10.5 :
JVMS
JVMS 7 5.1 mówi :
Kod bajtowy
Warto również przyjrzeć się implementacji kodu bajtowego w OpenJDK 7.
Jeśli dekompilujemy:
mamy na stałej puli:
i
main
:Uwaga jak:
0
i3
:ldc #2
ładowana jest ta sama stała (literały)12
: tworzona jest nowa instancja ciągu (za pomocą#2
argumentem jako)35
:a
ic
są porównywane jako zwykłe obiekty zif_acmpne
Reprezentacja ciągów ciągłych jest dość magiczna w kodzie bajtowym:
new String
)a powyższy cytat JVMS wydaje się mówić, że ilekroć wskazany Utf8 jest taki sam, ładowane są identyczne instancje
ldc
.Zrobiłem podobne testy dla pól i:
static final String s = "abc"
wskazuje na stałą tabelę poprzez atrybut ConstantValueldc
Wniosek : istnieje bezpośrednia obsługa kodów bajtów dla puli ciągów, a reprezentacja pamięci jest wydajna.
Bonus: porównaj to z pulą liczb całkowitych , która nie ma bezpośredniego wsparcia dla kodu bajtowego (tzn. Nie ma
CONSTANT_String_info
analogu).źródło
@Braj: Myślę, że wspominałeś już na odwrót. Proszę, popraw mnie jeśli się mylę
Tworzenie obiektu linia po linii:
Ciąg str1 = nowy ciąg („java5”)
Ciąg str2 = "java5"
Ciąg str3 = nowy ciąg (str2)
Ciąg str4 = "java5"
źródło
str1
nie bierze udziału w wartościstr2
lubstr3
czystr4
w jakikolwiek sposób.Pomyśl o
"bla"
byciu magiczną fabryką jakStrings.createString("bla")
(pseudo). Fabryka posiada pulę wszystkich utworzonych w ten sposób łańcuchów.Jeśli zostanie wywołany, sprawdza, czy w puli jest już ciąg o tej wartości. Jeśli true, zwraca ten obiekt łańcucha, a zatem łańcuchy uzyskane w ten sposób są rzeczywiście tym samym obiektem.
Jeśli nie, tworzy nowy obiekt ciągu wewnętrznie, zapisuje go w puli, a następnie zwraca. W ten sposób, gdy następnym razem zapytanie o tę samą wartość ciągu jest zwracane, zwraca tę samą instancję.
Ręczne tworzenie
new String("")
zastępuje to zachowanie, omijając pulę literałów łańcuchowych. Dlatego zawsze należy sprawdzać równość za pomocą,equals()
która porównuje sekwencję znaków zamiast równości odniesienia do obiektu.źródło
Jeden prosty sposób na zrozumienie różnicy znajduje się poniżej:
wyjście jest
Zatem nowa String () zawsze utworzy nową instancję.
źródło
Dowolny literał łańcuchowy jest tworzony w puli literału łańcuchowego, a pula nie pozwala na żadne duplikaty. Zatem jeśli dwa lub więcej obiektów łańcuchowych zostanie zainicjowanych z tą samą wartością literału, wówczas wszystkie obiekty będą wskazywały na ten sam literał.
„obj1” i „obj2” będą wskazywać ten sam literał łańcuchowy, a pula literałów łańcuchowych będzie miała tylko jeden literał „abc”.
Kiedy tworzymy obiekt klasy String za pomocą nowego słowa kluczowego, utworzony w ten sposób ciąg jest przechowywany w pamięci sterty. Wszelkie literały łańcuchowe przekazywane jako parametr do konstruktora klasy String są jednak przechowywane w puli łańcuchów. Jeśli tworzymy wiele obiektów przy użyciu tej samej wartości z nowym operatorem, nowy obiekt będzie tworzony na stercie za każdym razem, ponieważ tego nowego operatora należy unikać.
„obj1” i „obj2” będą wskazywać na dwa różne obiekty na stercie, a pula literałów łańcuchowych będzie miała tylko jeden literał „abc”.
Warto również zwrócić uwagę na zachowanie łańcuchów, ponieważ każde nowe przypisanie lub konkatenacja na łańcuchu tworzy nowy obiekt w pamięci.
Teraz w powyższym przypadku:
Wiersz 1: literał „abc” jest przechowywany w puli ciągów.
Wiersz 2: literał „abcdef” zostaje zapisany w puli ciągów.
Wiersz 3: Nowy literał „xyz” jest przechowywany w puli ciągów, a „str1” zaczyna wskazywać na ten literał.
Wiersz 4: Ponieważ wartość jest generowana przez dołączenie do innej zmiennej, wynik jest przechowywany w pamięci sterty, a dosłownie dodawane „ghi” zostanie sprawdzone pod kątem istnienia w puli ciągów i zostanie utworzone, ponieważ nie istnieje w powyższy przypadek.
źródło
Chociaż wygląda tak samo z punktu widzenia programistów, ma duży wpływ na wydajność. Prawie zawsze chciałbyś skorzystać z pierwszej formy.
źródło
Sprawdza, czy stała pula ciągów zawiera już ciąg „witaj”? Jeśli jest obecny, nie doda wpisu w puli stałej String. Jeśli nie jest obecny, doda pozycję w stałej puli ciągów.
Obiekt zostanie utworzony w obszarze pamięci sterty i
str
punktami odniesienia do obiektu utworzonego w lokalizacji pamięci sterty.jeśli chcesz
str
odwoływać się do obiektu punktowego zawierającego w stałej puli Ciąg, to musisz jawnie wywołaćstr.intern();
Sprawdza, czy stała pula ciągów zawiera już ciąg „witaj”? Jeśli jest obecny, nie doda wpisu w puli stałej String. Jeśli nie jest obecny, doda pozycję w stałej puli ciągów.
W obu powyższych przypadkach
str
odwołanie wskazuje na ciąg"world"
znaków w puli stałej.źródło
Gdy przechowujesz ciąg znaków jako
bezpośrednio, następnie JVM tworzy obiekt String o podanej cenie podczas oddzielnego bloku pamięci o nazwie Pula stałych String.
I ilekroć mamy tendencję do próby wyprodukowania kolejnego ciągu jako
JVM sprawdza, czy istnieje jakiś obiekt String ze stałą ceną w puli stałej String, jeśli tak, zamiast tworzyć nowy obiekt JVM przypisuje odwołanie istniejącego obiektu do nowej zmiennej.
A kiedy przechowujemy String jako
przy użyciu nowego słowa kluczowego powstaje nowy obiekt o podanej cenie bez względu na zawartość stałej puli Ciąg.
źródło