Струни - це об’єкти в Java, тож чому б ми не використали "new" для їх створення?


104

Ми зазвичай створюємо об'єкти за допомогою newключового слова, наприклад:

Object obj = new Object();

Струни - це об'єкти, але ми їх не newстворюємо:

String str = "Hello World";

Чому це? Чи можна зробити струну за допомогою new?


Ви також повинні ознайомитися з цим питанням stackoverflow.com/questions/456575/java-wrapper-equality-test
змінено

1
Тому що рядкові літерали - це вже об’єкти.
Маркіз Лорн

1
Зауважте, що new String(...)він використовувався для обходу деталі реалізації підстроки великих рядків. Це було виправлено в Java 7 і більше не потрібно.
Thorbjørn Ravn Andersen

Я 100-й любитель цієї посади. :)
Mukit09

Відповіді:


130

На додаток до того , що вже було сказано, строкові літерали [тобто рядки , як , "abcd"але не так, як new String("abcd")] в Java інтерновані - це означає , що кожен раз , коли ви звертаєтеся до «ABCD», ви отримаєте посилання на один Stringекземпляр, а не новий щоразу. Отже, у вас буде:

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

a == b; //True

але якби ти мав

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

то це можливо мати

a == b; // False

(і якщо комусь потрібно нагадати, завжди використовуйте .equals()для порівняння рядків; ==тести на фізичну рівність).

Літеральні рядки для інтернінгу хороші тим, що вони часто використовуються не раз. Наприклад, розгляньте (надуманий) код:

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

Якби у нас не було інтернування Strings, "Наступну ітерацію" потрібно було б створити 10 разів, тоді як зараз вона буде створена лише один раз.


1
Використовуючи String a = new String ("abcd"), чи означає це, що дві пам'яті з подібним вмістом присутні в пам'яті.
змінено

1
Правильно - компілятор не обов'язково перевірятиме, чи така струна вже була інтернована (хоча ви, звичайно, можете написати ту, що зробила).
danben

так, така оптимізація можлива, оскільки рядки незмінні і тому їх можна спільно використовувати без проблем. спільна обробка "asdf" - це реалізація дизайну "Flyweight".
мануель альдана

Ніхто не сказав, що це неможливо, тільки що це не гарантується. Це був ваш голос?
danben

Що ви маєте на увазі під "== тестами на рівність об'єктів"? Це не здається мені правдою, але, можливо, ти мав на увазі щось інше, ніж це, мабуть, означає.
Dawood ibn Kareem

32

Струни - це "спеціальні" об'єкти на Java. Дизайнери Java мудро вирішили, що рядки використовуються так часто, що їм потрібен власний синтаксис, а також стратегія кешування. Коли ви оголошуєте рядок, кажучи:

String myString = "something";

myString - це посилання на об'єкт String зі значенням "щось". Якщо ви згодом заявите:

String myOtherString = "something";

Java досить розумна, щоб розібратися в тому, що myString і myOtherString однакові і збереже їх у глобальній таблиці String як один і той же об’єкт. Він спирається на той факт, що ви не можете змінювати Strings для цього. Це зменшує необхідну кількість пам'яті і може зробити порівняння швидше.

Якщо натомість ви пишете

String myOtherString = new String("something");

Java створить для вас абсолютно новий об’єкт, відмінний від об’єкта myString.


Гей ... це не вимагає "нескінченної мудрості", щоб визнати потребу в якійсь синтаксичній підтримці рядкових літералів. Так само, як і будь-який інший серйозний дизайн мови програмування підтримує якусь строкову літералу.
Stephen C

12
Гіпербола була зведена до оглушення, капітане :)
Джеймі МакКріндл

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").

Сподіваюсь, це усуне кілька сумнівів. :)


Рядок d = новий рядок ("def"); // 1 Об'єкт + "def" додано до басейну -> тут "def" буде додано до пулу, лише якщо його ще немає
southerton

@southerton Безглуздо. Це вже є в басейні. Там її розмістив компілятор.
Маркіз Лорн

@EJP Чому (e == d) тут хибне? вони обидва посилаються на один і той же об'єкт "def" в пулі, правда?
Раджа

Рядок c = новий рядок ("abc"); // 1 Об'єкт ... чи правильно це твердження? Якщо "abc" вже посилається на постійний пул, то в чому полягає метод inter?
Prashanth Debbadwar

6

Це ярлик. Спочатку це було не так, але Java змінила це.

Цей FAQ коротко розповідає про це. Посібник із специфікації Java також розповідає про це. Але я не можу знайти його в Інтернеті.


2
Перервана посилання, і я не знаю жодних інших доказів того, що воно коли-небудь змінювалося.
Маркіз Лорн

1
@EJP Він все ще знаходиться в машині зворотного зв'язку, якщо це корисно.
Ар'ян

6

Рядок підлягає декільком оптимізаціям (на користь кращої фрази). Зауважте, що String також має перевантаження оператора (для оператора +) - на відміну від інших об'єктів. Так що це дуже особливий випадок.


1
Фактично + - це оператор, який перекладається на виклик StringBuilder.append (..).
віскісієра

5

Ми зазвичай використовуємо рядкові рядки, щоб не створювати зайвих об'єктів. Якщо ми будемо використовувати новий оператор для створення об'єкта String, він створюватиме новий об'єкт щоразу.

Приклад:

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

Для наведеного вище коду в пам'яті:

