2012-03-14 7 views
52

Mam następujące linie kodów do porównania String. str1 nie jest równe str2, co jest zrozumiałe, ponieważ porównuje odniesienie do obiektu. Ale dlaczego s1 jest równy s2?Co sprawia, że ​​porównanie porównawcze (==) działa dla niektórych ciągów w Javie?

String s1 = "abc"; 
String s2 = "abc"; 

String str1 = new String("abc"); 
String str2 = new String("abc"); 

if (s1==s2) 
    System.out.println("s1==s2");   
else 
    System.out.println("s1!=s2"); 

if (str1==str2) 
    System.out.println("str1==str2");   
else 
    System.out.println("str1!=str2"); 

if (s1==str1) 
    System.out.println("str1==s1");   
else 
    System.out.println("str1!=s1"); 

wyjściowa:

s1==s2 
    str1!=str2 
    str1!=s1 
+12

Próbowałaś szukają więc najpierw? :( –

+13

http://stackoverflow.com/questions/7144059/java-string-pool-object-creation, http://stackoverflow.com/questions/4033625/if-compares-references-in-java-why-does -it-assess-to-true-with-this-strin, http://stackoverflow.com/questions/6377058/string-reference, http://stackoverflow.com/questions/1903094/java-strings-and-stringpool –

+1

(Nie zamykam tego, ponieważ nie znalazłem innego pytania * tak skupionego * jak to, ale jestem pewien, że istnieje) –

Odpowiedz

80

Ciąg stały basen będzie zasadniczo buforować wszystkie literały ciągów więc oni ten sam obiekt pod spodem, dlatego widać wyjście zrobić dla s1==s2. Jest to w zasadzie optymalizacja w VM, aby uniknąć tworzenia nowego obiektu typu string za każdym razem, gdy deklarowany jest literał, co może bardzo szybko stać się bardzo kosztownym! W przypadku przykładu str1==str2 wyraźnie mówisz maszynie wirtualnej, aby utworzyła nowe obiekty ciągów, a więc dlaczego jest fałszywa.

Na marginesie, wywołanie metody intern() na dowolnym ciągu doda ją do puli stałej (i zwróci String, który został dodany do puli). Nie jest to jednak dobry pomysł, aby zrobić to jednak, chyba że masz pewność masz do czynienia z ciągami, które z pewnością będą używane jako stałe, w przeciwnym razie możesz skończyć się trudnym do wyśledzenia wycieków pamięci.

+2

Pokonałeś mnie tylko 2 sekundy, dając pomysł metody intern() –

+6

Drzemiesz, przegrywasz haha ​​j/k. = P –

+1

+1 ale warto też zauważyć, że dodanie wielu ciągów do puli stałej jest złym pomysłem, ponieważ może powodować trudne do wykrycia wycieki pamięci. Robimy to tylko dla małej liczby ustalonych łańcuchów, które rzeczywiście będą używane jako stałe (np. Klucze HashMap), nigdy dla arbitralnych danych String. – mikera

18

s1 i s2 są literałami ciągu. Podczas tworzenia nowego ciągu literowego kompilator najpierw sprawdza, czy literał reprezentujący to samo występuje w puli String, czy nie. Jeśli istnieje jeden, kompilator zwraca ten literał, w przeciwnym razie kompilator utworzy nowy.

Po utworzeniu String s2 kompilator zwraca String s1 z puli, która została wcześniej utworzona. To jest powód, dla którego s1 i s2 są takie same. To zachowanie jest nazywane interning.

+0

Jestem nieco zdezorientowany, gdy mówisz, że "kompilator tworzy nowy". AFAIK, kompilator jest przeznaczony do tworzenia pośredniego kodu maszynowego i nie uruchamia (w związku z tym tworzy) żadnego obiektu w pamięci. Czy miałeś na myśli ten kompilator _replaces_ string literals? Proszę to wyjaśnić. – peakit

+0

Nie mam teraz żadnej dokumentacji pomocniczej, ale uważam, że @ Chandra Sekhar odnosiła się do kompilatora JIT lub Just In Time, a nie do kompilatora javac. – Robert

3

W języku Java te same ciągłe ciągi będą ponownie używane. Tak więc s1 i s2 wskazują na ten sam obiekt "abc" i s1==s2. Ale gdy użyjesz new String("abc"), zostanie utworzony inny obiekt. Tak więc s1 != str1.

5

Wynika to z internowania literałów łańcuchowych. W tej sprawie, Java documentations mówi:

Wszystkie ciągami tekstowymi i string-ceniony wyrażenia stałe są internowany

I to wyjaśnia, dlaczego s1 i s2 są takie same (te dwie zmienne wskazują na ten sam internowany ciąg)

9

Zjawisko to wynika z String interning.

Zasadniczo wszystkie ciągi literałów są "buforowane" i ponownie wykorzystywane.

0

Jak string jest niezmienne w Javie, cały string literals buforowane do ponownego użycia.

Podczas tworzenia obiektu String za pomocą operatora new() zawsze tworzy on nowy obiekt w pamięci sterty. Z drugiej strony, jeśli tworzysz obiekt używając składni literału String, np."Java", może zwrócić istniejący obiekt z puli String (pamięć podręczna obiektu String w przestrzeni roboczej Perm, która jest teraz przenoszona do przestrzeni sterty w ostatnim wydaniu Java), jeśli już istnieje. W przeciwnym razie utworzy nowy obiekt typu string i umieści go w puli ciągów do przyszłego ponownego wykorzystania.

String s1 = new String("java"); 
String s2 = new String("java"); 
String s3 = "java"; 
String s4 = "java"; 

enter image description here

Please refer this link