당신은 정말 문자열과의 StringBuilder를 이해한다

머리말

인터뷰는 자주이 질문을 요청할 수 있습니다 : 문자열, 모두 StringBuilder, StringBuffer와의 차이점에 대해 말해, 많은 사람들에 의해 문자열 경우라고 할 수있다 +(같은 이유로 다음에 좋은 플러스) 시간이 문자열을 연결하는 임시을 많이 생성 변수, 성능은하지만, 정말 그렇게, 상대적으로 낮은 (도 썼다 많은 온라인 게시물)인가?

문자열 접합

사실, 순서를 통해 문자열을 알 수 없습니다 +결국에는 임시 변수가 생성되고, 문자열을 스플 라이스 시간? 사실,이 문제는 매우 간단합니다, 그냥 할 필요가 javap클래스 파일 문자열 작업이 이루어 있는지 확인하기 위해 생성 된 클래스 파일을 디 컴파일. 여기에서 우리는 "자바 프로그래밍 아이디어"에있는 문자열 부분 을 설명하기 위해 예.

처음에 우리는 다음과 같은 코드를 보면 :

public class Test {
    public static void main(String[] args){
        String mango = "mango";
        String s = "abc" + mango + "def" + 47;
        System.out.println(s);
    }
}
复制代码

이 코드를 통해 더 일반적입니다 +우리에 의해, 코드 문자열 연결에 javac Test.java다음이 코드를 컴파일 javap -c Test.class생성 된 디 컴파일 Test.class파일입니다. 관련이없는 부분 걸러, 그것은 보여줍니다 main()다음 바이트 코드로, 그래서 코드 바이트 코드를. 당신은 매우 흥미로운 일이 일어 찾을 수 있습니다.

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: ldc           #2                  // String mango
         2: astore_1
         3: new           #3                  // class java/lang/StringBuilder
         6: dup
         7: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
        10: ldc           #5                  // String abc
        12: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        15: aload_1
        16: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        19: ldc           #7                  // String def
        21: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        24: bipush        47
        26: invokevirtual #8                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
        29: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        32: astore_2
        33: getstatic     #10                 // Field java/lang/System.out:Ljava/io/PrintStream;
        36: aload_2
        37: invokevirtual #11                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        40: return
}
复制代码

여기 어셈블리 언어로 제공, 독자들이 온라인으로 검색 할 수 있습니다 바이트 코드 명령어 목록이 내가 여기에 제공, 많은있을 것 독자가 각 명령의 의미를 이해하기 위해 테이블을 볼 수 있습니다, 나는 자세히 확장 여기 아니다. 각 명령어가 될 수 후 , 명령 코드의 내용은, 조작 뒤에 오브젝트를 나타내는. 주의 깊은 독자는 발견 할 것이다 : 컴파일러는 자동으로 java.lang.StringBuilder가 (자바의 전면 도입 참조 형식입니다, 당신은의 "자바 가상 머신에 대한 심층적 인 이해"에서 볼 수 독자에 대해 자세히 알아 보려면 클래스 파일 구조 장) . 우리는 소스 코드에 모두 StringBuilder를 사용하지 않은,하지만 더 효율적이기 때문에 컴파일러는, 그것을 사용하는 무료 손을 부여하고 있지만.////L

컴파일러가 모두 StringBuilder 객체를 생성하는 위의 바이트 코드를 시청 후 당신은 발견 할 것이다 +각각의 번호를 사용하여 문자열에 연결 append(), 사 개 통화의 총, 마지막 호출의 toString () 메소드는 결과를 생성하기 위해 스플 라이스하는 방법. (참고 : 관심있는 독자에 의해 위의 코드의 StringBuilder를 대체 할 수있는 경우 javac javap컴파일, 당신은 발견 할 것이다 main()과정에서 같은 바이트 코드입니다 생성).

결론

위의 예에 의해 우리는 우리가 통과 할 때 발견 +잇기 문자열을, 컴파일러는 우리가 모두 StringBuilder에 접합하기 위해 자동으로 최적화, 인터넷 임시 변수의 생성을 밝혔다 발생하지 않습니다, 이러한 단점을 늦출. (주의 :이 StringBuidler jdk5.0 후에 도입되어 있기 때문에, 그것을 jdk5.0이어서 자신을 인증 할 수있는 관심있는 독자의 StringBuffer에 의해 접합되기 전에).

확장

컴파일러는 우리가 무료로 사용할 수없는 것을, 우리를 위해 최적화되어 있기 때문에 이제, 우리는 확실히 매우 행복 할 것이다 String그것을 (모든 행복을 생각). 때로는 최적화 컴파일러가 원하는 결과를하지 않을 수 있기 때문에 하하, 너무 흥분하지 않습니다. 이제 다음 코드를 살펴 보자 :

