Какова цель выражения "новая строка (...)" в Java? - PullRequest
60 голосов
/ 24 декабря 2008

Просматривая примеры кода в Интернете, я иногда сталкивался с назначением константы String для объекта String с помощью оператора new.

Например:

String s;
...
s = new String("Hello World");

Это, конечно, по сравнению с

s = "Hello World";

Я не знаком с этим синтаксисом и понятия не имею, какой будет цель или эффект. Поскольку строковые константы обычно хранятся в пуле констант, а затем в любом представлении, которое есть у JVM для работы со строковыми константами, будет ли что-либо выделено в куче?

Ответы [ 8 ]

71 голосов
/ 24 декабря 2008

Единственное место, где вам может понадобиться new String(String) - принудительно скопировать подстроку в новый массив символов, как в

small=new String(huge.substring(10,20))

Однако это поведение, к сожалению, не документировано и зависит от реализации.

Я был сожжен этим, когда читал большие файлы (некоторые размером до 20 МБ) в строку и разбивал ее на строки по факту. Я закончил со всеми строками для строк, ссылающихся на символ [], состоящий из всего файла. К сожалению, это непреднамеренно сохранило ссылку на весь массив для нескольких строк, которые я держал в течение более длительного времени, чем обработка файла - я был вынужден использовать new String(), чтобы обойти его.

Единственный способ сделать это независимым от реализации:

small=new String(huge.substring(10,20).toCharArray());

К сожалению, это должно копировать массив дважды, один раз для toCharArray() и один раз в конструкторе String.

Должен существовать документированный способ получения новой строки путем копирования символов существующей; или документация String(String) должна быть улучшена, чтобы сделать ее более явной (в ней есть смысл, но она довольно расплывчата и открыта для интерпретации).

Подводный камень предположения о том, что не указано в документе

В ответ на комментарии, которые продолжают поступать, понаблюдайте за реализацией Apache Harmony new String():

public String(String string) {
    value = string.value;
    offset = string.offset;
    count = string.count;
}

Правильно, там нет копии базового массива. И все же, он по-прежнему соответствует документации (Java 7) String в том смысле, что он:

Инициализирует вновь созданный объект String так, чтобы он представлял ту же последовательность символов, что и аргумент; другими словами, вновь созданная строка является копией строки аргумента. Если не требуется явная копия оригинала, использование этого конструктора не требуется, поскольку строки являются неизменяемыми.

Заметной частью является «копия аргумента string »; в нем не говорится «копия строки аргумента и базовый массив символов, поддерживающий строку».

Будьте внимательны при программировании документации , а не one реализация .

7 голосов
/ 24 декабря 2008

Единственный раз, когда я нашел это полезным, это объявление переменных блокировки:

private final String lock = new String("Database lock");

....

synchronized(lock)
{
    // do something
}

В этом случае инструменты отладки, такие как Eclipse, будут показывать строку при перечислении того, что блокирует поток в данный момент или ожидает. Вы должны использовать «новую строку», то есть выделить новый объект строки, потому что в противном случае литерал общей строки может быть заблокирован в каком-либо другом не связанном коде.

4 голосов
/ 18 января 2014

String s1 = "foo"; литерал будет идти в StringPool, а s1 будет ссылаться.

String s2 = "foo"; на этот раз он проверит, что литерал "foo" уже доступен в StringPool или нет, поскольку теперь он существует, поэтому s2 будет ссылаться на тот же литерал.

String s3 = new String ("foo"); Литерал "foo" будет сначала создан в StringPool, затем будет создан конструктор аргументов строки String Object, т.е. "foo" в куче из-за объекта создание через новый оператор, тогда s3 будет ссылаться на него.

String s4 = new String ("foo"); такой же, как s3

so System.out.println (s1 == s2); // true из-за буквального сравнения.

и System.out.println (s3 == s4); // false из-за сравнения объектов (s3 и s4 создаются в разных местах в куче)

2 голосов
/ 11 сентября 2013

Единственная утилита для этого конструктора, описанная Software Monkey и Ruggs, похоже, исчезла из JDK7. В классе String больше нет поля offset, а подстрока всегда использует

Arrays.copyOfRange(char[] original, int from, int to) 

, чтобы обрезать массив символов для копии.

1 голос
/ 24 декабря 2008

Ну, это зависит от того, что "..." в примере. Если это, например, StringBuffer, или байтовый массив, или что-то еще, вы получите строку, созданную из данных, которые вы передаете.

Но если это просто другая строка, как в new String("Hello World!"), то она должна быть заменена просто "Hello World!" во всех случаях. Строки являются неизменяемыми, поэтому клонирование не имеет смысла - просто более многословно и менее эффективно создавать новый объект String, который будет служить дубликатом существующей строки (будь то литеральная или другая переменная String, которая у вас уже есть).

Фактически, Effective Java (которую я настоятельно рекомендую) использует именно это как один из примеров «Избегайте создания ненужных объектов»:


В качестве крайнего примера того, чего не следует делать, рассмотрим следующее утверждение:

String s = new String("stringette");  **//DON'T DO THIS!**

(Эффективная Java, второе издание)

0 голосов
/ 15 мая 2013

Есть два способа создания строк в Java. Ниже приведены примеры обоих способов: 1) Объявите переменную типа String (класс в Java) и присвойте ей значение, которое должно быть заключено в двойные кавычки. Это создаст строку в области пула строк памяти. Например: String str = "JAVA";

2) Используйте конструктор класса String и передайте строку (в двойных кавычках) в качестве аргумента. Например: String s = новая строка ("JAVA"); Это создаст новую строку JAVA в основной памяти, а также в пуле строк, если эта строка еще не присутствует в пуле строк.

0 голосов
/ 24 декабря 2008

Я думаю, это будет зависеть от примеров кода, которые вы видите.

В большинстве случаев использование конструктора класса "new String ()" в примере кода только для того, чтобы показать очень хорошо известный класс Java вместо создания нового.

Вы должны избегать его использования в большинстве случаев. Не только потому, что строковые литералы интернированы, но главным образом потому, что строки неизменяемы. Не имеет смысла иметь две копии, которые представляют один и тот же объект.

Хотя статья, о которой упоминает Раггс, является "интересной" , ее не следует использовать, если нет особых обстоятельств, поскольку она может причинить больше вреда, чем пользы. Вы будете кодировать реализацию, а не спецификацию, и тот же код не сможет выполнить то же самое, например в JRockit, IBM VM или других.

0 голосов
/ 24 декабря 2008

Как правило, это означает, что кто-то не удовлетворен модным стилем объявления C ++ при инициализации.

В те времена C не считалось хорошей формой определять автоматические переменные во внутренней области видимости; C ++ устранил ограничение синтаксического анализатора, а Java расширил его.

Итак, вы видите код, который имеет

int q;
for(q=0;q<MAX;q++){
    String s;
    int ix;
    // other stuff
    s = new String("Hello, there!");
    // do something with s
}

В крайнем случае, все объявления могут быть в верхней части функции, а не во вложенных областях, таких как цикл for.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...