String
( 불변 인 ) 변수를로 선언하고 final
컴파일 타임 상수 표현식으로 초기화하면 컴파일 타임 상수 표현식이되고 값은 사용되는 컴파일러에 의해 인라인됩니다. 따라서 두 번째 코드 예제에서 값을 인라인 한 후 문자열 연결은 컴파일러에 의해 다음과 같이 변환됩니다.
String concat = "str" + "ing"; // which then becomes `String concat = "string";`
문자열 리터럴이 삽입 되어 있기 때문에 비교할 때 "string"
제공됩니다 .true
에서 §4.12.4 JLS - final
변수 :
프리미티브 유형 또는 유형의 변수 String
, 즉 final
컴파일 타임 상수 표현식 (§15.28)으로 초기화 된 변수를 상수 변수 라고 합니다 .
또한 JLS §15.28-상수 표현 :
형식의 컴파일 타임 상수 식은 메서드를 사용하여 고유 한 인스턴스를 공유하기 위해 String
항상 "인터 닝" 됩니다 String#intern()
.
String
변수가 아닌 첫 번째 코드 예제에서는 그렇지 않습니다 final
. 따라서 이들은 컴파일 타임 상수 표현식이 아닙니다. 연결 작업은 런타임까지 지연되어 새 String
객체 가 생성 됩니다. 두 코드의 바이트 코드를 비교하여이를 확인할 수 있습니다.
첫 번째 코드 예제 (비 final
버전) 는 다음 바이트 코드로 컴파일됩니다.
Code:
0: ldc #2; //String str
2: astore_1
3: ldc #3; //String ing
5: astore_2
6: new #4; //class java/lang/StringBuilder
9: dup
10: invokespecial #5; //Method java/lang/StringBuilder."<init>":()V
13: aload_1
14: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
17: aload_2
18: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
21: invokevirtual #7; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
24: astore_3
25: getstatic #8; //Field java/lang/System.out:Ljava/io/PrintStream;
28: aload_3
29: ldc #9; //String string
31: if_acmpne 38
34: iconst_1
35: goto 39
38: iconst_0
39: invokevirtual #10; //Method java/io/PrintStream.println:(Z)V
42: return
분명히 두 개의 개별 변수를 저장 str
하고 연결 작업을 수행하는 데 사용하고 있습니다.ing
StringBuilder
반면 두 번째 코드 예제 ( final
버전) 는 다음과 같습니다.
Code:
0: ldc #2; //String string
2: astore_3
3: getstatic #3; //Field java/lang/System.out:Ljava/io/PrintStream;
6: aload_3
7: ldc #2; //String string
9: if_acmpne 16
12: iconst_1
13: goto 17
16: iconst_0
17: invokevirtual #4; //Method java/io/PrintStream.println:(Z)V
20: return
따라서 string
컴파일 타임에 문자열을 작성하기 위해 최종 변수를 직접 인라인하여 ldc
단계의 조작으로 로드됩니다 0
. 그런 다음 두 번째 문자열 리터럴이 ldc
단계에서 작업에 의해로드됩니다 7
. String
런타임에 새로운 객체를 생성하지는 않습니다 . 문자열은 컴파일 타임에 이미 알려져 있으며 인턴됩니다.