Łańcuchy są obiektami w Javie, więc dlaczego nie użyjemy „nowego” do ich utworzenia?


104

Zwykle tworzymy obiekty za pomocą newsłowa kluczowego, na przykład:

Object obj = new Object();

Ciągi znaków to obiekty, ale nie używamy ich newdo ich tworzenia:

String str = "Hello World";

Dlaczego to? Czy mogę zrobić string z new?


Powinieneś także rzucić okiem na to pytanie stackoverflow.com/questions/456575/java-wrapper-equality-test
zmieniono

1
Ponieważ literały łańcuchowe są już obiektami.
Markiz Lorne,

1
Należy zauważyć, że new String(...)zostało to użyte do obejścia szczegółów implementacji podczas tworzenia podciągów dużych ciągów. Zostało to naprawione w Javie 7 i nie jest już konieczne.
Thorbjørn Ravn Andersen

Jestem setnym polubieniem tego posta. :)
Mukit09

Odpowiedzi:


130

Oprócz tego, co już zostało powiedziane, literały ciągów [tj. Ciągi znaków takie jak, "abcd"ale nie lubią new String("abcd")] w Javie są internalizowane - oznacza to, że za każdym razem, gdy odwołujesz się do „abcd”, otrzymujesz odniesienie do pojedynczej Stringinstancji, a nie do nowej za każdym razem. Więc będziesz mieć:

String a = "abcd";
String b = "abcd";

a == b; //True

ale gdybyś miał

String a = new String("abcd");
String b = new String("abcd");

wtedy jest to możliwe

a == b; // False

(a na wypadek, gdyby ktoś potrzebował przypomnienia, zawsze używaj .equals()do porównania ciągów; ==testy na równość fizyczną).

Internowanie literałów String jest dobre, ponieważ często są używane więcej niż raz. Na przykład rozważ (wymyślony) kod:

for (int i = 0; i < 10; i++) {
  System.out.println("Next iteration");
}

Gdybyśmy nie mieli internowania ciągów znaków, „Następna iteracja” musiałaby zostać zainicjowana 10 razy, podczas gdy teraz zostanie utworzona tylko raz.


1
Czy używając String a = new String ("abcd"), oznacza to, że w pamięci znajdują się dwa łańcuchy o podobnej zawartości.
zmienił

1
Racja - kompilator niekoniecznie sprawdzi, czy taki ciąg został już internowany (chociaż z pewnością można napisać taki, który to zrobił).
danben

tak, ta optymalizacja jest możliwa, ponieważ ciągi znaków są niezmienne i dlatego można je bez problemu udostępniać. wspólna obsługa „asdf” jest implementacją wzorca projektowego „Flyweight”.
manuel aldana

Nikt nie powiedział, że to niemożliwe, tylko że nie jest to gwarantowane. Czy to był twój głos przeciw?
danben

Co masz na myśli przez „== testy równości obiektów”? Nie wydaje mi się to prawdą, ale być może miałeś na myśli coś innego niż to, co zdaje się oznaczać.
Dawood ibn Kareem

32

Łańcuchy to „specjalne” obiekty w Javie. Projektanci Javy mądrze zdecydowali, że ciągi znaków są używane tak często, że potrzebowali własnej składni oraz strategii buforowania. Kiedy deklarujesz ciąg, mówiąc:

String myString = "something";

myString jest odwołaniem do obiektu String z wartością „coś”. Jeśli później zadeklarujesz:

String myOtherString = "something";

Java jest wystarczająco sprytna, aby dowiedzieć się, że myString i myOtherString są takie same i przechowują je w globalnej tabeli String jako ten sam obiekt. Polega on na tym, że nie możesz modyfikować ciągów znaków, aby to zrobić. Zmniejsza to ilość wymaganej pamięci i może przyspieszyć porównania.

Jeśli zamiast tego napiszesz

String myOtherString = new String("something");

Java utworzy dla Ciebie zupełnie nowy obiekt, inny niż obiekt myString.


Hej ... nie wymaga "nieskończonej mądrości", aby rozpoznać potrzebę jakiegoś rodzaju wsparcia syntaktycznego dla literałów łańcuchowych. Prawie każdy inny poważny projekt języka programowania obsługuje jakiś rodzaj literału ciągu.
Stephen C,

12
Hiperbola została zredukowana do ogłuszenia, Kapitanie :)
Jamie McCrindle

16
String a = "abc"; // 1 Object: "abc" added to pool

String b = "abc"; // 0 Object: because it is already in the pool

String c = new String("abc"); // 1 Object

