머리말
인터뷰는 자주이 질문을 요청할 수 있습니다 : 문자열, 모두 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
안티 - 컴파일 코드를, 당신은 느낌에 명확한 통찰력을해야합니다.)
참조
- "자바 프로그래밍 아이디어"