В чем разница между «текстом» и новой строкой («текст»)?


195

В чем разница между этими двумя утверждениями?

String s = "text";

String s = new String("text");


Кто-нибудь, пожалуйста, ответьте на это. String a = "Java"; String b = "Java"; System.out.println (a == b); true // но System.out.println ("a == b?" + a == b); // ложь ...
Энергия

я не понимаю, когда я добавил какой-то комментарий ("a == b?) => мой результат становится ЛОЖНЫМ. почему?
Energy

@Energy Результатом является то, falseчто порядок операций диктует, что оператор + идет первым, объединяя "a == b?" с, чтобы создать строку "a == b? Java". Тогда выражение "a==b?Java" == bоценивается как ложное.
Эллисон Б

@AllisonB получил это, спасибо большое!
Энергия

Ответы:


187

new String("text"); явно создает новый и референциально отличный экземпляр Stringобъекта; String s = "text";может повторно использовать экземпляр из пула строковых констант, если он доступен.

Вы очень редко бы хотели использовать new String(anotherString)конструктор. Из API:

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

Смежные вопросы


Что означает ссылочное различие

Изучите следующий фрагмент:

    String s1 = "foobar";
    String s2 = "foobar";

    System.out.println(s1 == s2);      // true

    s2 = new String("foobar");
    System.out.println(s1 == s2);      // false
    System.out.println(s1.equals(s2)); // true

==на двух ссылочных типах есть сравнение ссылочной идентичности. Два объекта, equalsкоторые не обязательно ==. Обычно неправильно использовать ==ссылочные типы; большую часть времени equalsнужно использовать вместо этого.

Тем не менее, если по какой-либо причине вам нужно создать две строки, equalsно не ==строку, вы можете использовать new String(anotherString)конструктор. Однако нужно еще раз сказать, что это очень своеобразно и редко является намерением.

Ссылки

Связанные вопросы


3
Если я напишу: String s = new String ("abc"); А теперь я пишу: String s = "abc"; Will String s = "abc"; создать новый строковый литерал в пуле строк?
Kaveesh Kanwal

Почему никто не отвечает на предыдущий вопрос?
Зедс

2
@KaveeshKanwal Нет, литерал не будет дублироваться. Как видите, есть 2 "abc"с. Только один из них пойдет в пул String, а другой будет ссылаться на него. Тогда есть то, sчто будет правильным новым объектом.
Каяман

1
@Kaveesh Kanwal - String s = new String ("abc") будет создавать только новый объект String со значением "abc". И 2-й оператор проверит, присутствует ли какой-либо строковый литерал abc в String Pool или нет. Если он уже присутствует, возвращается ссылка на существующий, а если нет, то в пуле String создается новый литерал ("abc"). Надеюсь, это решит ваш запрос!
user968813

В этом нет «мая». Компилятор должен объединять строковые литералы. JLS 3.10.5 .
Маркиз Лорн

119

Строковые литералы войдут в String Constant Pool .

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

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


Создание объекта построчно:

String str1 = new String("java5");

Используя строковый литерал "java5" в конструкторе, новое строковое значение сохраняется в пуле строковых констант. Используя оператор new, в куче создается новый строковый объект со значением «java5».

String str2 = "java5"

Ссылка "str2" указывает на уже сохраненное значение в пуле строковых констант

String str3 = new String(str2);

В куче создается новый строковый объект с тем же значением, что и у ссылки "str2"

String str4 = "java5";

Ссылка "str4" указывает на уже сохраненное значение в пуле строковых констант

Всего объектов: куча - 2, бассейн - 1

Дальнейшее чтение в сообществе Oracle


1
Хороший ответ ... но хотелось бы знать, что теперь я собираюсь изменить значение str1 = "java6", тогда это изменит значение str4?
CoronaPintu

2
да, я проверил, это не изменит значение str4
CoronaPintu

@Braj Можете ли вы предоставить документацию с утверждением вашего Ответчика?
Василий Бурк

@Braj: Заголовки для «Кучи» и «пула» в таблице должны быть обратными?
Рахул Куруп

Не верно. Постоянный пул создается во время компиляции, а не во время выполнения. Не используйте форматирование цитаты для текста, который не цитируется.
Маркиз Лорн

15

Создается строка в пуле строковых констант

String s = "text";

другой создает строку в константе pool ( "text"), а другую - в обычном пространстве кучи ( s). Обе строки будут иметь одинаковое значение «text».

String s = new String("text");

s затем теряется (имеет право на GC), если впоследствии не используется.

Строковые литералы, с другой стороны, используются повторно. Если вы используете "text"в нескольких местах вашего класса, на самом деле это будет одна и только одна строка (т.е. несколько ссылок на одну и ту же строку в пуле).


Строки в постоянном пуле никогда не теряются. Вы хотели сказать, что 's' потеряно, если позже не используется?
маркиз Лорн

@EJP: да, я имел в виду "с". Спасибо, что заметили. Я исправлю вопрос.

9

JLS

Концепция называется «интернирование» в JLS.

Соответствующий отрывок из JLS 7 3.10.5 :

Более того, строковый литерал всегда ссылается на один и тот же экземпляр класса String. Это связано с тем, что строковые литералы - или, в более общем смысле, строки, являющиеся значениями константных выражений (§15.28) - «интернированы», чтобы обмениваться уникальными экземплярами, используя метод String.intern.

Пример 3.10.5-1. Строковые литералы

Программа, состоящая из модуля компиляции (§7.3):

package testPackage;
class Test {
    public static void main(String[] args) {
        String hello = "Hello", lo = "lo";
        System.out.print((hello == "Hello") + " ");
        System.out.print((Other.hello == hello) + " ");
        System.out.print((other.Other.hello == hello) + " ");
        System.out.print((hello == ("Hel"+"lo")) + " ");
        System.out.print((hello == ("Hel"+lo)) + " ");
        System.out.println(hello == ("Hel"+lo).intern());
    }
}
class Other { static String hello = "Hello"; }

и блок компиляции:

package other;
public class Other { public static String hello = "Hello"; }

производит вывод:

true true true true false true

JVMs

JVMS 7 5.1 говорит :

Строковый литерал является ссылкой на экземпляр класса String и является производным от структуры CONSTANT_String_info (§4.4.3) в двоичном представлении класса или интерфейса. Структура CONSTANT_String_info дает последовательность кодовых точек Unicode, составляющих строковый литерал.

Язык программирования Java требует, чтобы идентичные строковые литералы (то есть литералы, которые содержат одинаковую последовательность кодовых точек) должны ссылаться на один и тот же экземпляр класса String (JLS §3.10.5). Кроме того, если метод String.intern вызывается для какой-либо строки, результатом является ссылка на тот же экземпляр класса, который будет возвращен, если эта строка появится в виде литерала. Таким образом, следующее выражение должно иметь значение true:

("a" + "b" + "c").intern() == "abc"

Для получения строкового литерала виртуальная машина Java проверяет последовательность кодовых точек, заданных структурой CONSTANT_String_info.

  • Если метод String.intern ранее вызывался для экземпляра класса String, содержащего последовательность кодовых точек Unicode, идентичную той, которая задана структурой CONSTANT_String_info, то результатом строкового литерального вывода является ссылка на тот же экземпляр класса String.

  • В противном случае создается новый экземпляр класса String, содержащий последовательность кодовых точек Unicode, заданную структурой CONSTANT_String_info; ссылка на этот экземпляр класса является результатом строкового литерала. Наконец, метод intern нового экземпляра String вызывается.

Bytecode

Также полезно взглянуть на реализацию байт-кода в OpenJDK 7.

Если мы декомпилируем:

public class StringPool {
    public static void main(String[] args) {
        String a = "abc";
        String b = "abc";
        String c = new String("abc");
        System.out.println(a);
        System.out.println(b);
        System.out.println(a == c);
    }
}

у нас по постоянному пулу:

#2 = String             #32   // abc
[...]
#32 = Utf8               abc

и main:

 0: ldc           #2          // String abc
 2: astore_1
 3: ldc           #2          // String abc
 5: astore_2
 6: new           #3          // class java/lang/String
 9: dup
10: ldc           #2          // String abc
12: invokespecial #4          // Method java/lang/String."<init>":(Ljava/lang/String;)V
15: astore_3
16: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
19: aload_1
20: invokevirtual #6          // Method java/io/PrintStream.println:(Ljava/lang/String;)V
23: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
26: aload_2
27: invokevirtual #6          // Method java/io/PrintStream.println:(Ljava/lang/String;)V
30: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
33: aload_1
34: aload_3
35: if_acmpne     42
38: iconst_1
39: goto          43
42: iconst_0
43: invokevirtual #7          // Method java/io/PrintStream.println:(Z)V

Обратите внимание, как:

  • 0и 3: ldc #2загружается одна и та же константа (литералы)
  • 12: создается новый экземпляр строки (с #2аргументом as)
  • 35: aи cсравниваются как обычные объекты сif_acmpne

Представление константных строк довольно волшебно в байт-коде:

  • у него есть специальная структура CONSTANT_String_info , в отличие от обычных объектов (например new String)
  • структура указывает на структуру CONSTANT_Utf8_info, которая содержит данные. Это единственные необходимые данные для представления строки.

и цитата JVMs выше , кажется, говорят , что всякий раз , когда utf8 указал на то же самое, то идентичные экземпляры загружаются ldc.

Я сделал аналогичные тесты для полей, и:

  • static final String s = "abc"указывает на таблицу констант через атрибут ConstantValue
  • не финальные поля не имеют этого атрибута, но все еще могут быть инициализированы с ldc

Вывод : есть прямая поддержка байт-кода для пула строк, и представление памяти эффективно.

Бонус: сравните это с целочисленным пулом , который не имеет прямой поддержки байт-кода (т.е. не имеет CONSTANT_String_infoаналогов).


2

@Braj: я думаю, что вы упомянули об обратном. Пожалуйста, поправьте меня, если я ошибаюсь

Создание объекта построчно:

String str1 = новая строка ("java5")

   Pool- "java5" (1 Object)

   Heap - str1 => "java5" (1 Object)

String str2 = "java5"

  pool- str2 => "java5" (1 Object)

  heap - str1 => "java5" (1 Object)

String str3 = новая строка (str2)

  pool- str2 => "java5" (1 Object)

  heap- str1 => "java5", str3 => "java5" (2 Objects)

String str4 = "java5"

  pool - str2 => str4 => "java5" (1 Object)

  heap - str1 => "java5", str3 => "java5" (2 Objects)

str1не участвует в стоимости str2или str3или str4каким-либо образом.
Маркиз Лорн

1

Думайте о "bla"том, чтобы быть волшебной фабрикой как Strings.createString("bla")(псевдо). Фабрика содержит пул всех строк, созданных таким образом.

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

Если нет, то он создает новый строковый объект внутри, сохраняет его в пуле и затем возвращает его. Таким образом, когда то же самое строковое значение запрашивается в следующий раз, оно возвращает тот же экземпляр.

Создание вручную new String("")переопределяет это поведение, обходя пул строковых литералов. Таким образом, всегда следует проверять равенство, используя equals()который сравнивает последовательность символов вместо равенства объектов.


«Волшебная фабрика», на которую вы ссылаетесь, - это не что иное, как компилятор Java. Ошибочно писать об этом процессе, как если бы он происходил во время выполнения.
Маркиз Лорн

1

Один простой способ понять разницу ниже:

String s ="abc";
String s1= "abc";
String s2=new String("abc");

        if(s==s1){
            System.out.println("s==s1 is true");
        }else{
            System.out.println("s==s1 is false");
        }
        if(s==s2){
            System.out.println("s==s2 is true");
        }else{
            System.out.println("s==s2 is false");
        }

вывод

s==s1 is true
s==s2 is false

Таким образом, new String () всегда будет создавать новый экземпляр.


1

Любой строковый литерал создается внутри пула строковых литералов, и пул не допускает дублирования. Таким образом, если два или более строковых объекта инициализируются одним и тем же литеральным значением, тогда все объекты будут указывать на один и тот же литерал.

String obj1 = "abc";
String obj2 = "abc";

«obj1» и «obj2» будут указывать на один и тот же строковый литерал, а пул строкового литерала будет иметь только один литерал «abc».

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

String obj1 = new String("abc");
String obj2 = new String("abc");

«obj1» и «obj2» будут указывать на два разных объекта в куче, а пул строкового литерала будет иметь только один литерал «abc».

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

String str1 = "abc";
String str2 = "abc" + "def";
str1 = "xyz";
str2 = str1 + "ghi";

Теперь в приведенном выше случае:
строка 1: литерал «abc» хранится в пуле строк.
Строка 2: литерал «abcdef» сохраняется в пуле строк.
Строка 3: новый литерал «xyz» сохраняется в пуле строк, и «str1» начинает указывать на этот литерал.
Строка 4: поскольку значение генерируется путем добавления к другой переменной, результат сохраняется в памяти кучи, а добавляемый литерал «ghi» будет проверен на наличие в пуле строк и будет создан, так как он не существует в вышеуказанный случай.


0

Хотя это выглядит одинаково с точки зрения программистов, оно оказывает большое влияние на производительность. Вы хотели бы использовать первую форму почти всегда.


0
String str = new String("hello")

Он проверит, содержит ли пул констант String строку "hello"? Если он присутствует, он не будет добавлять запись в пул констант String. Если нет, то он добавит запись в пул констант String.

Объект будет создан в области памяти кучи и str ссылки будут ссылаться на объект, созданный в области динамической памяти.

если вам нужна strссылка на точечный объект, содержащийся в пуле констант String, нужно явно вызватьstr.intern();

String str = "world";

Он проверит, содержит ли пул констант String строку "hello"? Если он присутствует, он не будет добавлять запись в пул констант String. Если нет, то он добавит запись в пул констант String.

В обоих вышеупомянутых случаях strссылки на String "world"присутствуют в пуле констант.


Это компилятор Java. Строковый литерал создает уникальную запись в пуле констант во время компиляции. Ошибочно деактивировать этот процесс, как если бы он происходил во время выполнения ...
Маркиз Лорн,

Можете ли вы объяснить, что не так в этом посте ясно?
Джаеш

Что неправильно в этом посте, так это то, что строковый литерал объединяется во время компиляции, как я уже сказал. Не при выполнении кода, как в вашем ответе.
маркиз Лорн

@EJP Я ценю ваш ответ. Можете ли вы указать точную строку, которая является неправильной в ответе. Я вижу, что все ответы выше совпадают с тем, что я написал. Пожалуйста, помогите, я хочу исправить свое понимание. Спасибо.
Джаеш

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

0

Когда вы храните строку как

String string1 = "Hello";

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

И всякий раз, когда мы имеем тенденцию пытаться создать другую строку как

String string2 = "Hello";

JVM проверяет, существует ли какой-либо объект String с постоянной ценой в пуле констант String, если это так, вместо создания нового объекта JVM назначает ссылку на существующий объект новой переменной.

И когда мы храним String как

String string = new String("Hello");

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

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