Typ/Klasa String
// obiekt typu/klasy String można utworzyć przy użyciu konstruktora
String s1 = new String("Dzień ");
// użycie zapisu s2="jakiś_tekst" również tworzy obiekt typu String, dlatego nie trzeba używać zapisu new String(...)
String s2 = "dobry";
// W języiu Java dla danych typu String możemy używać operatora +.
// W przypadku klasy String operator + działa wyjątkowo tak, jak w przypadku typów prostych.
String s3 = s1 + s2;
System.out.println( s3 ); // zostanie wyświetlony łańcuch "Dzień dobry"
String str1 = "Hello!";
String str2 = "Hello!";
System.out.println( str1==str2 ); // true, ponieważ JVM (Java Virtual Machine) rozpoznaje, że
// oba łańcuchy są identyczne i dla zoptymalizowania programu
// utworzyła tylko jeden obiekt klasy String.
// Dlatego str1 i str2 wskazują na ten sam obiekt.
System.out.println( str1.equals(str2) ); // true, ponieważ metoda equals() porównuje zawartość
// obiektów wskazywanych przez zmienne str1 i str2 oraz adresy obiektów,
// a mechanizm Javy w wypadku obiektu klasy String inteligentnie unika
// tworzenia dodatkowych obiektów, jeśli dwa łańcuchy znaków są identyczne.
Jeśli użyjemy następującego zapisu, efekt będzie NIEMALŻE identyczny:
String str1 = new String( "Hello!" );
String str2 = new String( "Hello!" );
System.out.println( str1.equals( str2 ) ); //true
...NIEMALŻE, ponieważ w takim przypadku tworzone są dwa osobne obiekty (pod osobnymi adresami na stercie) lecz o takiej samej zawartości i takim samym tzw. kodzie hashCode. Jednakże w Javie te obiekty typu String będą traktowane tak, jakby były tym samym obiektem - anomalia ta dotyczy wyjątkowo klasy String.
Doświadczenie obrazujące opisaną wyżej sytuację - za pomocą metody System.identityHashCode(...) wyświetlamy rzeczywisty, systemowy identyfikator obiektów:
String str1 = new String("Hello!");
String str2 = new String("Hello!");
String str3 = "Hello!";
String str4 = "Hello!";
System.out.println( "str1 hashCode: " + str1.hashCode() + " created with String constructor" );
System.out.println( "str2 hashCode: " + str2.hashCode() + " created with String constructor" );
System.out.println( "str3 hashCode: " + str3.hashCode() );
System.out.println( "str4 hashCode: " + str4.hashCode() );
System.out.println();
System.out.println( "str1 identityHashCode: " + System.identityHashCode( str1 ) + " created with String constructor" );
System.out.println( "str2 identityHashCode: " + System.identityHashCode( str2 ) + " created with String constructor" );
System.out.println( "str3 identityHashCode: " + System.identityHashCode( str3 ) );
System.out.println( "str4 identityHashCode: " + System.identityHashCode( str4 ) );
Wynik:
str1 hashCode: -2137068113 created with String constructor
str2 hashCode: -2137068113 created with String constructor
str3 hashCode: -2137068113
str4 hashCode: -2137068113
str1 identityHashCode: 2755360 created with String constructor
str2 identityHashCode: 17259968 created with String constructor
str3 identityHashCode: 19993396
str4 identityHashCode: 19993396
Wniosek: Wszystkie cztery obiekty mają identyczny hashCode, lecz dwa obiekty utworzone przy użyciu konstruktora klasy String są ewidentnie osobnymi obiektami. Natomiast dwie zmienne utworzone przy użyciu zwykłego łańcucha znaków wskazują na ten sam pojedynczy obiekt klasy String. DLA WNIKLIWYCH: W rzeczywistości zmienne typu String utworzone przy użyciu prostego łańcucha znaków są zapisywane w tzw. puli stałych łańcuchowych (ang. String Constants Pool), która od Javy w wersji 8 znajduje się w specjalnym obszarze sterty (ang. heap). Obiekty typu String utworzone przy użyciu konstruktora new String( "..." ) są zapisywane tak, jak wszystkie inne obiekty, w głównym obszarze sterty. Tutaj są dodatkowe artykuły na ten temat:
https://javaranch.com/journal/200409/Journal200409.jsp#a1
W pokazanym niżej kodzie dwa pierwsze wiersze to dwa osobne obiekty o takich samych zawartościach. Jednakże w Javie te obiekty typu String będą traktowane tak, jakby były tym samym obiektem - anomalia ta dotyczy wyjątkowo klasy String. Natomiast dwa ostatnie wiersze to jeden obiekt, na który wskazują dwie zmienne - referencje.
String x1 = new String("x");
String x2 = new String("x");
String x3 = "x";
String x4 = "x";
Patrz także: Metoda equals() oraz Metoda hashCode().
Doświadczenie obrazujące działanie operatora == oraz metody equals() na obiektach klasy String oraz obiektach dowolnej innej klasy. Przyjmujemy, że w obiekcie MyObject występuje atrybut, w którym zapisany jest łańcuch znaków podany w konstruktorze obiektu - podobnie, jak w przypadku obiektów klasy String.
String str1 = new String( "aaa" );
String str2 = new String( "aaa" );
MyObject obj1 = new MyObject( "aaa" );
MyObject obj2 = new MyObject( "aaa" );
//-----------
if( str1==str2 ) // true
if( obj1==obj2 ) // false
if( str1.equals(str2) ) // true
if( obj1.equals(obj2) ) // false