String d = new String("def"); // 1 Object + "def" is added to the Pool

String e = d.intern(); // (e==d) is "false" because e refers to the String in pool

String f = e.intern(); // (f==e) is "true" 

//Total Objects: 4 ("abc", c, d, "def").

Mam nadzieję, że to wyjaśnia kilka wątpliwości. :)


String d = new String ("def"); // 1 Obiekt + "def" jest dodawany do puli -> tutaj "def" zostanie dodane do puli tylko wtedy, gdy go tam jeszcze nie ma
Southerton

@southerton Meaningless. Jest już w basenie. Został tam umieszczony przez kompilator.
Markiz Lorne,

@EJP dlaczego (e == d) jest tutaj fałszywe? oba odnoszą się do tego samego obiektu „def” w puli, prawda?
Raja,

String c = new String ("abc"); // 1 Obiekt ... czy to stwierdzenie jest poprawne? Jeśli odwołanie do „abc” już się znajduje z puli stałych, to jaki jest pożytek z metody inter?
Prashanth Debbadwar

6

To jest skrót. Pierwotnie tak nie było, ale Java to zmieniła.

W tym FAQ omówiono to krótko. Poradnik specyfikacji języka Java również o tym mówi. Ale nie mogę znaleźć tego online.


2
Zerwane łącze i nie są mi znane żadne inne dowody na to, że zostało kiedykolwiek zmienione.
Markiz Lorne,

1
@EJP Jeśli jest to przydatne , nadal jest w drodze powrotnej .
Arjan

6

String podlega kilku optymalizacjom (z braku lepszej frazy). Zauważ, że String ma również przeciążenie operatorów (dla operatora +) - w przeciwieństwie do innych obiektów. Jest to więc bardzo szczególny przypadek.


1
Znak + jest w rzeczywistości operatorem, który jest tłumaczony na wywołanie StringBuilder.append (..).
whiskeysierra

5

Zwykle używamy literałów String, aby uniknąć tworzenia niepotrzebnych obiektów. Jeśli użyjemy operatora new do stworzenia obiektu String, to za każdym razem będzie on tworzył nowy obiekt.

Przykład:

String s1=“Hello“;
String s2=“Hello“;
String s3= new String(“Hello“);
String s4= new String(“Hello“);

Dla powyższego kodu w pamięci:

wprowadź opis obrazu tutaj


2

W Javie ciągi są szczególnym przypadkiem, z wieloma regułami, które dotyczą tylko ciągów. Podwójne cudzysłowy powodują, że kompilator tworzy obiekt String. Ponieważ obiekty String są niezmienne, umożliwia to kompilatorowi internowanie wielu ciągów i tworzenie większej puli ciągów. Dwie identyczne stałe typu String będą zawsze miały to samo odniesienie do obiektu. Jeśli nie chcesz, aby tak było, możesz użyć new String („”), co spowoduje utworzenie obiektu String w czasie wykonywania. Metoda intern () była kiedyś powszechna i powodowała, że ​​dynamicznie tworzone łańcuchy były porównywane z tabelą wyszukiwania ciągów. Po umieszczeniu łańcucha w internacie odwołanie do obiektu będzie wskazywać na kanoniczną instancję String.

    String a = "foo";
    String b = "foo";
    System.out.println(a == b); // true
    String c = new String(a);
    System.out.println(a == c); // false
    c = c.intern();
    System.out.println(a == c); // true

Gdy classloader ładuje klasę, wszystkie stałe typu String są dodawane do puli String.


„Podwójne cudzysłowy powodują, że kompilator tworzy obiekt typu String”. niedoceniany komentarz
csguy

0

Cukier syntaktyczny. Plik

String s = new String("ABC");

składnia jest nadal dostępna.


1
To nie jest całkiem w porządku. s = new String („ABC”) nie daje takich samych wyników, jak s = „ABC”. Zobacz komentarz danben.
Steve B.

2
Co więcej, nieco ironicznie, najpierw utworzy instancję String reprezentującą „ABC” inline - a następnie przekaże ją jako argument do wywołania konstruktora, co zwróci String o identycznej wartości.
Andrzej Doyle

1
Prawidłowy przypadek użycia tego konstruktora to String small = new String(huge.substring(int, int));, który umożliwia odtworzenie dużego źródła char[]z oryginalnego hugeString.
Pascal Thivent

1
@PascalThivent tak, ale już nie w Javie 8. Nie udostępnia już tablic (w ramach przygotowań do innych optymalizacji, takich jak automatyczna deduplikacja ciągów z G1 lub nadchodząca kompresja ciągów).
eckes

