>Java >java지도 시간 >Java의 intern() 메소드에 대한 심층 분석

Java의 intern() 메소드에 대한 심층 분석

Y2J
Y2J원래의
2017-05-19 09:47:332389검색

1. 문자열 문제

문자열은 실제로 일상적인 코딩 작업에 매우 유용하며 사용하기가 비교적 간단하므로 이에 대해 자세히 설명하는 사람은 거의 없습니다. 연구. 반면에 면접이나 필기 시험은 더 깊이 있고 어려운 질문이 포함되는 경우가 많습니다. 채용 시 후보자에게 가끔 관련 질문을 하는 것은 답변이 특히 정확하고 심층적이어야 한다는 의미는 아닙니다. 일반적으로 이러한 질문을 하는 목적은 첫 번째는 기본 지식에 대한 이해를 테스트하는 것입니다. 두 번째는 JAVA에 대한 기본 지식을 테스트하는 것입니다. 두 번째는 기술에 대한 지원자의 태도를 테스트하는 것입니다.

다음 프로그램이 어떤 결과를 출력하는지 볼까요? 모든 질문에 올바르게 대답하고 그 이유를 알 수 있다면 이 기사는 별로 의미가 없을 것입니다. 대답이 틀리거나 원리가 명확하지 않다면 다음 분석을 자세히 살펴보십시오. 이 기사는 각 프로그램의 결과와 결과를 출력하는 깊은 이유를 명확하게 이해하는 데 도움이 될 것입니다.

코드 세그먼트 1:

package com.paddx.test.string;
public class StringTest {
    public static void main(String[] args) {
        String str1 = "string";
        String str2 = new String("string");
        String str3 = str2.intern();
 
        System.out.println(str1==str2);//#1
        System.out.println(str1==str3);//#2
    }
}

코드 세그먼트 2:

package com.paddx.test.string;
public class StringTest01 {
    public static void main(String[] args) {
        String baseStr = "baseStr";
        final String baseFinalStr = "baseStr";
 
        String str1 = "baseStr01";
        String str2 = "baseStr"+"01";
        String str3 = baseStr + "01";
        String str4 = baseFinalStr+"01";
        String str5 = new String("baseStr01").intern();
 
        System.out.println(str1 == str2);//#3
        System.out.println(str1 == str3);//#4
        System.out.println(str1 == str4);//#5
        System.out.println(str1 == str5);//#6
    }
}

코드 세그먼트 3(1):

package com.paddx.test.string;  
public class InternTest {
    public static void main(String[] args) {
 
        String str2 = new String("str")+new String("01");
        str2.intern();
        String str1 = "str01";
        System.out.println(str2==str1);//#7
    }
}

코드 세그먼트 3(2):

package com.paddx.test.string;
 
public class InternTest01 {
    public static void main(String[] args) {
        String str1 = "str01";
        String str2 = new String("str")+new String("01");
        str2.intern();
        System.out.println(str2 == str1);//#8
    }
}

설명의 편의를 위해 위 코드의 #1~#8 출력 결과를 인코딩하였으며, 아래 파란색 글씨 부분이 그 결과입니다.

2. 문자열 심층 분석

1. 코드 세그먼트 분석 1

문자열은 기본 유형에 속하지 않지만, 기본 유형과 같으며 리터럴을 통해 직접 값을 할당할 수 있습니다. 물론 new를 사용하여 문자열 객체 를 생성할 수도 있습니다. 그러나 리터럴 할당을 통해 문자열을 생성하는 것과 새 문자열을 생성하는 것에는 근본적인 차이가 있습니다.

Java의 intern() 메소드에 대한 심층 분석

