“text”와 new String (“text”)의 차이점은 무엇입니까?


195

다음 두 진술의 차이점은 무엇입니까?

String s = "text";

String s = new String("text");


누구든지 이것에 회신하십시오. 문자열 a = "자바"; String b = "자바"; System.out.println (a == b); true // 그러나 System.out.println ( "a == b?"+ a == b); // false ...
에너지

댓글 ( "a == b?) =>을 (를) 추가 한 시점을 이해하지 못합니다. 결과가 FALSE가됩니다. 왜?
Energy

@Energy 결과는 false연산 순서가 + 연산자가 먼저 가고 "a == b?" 를 사용하여 문자열 "a == b? Java"를 만듭니다. 그런 다음 표현식 "a==b?Java" == b은 false로 평가됩니다.
Allison 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

==두 참조 유형은 참조 ID 비교입니다. equals반드시 그런 것은 아닙니다 ==. 일반적 ==으로 참조 유형 에 사용 하는 것은 잘못입니다 . 대부분의 시간 equals을 대신 사용해야합니다.

그럼에도 불구하고 어떤 이유로 든 문자열이 equals아닌 두 개를 만들어야 하는 경우 생성자를 사용할 있습니다 . 그러나 이것은 매우 독특하며 거의 의도가 아니라는 것을 다시 말해야합니다.==new String(anotherString)

참고 문헌

관련 문제


3
내가 쓰면 : String s = new String ( "abc"); 이제 다음과 같이 작성합니다. String s = "abc"; Will String s = "abc"; 문자열 풀에 새로운 문자열 리터럴을 만드시겠습니까?
Kaveesh Kanwal

왜 아무도 이전 질문에 대답하지 않습니까?
zeds

2
@KaveeshKanwal 아니요, 리터럴은 복제되지 않습니다. 보시다시피 2 "abc"초가 있습니다. 그중 하나만 문자열 풀로 이동하고 다른 하나는이를 참조합니다. 그렇다면 s적절한 새 객체가 될 것입니다.
Kayaman

1
@Kaveesh Kanwal-String s = new String ( "abc")은 "abc"값을 가진 새로운 String 객체 만 생성합니다. 그리고 두 번째 명령문은 문자열 풀에 "abc"문자열 리터럴이 이미 있는지 여부를 확인합니다. 이미 존재하는 경우 기존 참조에 대한 참조가 리턴되고 그렇지 않은 경우 새 리터럴 ( "abc")이 문자열 풀에 작성됩니다. 그것이 당신의 질의를 해결하기를 바랍니다!
user968813

그것에 대한 '아마도'가 없습니다. 컴파일러 문자열 리터럴을 풀링 해야합니다 . JLS 3.10.5 .
Lorne의 후작

119

문자열 리터럴문자열 상수 풀로 이동 합니다.

아래의 스냅 샷은 시각적으로 이해 하여 오랫동안 기억할 수 있도록 도와줍니다 .

여기에 이미지 설명을 입력하십시오


라인별로 객체 생성 :

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"의 값을 변경하기 위해 giong이고 str4의 값을 변경한다는 것을 알고 싶습니까?
CoronaPintu

2
그래, 난이 STR4의 값을 변경하지 않습니다 확인했다
CoronaPintu

@Braj 당신은 Answer의 주장에 대한 문서를 제공 할 수 있습니까?
Basil Bourque

@Braj : 테이블의 'Heap'& 'pool'에 대한 헤더가 반대로되어 있습니까?
Rahul Kurup

정확하지 않습니다. 상수 풀은 실행 시간이 아니라 컴파일 시간에 생성됩니다. 인용되지 않은 텍스트에는 인용 형식을 사용하지 마십시오.
Lorne의 후작

15

하나는 문자열 상수 풀에 문자열을 만듭니다.

String s = "text";

다른 하나는 상수 풀 ( "text")에 문자열을 만들고 일반 힙 공간 ( s) 에 다른 문자열을 만듭니다 . 두 문자열의 값은 "text"와 동일합니다.

String s = new String("text");

s 나중에 사용하지 않으면 GC에 적합하지 않습니다.

반면에 문자열 리터럴은 재사용됩니다. "text"클래스의 여러 곳에서 사용하는 경우 실제로는 하나의 문자열 (즉, 풀의 동일한 문자열에 대한 여러 참조)이됩니다.


상수 풀의 문자열은 손실되지 않습니다. 나중에 사용하지 않으면 's'가 없어 졌다고 말했습니까?
Lorne의 후작

@EJP : 예, "s"를 의미했습니다. 알아 주셔서 감사합니다. 질문을 바로 잡겠습니다.

9

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

JVMS 7 5.1 말한다 :

문자열 리터럴은 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

참고 사항 :

  • 03: 동일한 ldc #2상수로드 (리터럴)
  • 12: 새로운 문자열 인스턴스가 생성됩니다 ( #2인수로)
  • 35: ac일반 객체로 비교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아날로그 없음 ).


2

@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)