다음 프로그램은 두 가지 방법으로 문자열을 생성한다 : 방법 문자열 오브젝트 복수 사용함; StringBuidler 개의 코드 사용 방법.

public class Test {
    public String testString(String[] fields) {
        String result = "";
        for (int i = 0; i < fields.length; i++) {
            result += fields[i];
        }
        return result;
    }

    public String testStringBuilder(String[] fields){
        StringBuilder result = new StringBuilder();
        for (int i = 0; i<fields.length; i++){
            result.append(fields[i]);
        }
        return result.toString();
    }
}
复制代码

위의 두 가지 방법은, 유사한 코드를 수행 한 다음 문자열 배열, 통과 for함께 문자열의 배열을 스티치 루프를 제 차분 법을 사용하는 String사용하는 두 번째 방법 잇기 StringBuilder잇기를. 그럼 우리가 통과 javap바이트 코드를 두 가지 방법을 볼 수, 코드를 컴파일 독립적 인 부분을 제거 할 수 있습니다.

첫째, testString()바이트 코드 방법 :

public java.lang.String testString(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)Ljava/lang/String;
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=4, args_size=2
         0: ldc           #2                  // String
         2: astore_2
         3: iconst_0
         4: istore_3
         5: iload_3
         6: aload_1
         7: arraylength
         8: if_icmpge     38
        11: new           #3                  // class java/lang/StringBuilder
        14: dup
        15: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
        18: aload_2
        19: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        22: aload_1
        23: iload_3
        24: aaload
        25: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        28: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        31: astore_2
        32: iinc          3, 1
        35: goto          5
        38: aload_2
        39: areturn
复制代码

다음은 독자의 라인 8에서 보이는 if_icmpge명령 루프 제어 테이블이 발견되는 바이트 코드 명령어 비교하는 i루프 (38)에서 다시 순환으로 특정 시간과 동일한 값을 라인 (38)은 제 1 루프를 나타낸다 8행하기 위해 35행. 귀환 루프 시작점 (첫째는 광고 수단 35 5행). 그런 다음 우리는 (첫 번째 루프 참조 8처음에 라인 35라인 11 개 라인), A는 new이 너무 잘 알고, 명령, 객체를 생성하는 것입니다. 그러나 루프를 때마다, 신규 작성할 필요가 있다는 것을 의미 루프 내부에 실제로 StringBuilder오브젝트. 이것은 분명히 받아 들일 수없는 것입니다.

그럼 우리는 무엇을보고 testStringBuilder()바이트 코드 :

public java.lang.String testStringBuilder(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)Ljava/lang/String;
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=4, args_size=2
         0: new           #3                  // class java/lang/StringBuilder
         3: dup
         4: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
         7: astore_2
         8: iconst_0
         9: istore_3
        10: iload_3
        11: aload_1
        12: arraylength
        13: if_icmpge     30
        16: aload_2
        17: aload_1
        18: iload_3
        19: aaload
        20: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        23: pop
        24: iinc          3, 1
        27: goto          10
        30: aload_2
        31: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        34: areturn
复制代码

우리는 코드가 단순 루프 짧은의 몸의 일부가 아닌 것을 알 수 있습니다, 그것은 new단지의 StringBuilder 객체를 생성 나타내는 통화의 시작에 불과하다.

결론 확장

당신이 클래스 쓸 때, toString()문자열 조작이 비교적 간단 경우 방법은,이 컴파일러에 의존 할 수 있습니다, 그것은 당신을 위해 합리적인 최종 결과 문자열 구성 될 것이다. 당신이 싶은 경우 toString()순환 프로세스를 사용하여, 당신은 모두 StringBuilder 객체를 생성해야합니다. 당신이 결정할 수없는 경우 물론, 당신은 항상은 javap하여 프로그램을 분석 할 수 있습니다.

문제가 남아 :는 JVM이 수행하는 방법 결국 우리 모두가 알아야 열거하지만, 당신이 알고 무엇입니까? (생각 : 사실, 그것은 매우 간단합니다 작동하는 방법을 알고 싶어요, 당신은 또한에 의해 다음에 열거 코드를 작성 할 수 있습니다 javap안티 - 컴파일 코드를, 당신은 느낌에 명확한 통찰력을해야합니다.)

참조

- "자바 프로그래밍 아이디어"

추천

출처juejin.im/post/5d161dc06fb9a07ec63b28be