2014-09-10 2 views
1

자바 컴파일러가 String 추가 (+)를 StringBuilder.append() 메소드로 변환하지 않는 것을 확인했습니다. 나는자바 컴파일러가 문자열 연결을 최적화하지 않습니다.

public void doSomething(String a, String b) { 
    String c = a + "a"; 
    String d = b + "b"; 
    String e = c + d; 
    String f = e; 
    System.out.println(f); 
} 

컴파일 후 내 방법을 디 컴파일 하나 개의 방법이있는 클래스를 만들었 같이 보았다 :

public void doSomething(String paramString1, String paramString2) 
{ 
    String str1 = paramString1 + "a"; 
    String str2 = paramString2 + "b"; 
    String str3 = str1 + str2; 
    String str4 = str3; 
    System.out.println(str4); 
} 

컴파일러가 내 코드를 최적화하지 왜? 패키징에 개미를 사용하고 디버그 설정이 잘못되었습니다. 또한 단일 자바 파일 javac 시도했지만 결과가 동일합니다.

+6

정말입니까? 어쩌면 디 컴파일러가 코드를 복원 할 정도로 똑똑할까요? – talex

+0

'String g = a + b + c;'시도해보고 디 컴파일 결과를 알려주시겠습니까? –

+1

어떤 디 컴파일러를 사용하고 있습니까? –

답변

2

나는 javap -c Test.class이고 StringBuilder가 나타났습니다 (Java 8).

public void doSomething(java.lang.String, java.lang.String); 
Code: 
    0: new   #2 // class StringBuilder 
    3: dup   
    4: invokespecial #3 // Method StringBuilder."<init>":()V 
    7: aload_1  
    8: invokevirtual #4 // Method StringBuilder.append:(LString;)LStringBuilder; 
    11: ldc   #5 // String a 
    13: invokevirtual #4 // Method StringBuilder.append:(LString;)LStringBuilder; 
    16: invokevirtual #6 // Method StringBuilder.toString:()LString; 
    19: astore_3  
    20: new   #2 // class StringBuilder 
    23: dup   
    24: invokespecial #3 // Method StringBuilder."<init>":()V 
    27: aload_2  
    28: invokevirtual #4 // Method StringBuilder.append:(LString;)LStringBuilder; 
    31: ldc   #7 // String b 
    33: invokevirtual #4 // Method StringBuilder.append:(LString;)LStringBuilder; 
    36: invokevirtual #6 // Method StringBuilder.toString:()LString; 
    39: astore  4 
    41: new   #2 // class StringBuilder 
    44: dup   
    45: invokespecial #3 // Method StringBuilder."<init>":()V 
    48: aload_3  
    49: invokevirtual #4 // Method StringBuilder.append:(LString;)LStringBuilder; 
    52: aload   4 
    54: invokevirtual #4 // Method StringBuilder.append:(LString;)LStringBuilder; 
    57: invokevirtual #6 // Method StringBuilder.toString:()LString; 
    60: astore  5 
    62: aload   5 
    64: astore  6 
    66: getstatic  #8 // Field System.out:Ljava/io/PrintStream; 
    69: aload   6 
    71: invokevirtual #9 // Method java/io/PrintStream.println:(LString;)V 
    74: return   

자연 분해능을 달성하기 위해 decompiler가 이것을 단순화하려고합니다.

+0

답변 해 주셔서 감사합니다. 나는 컴파일과 디 컴파일 이후에 'String h = new StringBuilder (g) .append (a) .toString();' 을 추가했다. 문자열 str6 = str5 + paramString1; –

+0

@ ErcanÖzdemir 아, 깔끔한 속임수 . 난 당신이 자바 버전으로 javap을 할 줄 알았는데. –

0

런타임시 JIT 용으로 남아 있기 때문에 Java 컴파일러는 컴파일시 최적화를 수행하지 않습니다. 여러 문에서 문자열을 효율적으로 작성하려면 StringBuilder을 사용해야합니다.