@AndrzejDoyle Nieprawidłowa. Kompilator tworzy obiekt dla literału.
Markiz Lorne,

0

Nadal możesz używać new String("string"), ale byłoby trudniej tworzyć nowe ciągi bez literałów łańcuchowych ... musiałbyś używać tablic znaków lub bajtów :-) Literały ciągów mają jedną dodatkową właściwość: wszystkie te same literały ciągów z dowolnego punktu klasy do tego samego ciągu instancja (są internowani).


0

Nie ma prawie potrzeby tworzenia nowego ciągu znaków, ponieważ literał (znaki w cudzysłowie) jest już obiektem typu String utworzonym podczas ładowania klasy hosta. Przywoływanie metod dosłownie i don jest całkowicie legalne, głównym rozróżnieniem jest wygoda, jaką zapewniają dosłowne. Byłoby wielkim problemem i stratą czasu, gdybyśmy musieli stworzyć tablicę znaków i wypełnić ją char po znaku, a oni zrobiliby nowy String (tablicę znaków).


0

Zapraszam do tworzenia nowego ciągu za pomocą

String s = new String("I'm a new String");

Zwykła notacja s = "new String";jest mniej więcej wygodnym skrótem - którego należy używać ze względu na wydajność, z wyjątkiem tych dość rzadkich przypadków, w których naprawdę potrzebujesz ciągów znaków, które kwalifikują się do równania

(string1.equals(string2)) && !(string1 == string2)

EDYTOWAĆ

W odpowiedzi na komentarz: nie miała to być porada, a jedynie bezpośrednia odpowiedź na tezę pytających, że nie używamy słowa kluczowego „nowe” dla Strings, co po prostu nie jest prawdą. Mam nadzieję, że ta zmiana (w tym powyższa) trochę to wyjaśnia. Przy okazji - jest kilka dobrych i znacznie lepszych odpowiedzi na powyższe pytanie dotyczące SO.


4
-1 - Zła rada. Nie powinieneś "swobodnie" używać, new String(...)chyba że twoja aplikacja WYMAGA od ciebie stworzenia łańcucha o odrębnej tożsamości.
Stephen C,

1
Wiem to. Edytowałem post w celu wyjaśnienia.
Andreas Dolk,

0

Pula literałów zawiera wszystkie ciągi znaków, które zostały utworzone bez użycia słowa kluczowego new .

Jest różnica: ciąg znaków bez nowego odwołania jest przechowywany w puli literałów String, a ciąg znaków z new mówi, że znajdują się w pamięci sterty.

Łańcuchy z new są gdzie indziej w pamięci, tak jak każdy inny obiekt.


0

Ponieważ String jest niezmienną klasą w java.

Dlaczego jest niezmienny? Ponieważ String jest niezmienny, więc może być współużytkowany między wieloma wątkami i nie musimy synchronizować operacji String na zewnątrz. As String jest również używany w mechanizmie ładowania klas. Jeśli więc ciąg String był zmienny, wówczas java.io.writer mógł zostać zmieniony na abc.xyz.mywriter


0
TString obj1 = new TString("Jan Peter");                
TString obj2 = new TString("Jan Peter");                    

if (obj1.Name == obj2.Name)                 
    System.out.println("True");
else                
    System.out.println("False");

Wynik:

Prawdziwe

Stworzyłem dwa oddzielne obiekty, oba mają pole (ref) „Name”. Więc nawet w tym przypadku "Jan Peter" jest udostępniany, jeśli rozumiem sposób, w jaki działa java.


0

Cóż, StringPool jest zaimplementowany przy użyciu The Hashmap w java. Jeśli tworzymy zawsze z nowym słowem kluczowym, nie wyszukuje go w String Pool i tworzy dla niego nową pamięć, która może być potrzebna później, jeśli mamy uruchomioną operację intensywnie wykorzystującą pamięć i jeśli tworzymy wszystkie ciągi z nowym słowem kluczowym, które wpłynęłoby na wydajność naszej aplikacji. Dlatego zaleca się, aby nie używać nowych słów kluczowych do tworzenia ciągu znaków, ponieważ tylko wtedy trafi on do puli String, która z kolei jest Hashmap (pamięć zapisana, wyobraź sobie, że mamy wiele ciągów utworzonych za pomocą nowego słowa kluczowego) tutaj zostanie on zapisany i jeśli ciąg już istnieje, odniesienie do niego (które zwykle znajduje się w pamięci Stack) zostanie zwrócone do nowo utworzonego ciągu. Zrobiono więc, aby poprawić wydajność.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.