Mam proste pytanie dotyczące napisów w Javie. Następny segment prostego kodu po prostu łączy dwa ciągi, a następnie porównuje je ==
.
String str1="str";
String str2="ing";
String concat=str1+str2;
System.out.println(concat=="string");
Wyrażenie porównania concat=="string"
zwraca false
jako oczywiste (rozumiem różnicę między equals()
i ==
).
Gdy te dwa ciągi zostaną zadeklarowane w ten final
sposób,
final String str1="str";
final String str2="ing";
String concat=str1+str2;
System.out.println(concat=="string");
Wyrażenie porównania concat=="string"
w tym przypadku zwraca true
. Dlaczego robi final
różnicę? Czy to musi coś zrobić z pulą stażystów, czy jestem po prostu wprowadzany w błąd?
equals()
i==
w ich kontekście i zadaje bardziej sensowne pytanie.String
? Myślę, że bardzo logiczne, a nie głupie jest przeprowadzanie porównania zawartościequals
, metoda, którą możemy zastąpić, aby stwierdzić, kiedy uważamy dwa obiekty za równe, i wykonanie porównania tożsamości==
. Gdyby porównanie zawartości zostało wykonane przez,==
nie moglibyśmy pominąć tego, aby zdefiniować, co rozumiemy przez „równe treści”, a posiadanie znaczeniaequals
i==
odwrócenie tylko dlaString
s byłoby głupie. Niezależnie od tego, nie widzę żadnej przewagi w==
porównaniu zawartościequals
.Odpowiedzi:
Gdy zadeklarujesz zmienną
String
(która jest niezmienna ) jakofinal
i zainicjujesz ją wyrażeniem stałym czasu kompilacji, stanie się ono również wyrażeniem stałym czasu kompilacji, a jego wartość zostanie podkreślona przez kompilator, w którym jest używana. Zatem w drugim przykładzie kodu, po wstawieniu wartości, kompilacja łańcucha jest tłumaczona przez kompilator na:co w porównaniu do
"string"
da citrue
, ponieważ literały łańcuchowe są internowane .Z JLS §4.12.4 -
final
Zmienne :Również z JLS § 15.28 - Wyrażenie stałe:
Nie jest tak w pierwszym przykładzie kodu, w którym
String
zmienne nie sąfinal
. Nie są to zatem wyrażenia stałe czasu kompilacji. Operacja konkatenacji zostanie opóźniona do czasu wykonania, co spowoduje utworzenie nowegoString
obiektu. Możesz to sprawdzić, porównując kod bajtowy obu kodów.Pierwszy przykład kodu (nie w
final
wersji) jest kompilowany do następującego kodu bajtowego:Oczywiste jest przechowywanie
str
iing
w dwóch różnych zmiennych, a przy użyciuStringBuilder
wykonać operację konkatenacji.Natomiast twój drugi przykład kodu (
final
wersja) wygląda następująco:Więc bezpośrednio wstawia ostatnią zmienną do utworzenia ciągu
string
w czasie kompilacji, który jest ładowany przezldc
operację w kroku0
. Następnie drugildc
krok literału jest ładowany przez operację w kroku7
. Nie wymaga tworzenia żadnego nowegoString
obiektu w czasie wykonywania. Łańcuch jest już znany w czasie kompilacji i jest internowany.źródło
true
?String
Według moich badań wszystkie
final String
są internowane w Javie. Z jednego z postów na blogu:Oznacza to, że jeśli zadzwonisz
String.intern()
, możesz porównać dwa ciągi za pomocą==
operatora. Ale tutajString.intern()
nie jest to konieczne, ponieważ w Javiefinal String
są wewnętrznie internalizowane.Możesz znaleźć więcej informacji Porównanie ciągów znaków za pomocą operatora == i Javadoc dla metody String.intern () .
Zobacz także ten post Stackoverflow, aby uzyskać więcej informacji.
źródło
Jeśli spojrzysz na te metody
i jego dekompilacja z
javap -c ClassWithTheseMethods
wersjami, które zobaczyszi
Więc jeśli ciągi nie są ostateczne, kompilator będzie musiał użyć
StringBuilder
do połączeniastr1
istr2
takzostanie skompilowany do
co oznacza, że
concat
zostaną utworzone w czasie wykonywania, więc nie będą pochodzić z puli ciągów.Również jeśli ciągi są ostateczne, kompilator może założyć, że nigdy się nie zmieni, więc zamiast
StringBuilder
go używać , może bezpiecznie połączyć swoje wartości, abymożna zmienić na
i połączone w
co oznacza, że
concate
stanie się literałem żądła, które zostanie internowane w puli łańcuchów, a następnie porównane z tym samym literałem łańcucha z tej puli wif
instrukcji.źródło
Stos i ciąg conts koncepcja puli
źródło
Zobaczmy na
final
przykład kod bajtowyAt
0:
i2:
,String
"string"
jest wypychany na stos (ze stałej puli) i zapisywanyconcat
bezpośrednio w zmiennej lokalnej . Można wywnioskować, że kompilator sam tworzy (konkatenuje)String
"string"
w czasie kompilacji.Non
final
kod bajtowyTutaj masz dwie
String
stałe,"str"
a"ing"
które muszą być łączone przy starcie zStringBuilder
.źródło
Chociaż podczas tworzenia za pomocą notacji Javy literału ciągowego, automatycznie wywołuje metodę intern () w celu umieszczenia tego obiektu w puli ciągów, pod warunkiem, że nie był już obecny w puli.
Kompilator wie, że końcowa zmienna nigdy się nie zmieni, kiedy dodamy te zmienne końcowe, dane wyjściowe trafiają do puli ciągów, ponieważ
str1 + str2
dane wyjściowe również nigdy się nie zmienią, więc w końcu kompilator wywołuje metodę inter po wyjściu powyższych dwóch zmiennych końcowych. W przypadku niekończącego kompilatora zmiennych nie należy wywoływać metody intern.źródło