введіть тут опис зображення


2

У Java, Strings - це окремий випадок, з багатьма правилами, які застосовуються лише до Strings. Подвійні лапки змушують компілятор створити об'єкт String. Оскільки об'єкти String є незмінними, це дозволяє компілятору інтернувати декілька рядків та створювати більший пул рядків. Дві однакові константи String завжди матимуть однакові посилання на об'єкт. Якщо ви не хочете, щоб це було так, ви можете використовувати новий String (""), і це створить об'єкт String під час виконання. Метод intern () використовувався як загальний, щоб викликати динамічно створені рядки для перевірки таблиці таблиць пошуку рядків. Як тільки рядок у інтернеті, посилання на об'єкт вказуватиме на канонічний екземпляр 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

Коли завантажувач класів завантажує клас, всі струнні константи додаються до пулу String.


"Подвійні лапки змушують компілятор створити об'єкт String." занижений коментар
csguy

0

Синтаксичний цукор. The

String s = new String("ABC");

синтаксис все ще доступний.


1
Це не зовсім правильно. s = новий рядок ("ABC") не дасть вам таких самих результатів, як s = "ABC". Дивіться коментар danben.
Стів Б.

2
Крім того, дещо іронічно, він спочатку створить екземпляр String, що представляє "ABC" inline, а потім передасть це як аргумент виклику конструктора, який створить повернення String з однаковим значенням.
Анджей Дойл

1
Прийнятним випадком використання цього конструктора є те String small = new String(huge.substring(int, int));, що дозволяє переробити велику основу char[]в оригінальній рядку huge.
Паскаль Thivent

1
@PascalThivent так, але більше не з Java 8. Він більше не ділиться масивами (під час підготовки до інших оптимізацій, таких як автоматична дедуплікація рядків з G1 або майбутнє стиснення рядків).
eckes

@AndrzejDoyle Неправильно. Компілятор створює об'єкт для літералу.
Маркіз Лорн

0

Ви все ще можете використовувати new String("string"), але було б важче створити нові рядки без рядкових літералів ... вам доведеться використовувати символьні масиви чи байти :-) У рядкових літералів є ще одне додаткове властивість: всі ті ж літеральні рядки з будь-якої точки класу до тієї ж строки екземпляр (вони інтерновані).


0

Майже немає необхідності в новому рядку, оскільки літерал (символи в лапках) - це вже об'єкт String, створений при завантаженні хост-класу. Цілком законно посилатися на методи буквально і доно, головна відмінність - зручність, яку надають літерали. Було б великим болем і марною тратою зубів, якби нам довелося створити масив символів і заповнити його char за допомогою char і зробити їх новим String (char array).


0

Сміливо створіть нову стрічку

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

Звичайна позначення s = "new String";є більш-менш зручним ярликом - який слід використовувати з міркувань продуктивності, за винятком тих досить рідкісних випадків, коли вам дійсно потрібні рядки, які відповідають вимогам рівняння

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

EDIT

У відповідь на зауваження: це не мало бути порадою, а лише лише прямою відповіддю на тезу запитуючих, що ми не використовуємо «нове» ключове слово для Strings, що просто не відповідає дійсності. Сподіваюсь, що ця редакція (включаючи вищевикладене) це трохи уточнить BTW - є декілька хороших і набагато кращих відповідей на вищезазначене питання про SO.


4
-1 - Погана порада. Ви НЕ повинні «почувати себе вільними» використовувати new String(...)НЕ БЕЗПЕЧНО, щоб ваша програма ПОТРІБНА, щоб ви створили рядок з чіткою ідентичністю.
Стівен C

1
Я це знаю. Редагував пост для роз'яснення.
Андреас Долк

0

Буквальний пул містить будь-які рядки, створені без використання ключового слова new.

Існує різниця: String без нової посилання зберігається в Stral bural pool, а String with new говорить, що вони знаходяться в купі пам'яті.

Рядок з новим є в іншому місці в пам'яті, як і будь-який інший об'єкт.


0

Тому що String - це непорушний клас в java.

Тепер чому це незмінне? Оскільки String є незмінним, тому його можна ділити між декількома потоками, і нам не потрібно синхронізувати операцію String зовні. Як String також використовується в механізмі завантаження класів. Тож якби String був змінним, тоді java.io.writer міг бути змінений на 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");

Вихід:

Правда

Я створив два окремих об’єкти, в обох є поле (ref) "Ім'я". Тож навіть у цьому випадку "Ян Пітер" ділиться, якщо я розумію спосіб боротьби з java ..


0

Ну а StringPool реалізований за допомогою The Hashmap in java. Якщо ми завжди створюємо нове ключове слово, воно не шукає в String Pool і створює нову пам’ять для нього, яка може знадобитися пізніше, якщо у нас запущена операція з великою пам’яттю і якщо ми створюємо всі рядки з новим ключовим словом, яке вплине на продуктивність нашого додатку. Тому доцільно не використовувати нові ключові слова для створення рядка, оскільки тоді він лише перейде до String пулу, який, в свою чергу, є Hashmap, (пам'ять збережена, уявіть, якщо у нас багато рядків, створених за допомогою нового ключового слова), тут вона буде збережена і якщо рядок вже існує, посилання на нього (яке зазвичай міститься в пам'яті стека) буде повернуто до новоствореної рядка. Так зроблено для підвищення продуктивності.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.