В чем разница между текстом и новой строкой (текст)?
В чем разница между этими двумя утверждениями?
String s = "text";
String s = new String("text");
13 ответов
new String("text");
явно создает новый и референциально отличный экземпляр String
объект; String s = "text";
может повторно использовать экземпляр из пула строковых констант, если он доступен.
Вы очень редко бы хотели использовать new String(anotherString)
конструктор. Из API:
String(String original)
: Инициализирует вновь созданныйString
объект, так что он представляет ту же последовательность символов, что и аргумент; другими словами, вновь созданная строка является копией строки аргумента. Если не требуется явная копия оригинала, использование этого конструктора не требуется, поскольку строки являются неизменяемыми.
Смежные вопросы
- Строки Java: "String s = new String(" глупо ");"
- Строки являются объектами в Java, так почему бы нам не использовать "new" для их создания?
Что означает ссылочное различие
Изучите следующий фрагмент:
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)
конструктор. Однако нужно еще раз сказать, что это очень своеобразно и редко является намерением.
Рекомендации
- JLS 15.21.3 Операторы справочного равенства == и! =
class Object
-boolean Object(equals)
Связанные вопросы
Строковые литералы войдут в 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
Создается строка в пуле строковых констант
String s = "text";
другой создает строку в пуле констант ("text"
) и другая строка в обычном пространстве кучи (s
). Обе строки будут иметь одинаковое значение "text".
String s = new String("text");
s
затем теряется (имеет право на GC), если впоследствии не используется.
Строковые литералы, с другой стороны, используются повторно. Если вы используете "text"
в нескольких местах вашего класса это будет фактически одна и только одна строка (т. е. несколько ссылок на одну и ту же строку в пуле).
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
Строковый литерал является ссылкой на экземпляр класса 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
как аргумент)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
аналог).
Любой строковый литерал создается внутри пула строковых литералов, и пул не допускает дублирования. Таким образом, если два или более строковых объекта инициализируются одним и тем же литеральным значением, тогда все объекты будут указывать на один и тот же литерал.
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" будет проверен на наличие в пуле строк и будет создан, так как он не существует в вышеуказанный случай.
@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)
Один простой способ понять разницу ниже:
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() всегда будет создавать новый экземпляр.
Думать о "bla"
быть волшебной фабрикой как Strings.createString("bla")
(Псевдо). Фабрика содержит пул всех строк, созданных таким образом.
Если он вызывается, он проверяет, есть ли уже строка в пуле с этим значением. Если true, он возвращает этот строковый объект, следовательно, строки, полученные таким образом, действительно являются одним и тем же объектом.
Если нет, то он создает новый строковый объект внутри, сохраняет его в пуле, а затем возвращает его. Таким образом, когда то же самое строковое значение запрашивается в следующий раз, оно возвращает тот же экземпляр.
Создание вручную new String("")
переопределяет это поведение, обходя строковый литерал пула. Таким образом, равенство всегда должно быть проверено с помощью equals()
который сравнивает последовательность символов вместо равенства ссылок на объекты.
Хотя это выглядит одинаково с точки зрения программистов, оно оказывает большое влияние на производительность. Вы хотели бы использовать первую форму почти всегда.
Когда вы храните строку как
String string1 = "Hello";
непосредственно, затем JVM создает объект String с заданной ценой в течение отдельного блока памяти, называемого постоянным пулом String.
И всякий раз, когда мы имеем тенденцию пытаться создать другую строку как
String string2 = "Hello";
JVM проверяет, существует ли какой-либо объект String с постоянной ценой в пуле констант String, если это так, вместо создания нового объекта JVM назначает ссылку на существующий объект новой переменной.
И когда мы храним String как
String string = new String("Hello");
используя ключевое слово new, создается совершенно новый объект с заданной ценой независимо от содержимого пула констант String.
Когда вы используете что-то вродеnew String("Hello World")
инструмент анализа кода SpotBugs жалуется напроблемы с производительностью.
Описание этого вопроса показывает уважениеnew
иconstant string
Используя
java.lang.String(String)
конструктор тратит память, потому что созданный таким образом объект будет функционально неотличим от строки, переданной в качестве параметра. Просто используйте аргумент String напрямую.
Тип и шаблон ошибки: Dm - DM_STRING_CTOR
Извините за поздний ответ, но ответ очень нужен. Сначала нам нужно знать некоторые правила класса Java.lang.String.
Строковые литералы, например
String str="java";
(мы используем только двойные кавычки) отличаются от String Object (мы используем новое ключевое слово), напримерString str=new String("java");
Строка - неизменяемый объект т.е. если значение изменяется, новый объект создается и возвращается вам, например, см.
replace() and replaceAll()
функции и многое другое.Это создает проблему, заключающуюся в том, что многие объекты String в модификации, поэтому создатели Java придумали идею, которая получила название StringPool. StringPool хранится в области кучи, где будут храниться данные ссылки на объект, поскольку мы знаем, что String
Char[]
( до Java 9 очень долго читать) илиbyte
[]( после java 9 кратко читать).Строковые литералы хранятся в StringPool, а объекты String хранятся в обычной области объектов кучи.
Если есть много инициализации объектной строки, куча JVM будет завершена только в строковых операциях, команда разработчиков Java предложила решение intern(), которое перемещает / изменяет ссылку памяти на StringPool.
Еще одна хорошая ссылка для лучшего понимания java.lang.String
import java.util.*;
class GFG {
public static void main(String[] args)
{
String siteName1 = "java.com";
String siteName2 = "java.com";
String siteName3 = new String("java.com");
String siteName4 = new String("java.com").intern();
System.out.println("siteName1:::"+Integer.toHexString(System.identityHashCode(siteName1)));
System.out.println("siteName2:::"+Integer.toHexString(System.identityHashCode(siteName2)));
System.out.println("siteName3 creation Of New Object Without Interned:::"+Integer.toHexString(System.identityHashCode(siteName3)));//must be Diffrent bcoz new Object In Heap Area
System.out.println("siteName4 creation Of New Object With Interned:::"+Integer.toHexString(System.identityHashCode(siteName4)));//must be same MemoryAddress of siteName1,siteName2 and Interned, bcoz Objects Points to String pool Now
System.out.println(siteName1 == siteName2); // true
System.out.println(siteName1 == siteName3); // false this tells about lietral vs String Objects
String siteName5 = siteName3.intern(); // Interning will not change Original Object but gives us a new Object
System.out.println("siteName5 Interned from siteName3:::"+Integer.toHexString(System.identityHashCode(siteName5)));//must be same MemoryAddress of siteName1,siteName2 and Interned, bcoz Objects Points to String pool Now
System.out.println(siteName1 == siteName3); // false this tells about Immutability
System.out.println(siteName1 == siteName5); // true After Intering both are same
System.out.println(siteName1 == siteName4); // true
System.out.println(siteName5 == siteName4); // true
}
}
String str = new String("hello")
Он проверит, содержит ли пул констант String строку "hello"? Если он присутствует, он не будет добавлять запись в пул констант String. Если нет, то он добавит запись в пул констант String.
Объект будет создан в области памяти кучи и str
ссылки указывают на объект, созданный в ячейке памяти кучи.
если ты хочешь str
ссылка на точечный объект, содержащий в пуле String константу, то нужно явно вызвать str.intern();
String str = "world";
Он проверит, содержит ли пул констант String строку "hello"? Если он присутствует, он не будет добавлять запись в пул констант String. Если нет, то он добавит запись в пул констант String.
В обоих вышеупомянутых случаях str
ссылки на строку "world"
присутствует в постоянном пуле.