Разница между строковым объектом и строковым литералом
В чем разница между
String str = new String("abc");
а также
String str = "abc";
13 ответов
Когда вы используете строковый литерал, строка может быть интернирована, но когда вы используете new String("...")
Вы получаете новый строковый объект.
В этом примере оба строковых литерала ссылаются на один и тот же объект:
String a = "abc";
String b = "abc";
System.out.println(a == b); // true
Здесь создаются 2 разных объекта, и они имеют разные ссылки:
String c = new String("abc");
String d = new String("abc");
System.out.println(c == d); // false
В общем, вы должны использовать строковое литеральное обозначение, когда это возможно. Его легче читать, и это дает компилятору возможность оптимизировать ваш код.
Строковый литерал - это концепция языка Java. Это строковый литерал:
"a String literal"
Объект String - это отдельный экземпляр java.lang.String
учебный класс.
String s1 = "abcde";
String s2 = new String("abcde");
String s3 = "abcde";
Все они действительны, но имеют небольшую разницу. s1
будет ссылаться на интернированный объект String. Это означает, что последовательность символов "abcde"
будет храниться в центральном месте, и всякий раз, когда тот же буквальный "abcde"
используется снова, JVM не будет создавать новый объект String, но будет использовать ссылку на кэшированную строку.
s2
Гарантируется, что это новый объект String, поэтому в этом случае мы имеем:
s1 == s2 // is false
s1 == s3 // is true
s1.equals(s2) // is true
Длинный ответ доступен здесь, поэтому я дам вам короткий.
Когда вы делаете это:
String str = "abc";
Вы звоните intern()
метод на строку. Этот метод ссылается на внутренний пул String
объекты. Если строка, которую вы назвали intern()
на уже находится в пуле, то ссылка на это String
назначен на str
, Если нет, то новый String
помещается в пул, и ссылка на него затем присваивается str
,
Учитывая следующий код:
String str = "abc";
String str2 = "abc";
boolean identity = str == str2;
Когда вы проверяете идентичность объекта, выполнив ==
(вы буквально спрашиваете: эти две ссылки указывают на один и тот же объект?), вы получаете true
,
Тем не менее, вам не нужно intern()
Strings
, Вы можете заставить творение по новому Object
в куче, выполнив это:
String str = new String("abc");
String str2 = new String("abc");
boolean identity = str == str2;
В этом случае, str
а также str2
ссылки на разные Objects
, ни один из которых не был интернирован, так что при тестировании на Object
использование личности ==
, ты получишь false
,
С точки зрения хорошей практики кодирования: не используйте ==
чтобы проверить равенство строк, используйте .equals()
вместо.
Как строки неизменны, когда вы делаете:
String a = "xyz"
при создании строки JVM ищет в пуле строк, если строковое значение уже существует "xyz"
, если так 'a'
будет просто ссылкой на эту строку, и новый объект String не будет создан.
Но если вы скажете:
String a = new String("xyz")
вы заставляете JVM создавать новый String
ссылка, даже если "xyz"
в его бассейне.
Для получения дополнительной информации прочитайте это.
"abc"
это буквальная строка
В Java эти буквенные строки объединяются внутри и один и тот же экземпляр String "abc"
используется везде, где у вас есть этот строковый литерал, объявленный в вашем коде. Так "abc" == "abc"
всегда будет истиной, поскольку они оба являются одним и тем же экземпляром String.
С использованием String.intern()
метод, вы можете добавить любую строку, которая вам нравится, к внутренним пулам строк, они будут храниться в памяти до выхода из Java.
С другой стороны, используя new String("abc")
создаст новый строковый объект в памяти, который логически совпадает с "abc"
буквальный. "abc" == new String("abc")
всегда будет ложным, поскольку, хотя они логически равны, они ссылаются на разные экземпляры.
Обертывание конструктора String вокруг строкового литерала не имеет значения, оно просто излишне использует больше памяти, чем нужно.
String - это класс в Java, отличный от других языков программирования. Как и для каждого класса, объявление и инициализация объекта
String st1 = new String();
или же
String st2 = new String("Hello");
String st3 = new String("Hello");
Вот, st1
, st2
а также st3
это разные объекты.
То есть:
st1 == st2 // false
st1 == st3 // false
st2 == st3 // false
Так как st1
, st2
, st3
ссылаются на 3 разных объекта, и ==
проверяет равенство в памяти, отсюда и результат.
Но:
st1.equals(st2) // false
st2.equals(st3) // true
Вот .equals()
Метод проверяет содержание и содержание st1 = ""
, st2 = "hello"
а также st3 = "hello"
, Отсюда и результат.
А в случае объявления String
String st = "hello";
Вот, intern()
метод String
класс вызывается и проверяет, "hello"
находится в пуле интернов, и если нет, то он добавляется в пул интернов, и если в пуле интернов есть "привет", то st
укажет на память о существующем "hello"
,
Так в случае:
String st3 = "hello";
String st4 = "hello";
Вот:
st3 == st4 // true
Так как st3
а также st4
указывая на тот же адрес памяти.
Также:
st3.equals(st4); // true as usual
В первом случае создаются два объекта.
Во втором случае это только один.
Хотя оба пути str
имеет в виду "abc"
,
В дополнение к уже опубликованным ответам, также посмотрите эту отличную статью о javaranch.
Некоторая разборка всегда интересна...
$ cat Test.java
public class Test {
public static void main(String... args) {
String abc = "abc";
String def = new String("def");
}
}
$ javap -c -v Test
Compiled from "Test.java"
public class Test extends java.lang.Object
SourceFile: "Test.java"
minor version: 0
major version: 50
Constant pool:
const #1 = Method #7.#16; // java/lang/Object."<init>":()V
const #2 = String #17; // abc
const #3 = class #18; // java/lang/String
const #4 = String #19; // def
const #5 = Method #3.#20; // java/lang/String."<init>":(Ljava/lang/String;)V
const #6 = class #21; // Test
const #7 = class #22; // java/lang/Object
const #8 = Asciz <init>;
...
{
public Test(); ...
public static void main(java.lang.String[]);
Code:
Stack=3, Locals=3, Args_size=1
0: ldc #2; // Load string constant "abc"
2: astore_1 // Store top of stack onto local variable 1
3: new #3; // class java/lang/String
6: dup // duplicate top of stack
7: ldc #4; // Load string constant "def"
9: invokespecial #5; // Invoke constructor
12: astore_2 // Store top of stack onto local variable 2
13: return
}
Согласно документации класса String они эквивалентны.
Документация для String(String original)
также говорится, что: если не требуется явная копия оригинала, использование этого конструктора не требуется, поскольку строки являются неизменяемыми.
Ищите другие ответы, потому что кажется, что документация Java вводит в заблуждение:(
Ниже приведены некоторые сравнения:
String s1 = "Hello";
String s2 = "Hello";
String s3 = new String("Hello");
System.out.println(s1 == s2); //true
System.out.println(s1.equals(s2)); //true
System.out.println(s1 == s3); //false
System.out.println(s1.equals(s3)); //true
s3 = s3.intern();
System.out.println(s1 == s3); //true
System.out.println(s1.equals(s3)); //true
когда intern()
называется ссылка изменена.
Между строковым объектом и строковым литералом есть тонкие различия.
String s = "abc"; // creates one String object and one reference variable
В этом простом случае "abc" войдет в пул, а s будет ссылаться на него.
String s = new String("abc"); // creates two objects,and one reference variable
В этом случае, потому что мы использовали new
ключевое слово, Java создаст новый объект String в обычной (не пула) памяти, и s будет ссылаться на него. Кроме того, буквенное "abc" будет размещено в бассейне.
String s = new String("FFFF")
создает 2 объекта: "FFFF"
строка и String
объект, который указывает на "FFFF"
строка, так что это как указатель на указатель (ссылка на ссылку, я не увлекаюсь терминологией).
Говорят, что вы никогда не должны использовать new String("FFFF")