+ 연산자는 StringBuilder 개체의 호출로 컴파일됩니다. 당신이 쓸 때 : 당신이 작성했던 것처럼

String c = a + "a"; 

컴파일러는 코드를 생성합니다 : 당신이 그런 a + "a" + b + "b" 같은 단일 식에서 + 여러 번 사용하는 경우

String c = new StringBuilder().append(a).append("a").toString(); 

이 컴파일러는 하나를 사용합니다 StringBuilder 전체 표현식의 경우 append을 필요한만큼 여러 번 호출하십시오. 그러나 여러 문을 하나의 동등한 식으로 최적화하지 않으므로 하나의 StringBuilder을 사용하여 모든 문자열을 함께 조인하려면 단일 식으로 작성하거나 코드에 StringBuilder을 명시 적으로 사용해야합니다.

+0

StringBuilder 최적화는 JIT가 아니라 컴파일러에서 매우 일반적으로 수행되는 최적화입니다. – nneonneo

+0

뭐,'+ '를 사용하여 여러 문장을 하나의 큰 표현으로 결합 할 수 있습니까? 나는 컴파일러가 그것을 한 번도 본 적이 없으며, OP를 위해서나 Joop Eggen (그의 대답에서 바이트 코드로 판단)을 위해 분명히 발생하지 않았다. – Wyzard

+0

? 뭐? Joop의 바이트 코드를 봐라. 컴파일러는'+'를'StringBuilder.append'로 변환합니다. (허용되지 않으므로 인접한 여러 표현식을 결합하지 않습니다.) – nneonneo

3

디 컴파일러가 실제로 코드를 단순화합니다.

이 소스 파일을 고려해

public class foo { 
    public void a(String[] args) { 
     String b = (new StringBuilder()).append("x").append(args[0]).toString(); 
    } 

    public void b(String[] args) { 
     String b = "x" + args[0]; 
    } 
} 

javap 출력 :

public class foo { 
    public foo(); 
    Code: 
     0: aload_0  
     1: invokespecial #1     // Method java/lang/Object."<init>":()V 
     4: return   

    public void a(java.lang.String[]); 
    Code: 
     0: new   #2     // class java/lang/StringBuilder 
     3: dup   
     4: invokespecial #3     // Method java/lang/StringBuilder."<init>":()V 
     7: ldc   #4     // String x 
     9: invokevirtual #5     // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
     12: aload_1  
     13: iconst_0  
     14: aaload   
     15: invokevirtual #5     // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
     18: invokevirtual #6     // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 
     21: astore_2  
     22: return   

    public void b(java.lang.String[]); 
    Code: 
     0: new   #2     // class java/lang/StringBuilder 
     3: dup   
     4: invokespecial #3     // Method java/lang/StringBuilder."<init>":()V 
     7: ldc   #4     // String x 
     9: invokevirtual #5     // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
     12: aload_1  
     13: iconst_0  
     14: aaload   
     15: invokevirtual #5     // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
     18: invokevirtual #6     // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 
     21: astore_2  
     22: return   
} 

이 바이트 코드가 본질적으로 동일한 것을 볼 : 컴파일러는 StringBuilder.append 최적화를 사용하는 방법 a에있어서 b 변형시켰다. 이제

의 JD가 말하는 보자 :

public class foo 
{ 
    public void a(String[] paramArrayOfString) 
    { 
    String str = "x" + paramArrayOfString[0]; 
    } 

    public void b(String[] paramArrayOfString) { 
    String str = "x" + paramArrayOfString[0]; 
    } 
} 

맞아 그건 : JD 실제로 기능 a를 받아 문자열 또한 존재로 해석의 원래 소스 코드 StringBuilder 명시를 사용하여 지정에도 불구하고 !

따라서 패턴이 사용되는 것으로 감지되면 JD는 StringBuilder 최적화를 취소하려고 시도 할 수 있습니다.