Строки - это объекты в Java, так почему бы нам не использовать «новый» для их создания?


106

Обычно мы создаем объекты, используя 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 и больше не требуется.
Торбьёрн Равн Андерсен

Я 100-й лайкнул этот пост. :)
Mukit09

Ответы:


131

В дополнение к тому, что уже было сказано, строковые литералы [т. Е. Строки, подобные, "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");
}

Если бы у нас не было интернирования строк, «Следующая итерация» должна была бы быть создана 10 раз, тогда как теперь она будет создана только один раз.


1
Использование String a = new String ("abcd") означает, что в памяти присутствуют две строки с одинаковым содержимым.
изменено

1
Правильно - компилятор не обязательно будет проверять, была ли такая строка уже интернирована (хотя вы, конечно, можете написать такую, которая это сделала).
danben 05

да, такая оптимизация возможна, потому что строки неизменяемы и поэтому могут использоваться совместно без проблем. общая обработка asdf является реализацией шаблона проектирования «Легковес».
мануэль алдана 06

Никто не сказал, что это невозможно, только то, что это не гарантируется. Это был ваш голос против?
danben 06

Что вы имеете в виду, говоря «== тесты на равенство объектов»? Мне это не кажется правдой, но, возможно, вы имели в виду нечто иное, чем то, что кажется.
Давуд ибн Карим

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 в пуле, верно?
Raja

Строка c = новая строка («abc»); // 1 Объект ... это утверждение верно? Если на «abc» уже есть ссылка из пула констант, тогда какова польза от метода inter?
Прашант Деббадвар

6

Это ярлык. Изначально это было не так, но Java изменила это.

В этом FAQ кратко об этом говорится. Об этом также говорится в руководстве по спецификациям Java. Но я не могу найти его в Интернете.


2
Неработающая ссылка, и я не знаю других доказательств того, что она когда-либо изменялась.
Маркиз Лорн,

1
@EJP Если это пригодится, он все еще находится на обратном пути.
Arjan

6

Строка подверглась нескольким оптимизациям (из-за отсутствия лучшей фразы). Обратите внимание, что String также имеет перегрузку оператора (для оператора +) - в отличие от других объектов. Так что это особый случай.


1
+ - это фактически оператор, который переводится в вызов StringBuilder.append (..).
Whkeysierra

5

Обычно мы используем строковые литералы, чтобы избежать создания ненужных объектов. Если мы используем оператор new для создания объекта String, он будет каждый раз создавать новый объект.

Пример:

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

Для приведенного выше кода в памяти:

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


2

В Java строки - это особый случай, многие правила применяются только к строкам. Двойные кавычки заставляют компилятор создать объект String. Поскольку объекты String неизменяемы, это позволяет компилятору вставлять несколько строк и создавать пул строк большего размера. Две идентичные строковые константы всегда будут иметь одну и ту же ссылку на объект. Если вы не хотите, чтобы это было так, вы можете использовать new 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.


«Двойные кавычки заставляют компилятор создать объект String». недооцененный комментарий
csguy

0

Синтаксический сахар. В

String s = new String("ABC");

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


1
Это не совсем так. s = new String ("ABC") не даст таких же результатов, как s = "ABC". См. Комментарий Данбена.
Стив Б.

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

1
Допустимый вариант использования этого конструктора: он String small = new String(huge.substring(int, int));позволяет повторно использовать большой базовый объект char[]из исходной hugeString.
Паскаль Тивент

1
@PascalThivent - да, но больше не с Java 8. Он больше не разделяет массивы (в рамках подготовки к другим оптимизациям, таким как автоматическая дедупликация строк с помощью G1 или предстоящее сжатие строк).
eckes

@AndrzejDoyle Неправильно. Компилятор создает объект для литерала.
Маркиз Лорн,

0

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


0

Практически нет необходимости создавать новую строку, поскольку литерал (символы в кавычках) уже является объектом String, созданным при загрузке класса хоста. Совершенно законно вызывать методы для литералов и доноров, главное отличие - удобство, обеспечиваемое литералами. Было бы большой проблемой и пустой тратой времени, если бы нам пришлось создать массив символов и заполнить его char за char, а они сделали бы новую строку (массив char).


0

Не стесняйтесь создавать новую строку с

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

Обычная нотация s = "new String";- это более или менее удобный ярлык, который следует использовать по соображениям производительности, за исключением тех довольно редких случаев, когда вам действительно нужны строки, которые подходят для уравнения

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

РЕДАКТИРОВАТЬ

В ответ на комментарий: это не предназначалось для того, чтобы быть советом, а всего лишь прямым ответом на тезис вопрошающего, что мы не используем ключевое слово «новое» для строк, что просто неверно. Надеюсь, это изменение (включая приведенное выше) немного проясняет это. Кстати, есть несколько хороших и гораздо лучших ответов на вышеупомянутый вопрос о SO.


4
-1 - Плохой совет. Вы НЕ должны «чувствовать себя свободно» при использовании, new String(...)ЕСЛИ ваше приложение НЕ ТРЕБУЕТ, чтобы вы создавали String с отдельным идентификатором.
Stephen C

1
Я знаю это. Отредактировал пост для уточнения.
Андреас Долк

0

Пул литералов содержит любые строки, созданные без использования ключевого слова new.

Есть разница: строка без новой ссылки хранится в пуле строковых литералов, а строка с новой говорит, что они находятся в памяти кучи.

Строки с new находятся где-то в памяти, как и любой другой объект.


0

Поскольку String - неизменный класс в java.

Теперь почему он неизменен? Поскольку String является неизменяемым, его можно использовать в нескольких потоках, и нам не нужно синхронизировать операцию String извне. As 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");

Вывод:

Правда

Я создал два отдельных объекта, у обоих есть поле (ссылка) «Имя». Так что даже в этом случае "Ян Питер" является общим, если я понимаю, как работает Java.


0

Ну, StringPool реализован с использованием Hashmap в java. Если мы всегда создаем с новым ключевым словом, он не выполняет поиск в String Pool и не создает для него новую память, которая может понадобиться позже, если у нас выполняется операция с интенсивным использованием памяти, и если мы создаем все строки с новым ключевым словом, которое повлияет на производительность нашего приложения. Поэтому рекомендуется не использовать новые ключевые слова для создания строки, потому что тогда только она перейдет в пул строк, который, в свою очередь, является Hashmap (память сохранена, представьте, если у нас есть много строк, созданных с новым ключевым словом), здесь он будет сохранен и если строка уже существует, ссылка на нее (которая обычно находится в памяти стека) будет возвращена во вновь созданную строку. Итак, это сделано для повышения производительности.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.