str1의 값에 포함되지 않는다 str2거나 str3또는 str4어떤 식 으로든.
Lorne의 후작

1

(의사) "bla"와 같은 마법의 공장 이라고 생각하십시오 Strings.createString("bla"). 팩토리는이 방법으로 생성 된 모든 문자열의 풀을 보유합니다.

호출되면 풀에이 값을 가진 문자열이 이미 있는지 확인합니다. true 인 경우이 문자열 객체를 반환하므로이 방법으로 얻은 문자열은 실제로 동일한 객체입니다.

그렇지 않은 경우 내부적으로 새 문자열 객체를 만들어 풀에 저장 한 다음 반환합니다. 따라서 다음에 동일한 문자열 값을 쿼리하면 동일한 인스턴스가 반환됩니다.

수동 생성 new String("")은 문자열 리터럴 풀을 무시하여이 동작을 무시합니다. 따라서 equals()객체 참조 등식 대신 문자 시퀀스를 비교하는 등호를 항상 확인해야합니다 .


당신이 말하는 '매직 팩토리'는 Java 컴파일러에 지나지 않습니다. 마치 런타임에 발생한 것처럼이 프로세스를 쓰는 것은 실수입니다.
Lorne의 후작

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"리터럴이 하나만 있습니다.

new 키워드를 사용하여 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";

이제 위의 경우 :
Line 1 : "abc"리터럴이 문자열 풀에 저장됩니다.
2 행 : "abcdef"리터럴이 문자열 풀에 저장됩니다.
3 행 : 새로운 "xyz"리터럴이 문자열 풀에 저장되고 "str1"이이 리터럴을 가리 키기 시작합니다.
4 행 : 다른 변수에 값을 추가하여 값을 생성하므로 결과는 힙 메모리에 저장되고 "ghi"가 추가 된 리터럴은 문자열 풀에 존재하는지 확인하고 존재하지 않기 때문에 작성됩니다. 위의 경우.


0

프로그래머의 관점에서는 동일하게 보이지만 성능에 큰 영향을 미칩니다. 거의 항상 첫 번째 양식을 사용하고 싶을 것입니다.


0
String str = new String("hello")

문자열 상수 풀에 이미 문자열 "hello"가 포함되어 있는지 확인합니까? 있으면 문자열 상수 풀에 항목을 추가하지 않습니다. 존재하지 않으면 문자열 상수 풀에 항목이 추가됩니다.

힙 메모리 영역 str에서 객체가 생성되고 힙 메모리 위치에서 생성 된 객체를 참조합니다.

strString 상수 풀에 포함 된 포인트 객체에 대한 참조를 원한다면 명시 적으로 호출해야합니다.str.intern();

String str = "world";

문자열 상수 풀에 이미 문자열 "hello"가 포함되어 있는지 확인합니까? 있으면 문자열 상수 풀에 항목을 추가하지 않습니다. 존재하지 않으면 문자열 상수 풀에 항목이 추가됩니다.

위의 두 경우 모두 str참조 "world"는 상수 풀에 존재 하는 문자열을 가리 킵니다 .


'그것은 자바 컴파일러입니다. 문자열 리터럴은 컴파일시 상수 풀에서 고유 한 항목을 작성합니다. 이 .. 실행시 발생하는 것처럼 그것은이 과정을 deacribe하는 실수
론의 후작

이 게시물의 문제점을 명확하게 설명해 주시겠습니까?
Jayesh

이 게시물에서 잘못된 점은 이미 말했듯이 문자열 리터럴이 complle 시간에 풀링된다는 것입니다. 답변과 같이 코드를 실행할 때는 아닙니다.
Lorne의 후작

@EJP 답변 해 주셔서 감사합니다. 답이 틀린 정확한 줄을 지적 해 주시겠습니까? 위의 모든 답변이 내가 쓴 것과 동일하다는 것을 알았습니다. 이해해주세요. 이해해주세요. 감사.
Jayesh

반복적으로 말했듯이 코드 줄이 실행될 때 모든 프로세스가 수행되는 것처럼 전체 프로세스에 대해 작성했습니다. 대답에서 하나의 '정확한 줄'이 잘못되도록 모든 것을 줄일 수는 없습니다.
Lorne의 후작

0

문자열을 다음과 같이 저장할 때

String string1 = "Hello";

직접 JVM은 String 상수 풀이라는 별도의 메모리 블록 중에 지정된 가격으로 String 객체를 만듭니다.

그리고 우리가 다른 String을 시도하고 생성하려는 경향이있을 때마다

String string2 = "Hello";

JVM은 새로운 객체를 만들지 않고 기존 객체의 참조를 새로운 변수에 할당하는 대신 일정한 가격의 String 객체가 String 상수 풀 내에 존재하는지 여부를 확인합니다.

그리고 우리가 String을

String string = new String("Hello");

new 키워드를 사용하면 문자열 상수 풀의 내용에 관계없이 지정된 가격의 새로운 객체가 만들어집니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.