리터럴 할당을 통해 문자열을 생성하는 경우 상수에 우선순위가 부여됩니다. 동일한 문자열이 풀에 이미 존재하는 경우 스택의 참조 가 해당 문자열을 직접 가리킵니다. 문자열이 없으면 상수 풀에 문자열이 생성됩니다. 스택의 참조는 이 문자열을 가리킵니다. new를 통해 문자열을 생성하면 힙에 문자열 객체가 직접 생성됩니다. (참고로 JDK 7 이후에는 HotSpot이 상수 풀을 영구 생성에서 힙으로 옮겼습니다. 자세한 내용은 "JDK8 메모리를 참조하세요." Model-The Disappearing PermGen" 기사), 스택의 참조는 개체를 가리킵니다. 힙에 있는 문자열 객체의 경우 intern() 메서드를 통해 상수 풀에 문자열을 추가하고 상수에 대한 참조를 반환할 수 있습니다.

이제 코드 세그먼트 1의 결과를 명확하게 이해할 수 있습니다.

결과 #1: str1이 문자열의 상수를 가리키므로 str2는 힙에서 생성된 개체입니다. 따라서 str1==str2는 false를 반환합니다.

결과 #2: str2는 str2의 값("string")을 상수 풀에 복사하는 intern 메서드를 호출하지만 해당 문자열은 이미 상수 풀에 존재합니다(즉, 문자열이 가리키는 문자열). by str1), 문자열에 대한 참조를 직접 반환하므로 str1==str2는 true를 반환합니다.

코드 세그먼트 1을 실행한 결과는 다음과 같습니다.

Java의 intern() 메소드에 대한 심층 분석

2. 코드 세그먼트 2 분석

두 번째 코드 세그먼트의 결과는 StringTest01.class 파일을 디컴파일하면 더 쉽게 이해할 수 있습니다.

상수 풀 콘텐츠(부분):

Java의 intern() 메소드에 대한 심층 분석

실행 지침( part) , 두 번째 열 #+ordinal은 상수 풀의 항목에 해당합니다):

Java의 intern() 메소드에 대한 심층 분석

위의 실행 과정을 설명하기 전에 먼저 두 가지 명령을 이해하세요.

ldc: 런타임 상수 풀에서 항목을 푸시하고, 지정된 항목의 참조를 상수 풀에서 스택으로 로드합니다.

astore_: 참조를 지역 변수에 저장하고 참조를 n번째 지역 변수 에 할당합니다.

이제 코드 세그먼트 2의 실행 프로세스를 설명하기 시작합니다.

0: ldc               #2: 상수 풀의 두 번째 항목("baseStr")을 스택에 로드합니다.

2: astore_1 : 1의 참조를 첫 번째 지역 변수에 할당합니다. 즉, String baseStr = "baseStr"

3: ldc #2: 상수에서 두 번째 변수를 로드합니다. 풀 항목("baseStr")을 스택에 추가합니다.

5: astore_2: 3의 참조를 두 번째 지역 변수, 즉 final String에 할당합니다. baseFinalStr="baseStr"

6: ldc #3: 첫 번째 문자열을 로드합니다. 상수 풀 세 개의 항목("baseStr01")이 스택에 추가됩니다.

8: astore_3: 6의 참조를 세 번째 지역 변수, 즉 String str1="baseStr01";

에 할당합니다.

9: ldc #3: 상수 풀의 세 번째 항목("baseStr01")을 스택에 로드합니다.

11: astore 4: 9의 참조를 네 번째 지역 변수에 할당합니다. String str2="baseStr01";

결과 #3: str1==str2는 둘 다 확실히 true를 반환합니다. str1과 str2는 상수 풀에서 동일한 참조 주소를 가리킵니다. 따라서 실제로 JAVA 1.6 이후에는 상수 문자열의 "+" 연산이 컴파일 단계에서 문자열로 직접 합성됩니다.

13: new           #4: StringBuilder의 인스턴스를 생성합니다.

16: dup: 13에서 생성된 객체의 참조를 복사하여 스택에 푸시합니다.

17: Invokespecial #5: 상수 풀의 다섯 번째 항목인 StringBuilder.

위의 세 가지 지침은 StringBuilder 개체를 생성하는 데 사용됩니다.

20: aload_1 : 첫 번째 매개변수 값인 "baseStr"을 로드합니다.

21: Invokevirtual #6: StringBuilder 개체의 추가 메서드를 호출합니다.

24: ldc #7: 상수 풀의 일곱 번째 항목("01")을 스택에 로드합니다.

26: Invokevirtual #6: StringBuilder.append 메서드를 호출합니다.

29: Invokevirtual #8: StringBuilder.toString 메서드를 호출합니다.

32: astore 5: 29의 결과 참조 할당을 다섯 번째 지역 변수, 즉 변수 str3에 대한 할당으로 변경합니다.

결과 #4: str3은 실제로 stringBuilder.append()에 의해 생성된 결과이기 때문에 str1과 같지 않으며 결과는 false를 반환합니다.

34: ldc #3: 상수 풀의 세 번째 항목("baseStr01")을 스택에 로드합니다.

36: astore 6: 34의 참조를 여섯 번째 지역 변수, 즉 str4="baseStr01";

결과 #5: str1과 str4가 상수를 가리키기 때문에 세 번째 항목이 풀에 있으므로 str1==str4는 true를 반환합니다. 여기서도 final 필드의 경우 상수 교체가 컴파일 타임에 직접 수행되는 반면, non-final 필드의 경우 런타임에 할당 처리가 수행되는 현상도 확인할 수 있습니다.

38: new #9: 문자열 객체 생성

41: dup: 참조를 복사하여 스택에 푸시합니다.

42: ldc #3: 상수 풀의 세 번째 항목("baseStr01")을 스택에 로드합니다.

44: Invokespecial #10: String."" 메소드를 호출하고 42단계의 참조를 메소드에 매개변수로 전달합니다.

47: Invokevirtual #11: String.intern 메서드를 호출합니다.

38번부터 41번까지 해당 소스코드는 new String(“baseStr01″).intern()입니다.

50: astore 7: 47단계에서 반환된 결과를 변수 7에 할당합니다. 즉, str5는 상수 풀에서 baseStr01의 위치를 ​​가리킵니다.

결과 #6: str5와 str1이 모두 상수 풀의 동일한 문자열을 가리키므로 str1==str5는 true를 반환합니다.

코드 세그먼트 2를 실행하면 출력 결과는 다음과 같습니다.

Java의 intern() 메소드에 대한 심층 분석

3. 코드 세그먼트 3 분석:

코드 세그먼트 3의 경우 JDK 1.6과 JDK 1.7에서 실행 결과가 다릅니다. 먼저 실행 결과를 살펴보고 그 이유를 설명하겠습니다.

JDK 1.6에서 실행 결과:

Java의 intern() 메소드에 대한 심층 분석

JDK 1.7에서 실행 결과:

Java의 intern() 메소드에 대한 심층 분석

코드 세그먼트 1의 분석에 따르면 str2와 str1은 원래 다른 위치를 가리키고 false를 반환해야 하기 때문에 JDK 1.6의 결과를 쉽게 얻을 수 있습니다.

이상한 문제는 JDK 1.7 이후에는 첫 번째 경우에는 true를 반환하지만 위치를 변경한 후에는 반환 결과가 false가 된다는 점입니다. 그 주된 이유는 JDK 1.7 이후 HotSpot이 상수 풀을 영구 생성에서 메타 공간으로 옮겼기 때문입니다. 이로 인해 JDK 1.7 이후의 인턴 방식은 구현에 있어 상대적으로 큰 변화를 겪었습니다. 로 이동하여 이 이미 상수 풀에 존재하는지 쿼리하고, 존재하는 경우 상수 풀에 참조를 반환합니다. 이는 이전과 다르지 않습니다. 상수 풀에서 찾을 수 없으면 문자열은 더 이상 상수 풀에 복사되지 않지만 원래 문자열에 대한 참조는 상수 풀에 생성됩니다. 그래서:

결과 #7: 첫 번째 경우에는 상수 풀에 문자열 "str01"이 없기 때문에 힙의 "str01"에 대한 참조가 상수 풀에 생성되고, 이를 수행할 때 리터럴 할당의 경우 상수 풀이 이미 존재하므로 참조를 직접 반환할 수 있습니다. 따라서 str1과 str2는 모두 힙의 문자열을 가리키고 true를 반환합니다.

결과 #8: 위치를 교환한 후 리터럴 할당(String str1 = "str01")을 수행할 때 상수 풀이 존재하지 않기 때문에 str1은 상수 풀의 위치를 ​​가리키고 str2는 힙을 가리킵니다. intern 메소드는 의 객체에 사용되며 str1 및 str2에는 영향을 미치지 않으므로 false가 반환됩니다.

[관련 추천]

1. Java 무료 동영상 튜토리얼

intern() 메소드 사용 경험 요약 JAVA

3. Java에서 intern 메소드의 개념은 무엇인가요

4. Java에서 intern()의 역할을 분석합니다

5. String 객체의 intern()에 대한 자세한 설명

위 내용은 Java의 intern() 메소드에 대한 심층 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.