다음 두 진술의 차이점은 무엇입니까?
String s = "text";
String s = new String("text");
답변
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
==
두 참조 유형은 참조 ID 비교입니다. equals
반드시 그런 것은 아닙니다 ==
. 일반적 ==
으로 참조 유형 에 사용 하는 것은 잘못입니다 . 대부분의 시간 equals
을 대신 사용해야합니다.
그럼에도 불구하고 어떤 이유로 든 문자열이 equals
아닌 두 개를 만들어야 하는 경우 생성자를 사용할 수 있습니다 . 그러나 이것은 매우 독특하며 거의 의도가 아니라는 것을 다시 말해야합니다.==
new String(anotherString)
참고 문헌
관련 문제
답변
문자열 리터럴 은 문자열 상수 풀로 이동 합니다.
아래의 스냅 샷은 시각적으로 이해 하여 오랫동안 기억할 수 있도록 도와줍니다 .
라인별로 객체 생성 :
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 클래스의 동일한 인스턴스를 나타냅니다. String.intern 메소드를 사용하여 고유 한 인스턴스를 공유하기 위해 문자열 리터럴 또는보다 일반적으로 상수 표현식 (§15.28)의 값인 문자열이 “인터 닝”되기 때문입니다.
예 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 구조는 문자열 리터럴을 구성하는 일련의 유니 코드 코드 포인트를 제공합니다.
Java 프로그래밍 언어를 사용하려면 동일한 문자열 리터럴 (즉, 동일한 코드 포인트 시퀀스를 포함하는 리터럴)이 동일한 String 클래스 인스턴스 (JLS §3.10.5)를 참조해야합니다. 또한 String.intern 메소드가 문자열에서 호출되면 결과는 해당 문자열이 리터럴로 표시 될 때 리턴되는 동일한 클래스 인스턴스에 대한 참조입니다. 따라서 다음 표현식은 true 값을 가져야합니다.
("a" + "b" + "c").intern() == "abc"
문자열 리터럴을 도출하기 위해 Java Virtual Machine은 CONSTANT_String_info 구조에서 제공하는 코드 포인트 시퀀스를 검사합니다.
CONSTANT_String_info 구조에서 제공 한 것과 동일한 유니 코드 코드 포인트 시퀀스를 포함하는 String 클래스의 인스턴스에서 String.intern 메소드를 이전에 호출 한 경우 문자열 리터럴 파생 결과는 동일한 클래스의 클래스 인스턴스에 대한 참조입니다.
그렇지 않으면 CONSTANT_String_info 구조에 의해 제공된 일련의 유니 코드 코드 포인트를 포함하는 String 클래스의 새 인스턴스가 작성됩니다. 해당 클래스 인스턴스에 대한 참조는 문자열 리터럴 파생의 결과입니다. 마지막으로, 새로운 String 인스턴스의 인턴 메소드가 호출됩니다.
바이트 코드
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
결론 : 문자열 풀에 대한 직접적인 바이트 코드 지원이 있으며 메모리 표현이 효율적입니다.
보너스 : 직접 바이트 코드를 지원하지 않는 Integer pool 과 비교하십시오 (예 : CONSTANT_String_info
아날로그 없음 ).
답변
@Braj : 나는 당신이 다른 방법으로 언급했다고 생각합니다. 내가 틀렸다면 정정 해주세요
라인별로 객체 생성 :
문자열 str1 = 새 문자열 ( “java5”)
Pool- "java5" (1 Object)
Heap - str1 => "java5" (1 Object)
문자열 str2 = “java5”
pool- str2 => "java5" (1 Object)
heap - str1 => "java5" (1 Object)
문자열 str3 = 새 문자열 (str2)
pool- str2 => "java5" (1 Object)
heap- str1 => "java5", str3 => "java5" (2 Objects)
문자열 str4 = “java5”
pool - str2 => str4 => "java5" (1 Object)
heap - str1 => "java5", str3 => "java5" (2 Objects)
답변
(의사) "bla"
와 같은 마법의 공장 이라고 생각하십시오 Strings.createString("bla")
. 팩토리는이 방법으로 생성 된 모든 문자열의 풀을 보유합니다.
호출되면 풀에이 값을 가진 문자열이 이미 있는지 확인합니다. true 인 경우이 문자열 객체를 반환하므로이 방법으로 얻은 문자열은 실제로 동일한 객체입니다.
그렇지 않은 경우 내부적으로 새 문자열 객체를 만들어 풀에 저장 한 다음 반환합니다. 따라서 다음에 동일한 문자열 값을 쿼리하면 동일한 인스턴스가 반환됩니다.
수동 생성 new String("")
은 문자열 리터럴 풀을 무시하여이 동작을 무시합니다. 따라서 equals()
객체 참조 등식 대신 문자 시퀀스를 비교하는 등호를 항상 확인해야합니다 .
답변
차이점을 이해하는 간단한 방법은 다음과 같습니다.
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 ()은 항상 새 인스턴스를 만듭니다.