2012-10-14 16 views
7

Według JLS (15,28 wyrażeniami) ekspresyjnym zawierającym tylko:wykonawcza ciąg oceny konkatenacji

i)Literals of primitive type and literals of type String (§3.10.1, §3.10.2, §3.10.3, 
§3.10.4, §3.10.5) 
or 
ii)Simple names (§6.5.6.1) that refer to constant variables (§4.12.4). 
or 
iii)... 

jest stałą ekspresję.

Teraz String s1="a"+"b"; jest wyrażeniem stałym i zostanie oszacowany na "ab" w czasie kompilacji.

tak s1="ab";

[1] mam rację, mówiąc, że teraz istnieją trzy obiekty w basenie String gdyż zgodnie z powyższym stwierdzeniem: - "A", "B", "AB" ???

Teraz

final String s="a"; 
final String s1="b"; 
String s2=s+s1; // is also constant expression and get evaluated at compile time. 

powyższy kod będzie transaled do s2="a"+"b"; po kompilacji.

dzięki czemu s2="ab"; zostanie automatycznie zapisany w puli ciągów.

Ale

// note there is no final now. 
String s="a"; 
String s1="b"; 
String s2="a"+"b"; // constant expression. 
String s3=s+s1; // is NOT a constant expression and get evaluated at RUN TIME. 

dla String s3=s+s1;, kod zostanie przetłumaczony na:

s3=new StringBuilder(String.valueOf(s)).append(s1).toString(); 

i utworzy nowy obiekt String.

dlatego s2==s3 będzie fałszywy;

Czy to oznacza wynik konkatenacji ciągów ocenianych w środowisku wykonawczym przy użyciu StringBuilder nie jest przechowywany w puli String, ale zamiast tego przechodzi do sterty (poza pulą)?

+1

Należy pamiętać, że pula ciągów podczas etapu kompilacji nie jest taka sama, jak pula ciągów podczas uruchamiania programu. –

+0

@ Thorbjørn Ravn Andersen: Fajna uwaga. możesz podać mi jakiś link lub źródło, gdzie mogę zobaczyć szczegóły twojego punktu. –

Odpowiedz

3

Od JLS §15.18.1:

15.18.1. Łączenie łańcuchów Operator +

Jeśli tylko jedno wyrażenie argument jest typu String, a następnie ciąg konwersji (§5.1.11) odbywa się na drugim argumencie do wytworzenia ciąg w czasie wykonywania.

Wynikiem konkatenacji ciągów jest odwołanie do obiektu String , który jest połączeniem dwóch ciągów operacji. Znaki lewego operandu poprzedzają znaki prawego argumentu w nowo utworzonym łańcuchu.

Obiekt String jest nowo utworzony (§ 12.5), chyba że wyrażenie jest wyrażeniem stałym w czasie kompilacji (§15.28).

Implementacja może wykonać konwersję i konkatenację w jednym kroku, aby uniknąć tworzenia, a następnie odrzucania obiektu String klasy pośredniej . Aby zwiększyć wydajność powtarzania ciągów znaków , kompilator Java może używać klasy StringBuffer lub podobnej techniki w celu zredukowania liczby pośrednich obiektów String tworzonych przez ocenę wyrażenia.

W przypadku typów pierwotnych implementacja może również optymalizować tworzenie obiektu opakowania za pomocą konwersji z typu podstawowego na typ .

Więc

  1. Jest jeden obiekt w stałej puli ("ab"). Pliki tymczasowe nie są zapisywane.
  2. Podobnie, w puli stałej będzie tylko "ab".
  3. Nowy ciąg jest nowym obiektem String i nie będzie w puli, chyba że zostanie wyraźnie internowany.

Pouczające jest spojrzeć na niektóre kodu bajtowego:

String sa1 = "a"+ "b"; 

final String sb1 = "a"; 
final String sb2 = "b"; 
String sb3 = sb1 + sb2; 

String sc1 = "a"; 
String sc2 = "b"; 
String sc3 = "a" + "b"; 
String sc4 = sc1 + sc2; 

staje

Code: 
    0: ldC#2; //String ab 
    2: astore_0 
    3: ldC#2; //String ab 
    5: astore_3 
    6: ldC#3; //String a 
    8: astore 4 
    10: ldC#4; //String b 
    12: astore 5 
    14: ldC#2; //String ab 
    16: astore 6 
    18: new #5; //class java/lang/StringBuilder 
    21: dup 
    22: invokespecial #6; //Method java/lang/StringBuilder."<init>":()V 
    25: aload 4 
    27: invokevirtual #7; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
    30: aload 5 
    32: invokevirtual #7; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
    35: invokevirtual #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 
    38: astore 7 
    40: return 

Widać, że w pierwszych dwóch przypadkach, „AB” jest ładowany prosto ze stałej puli . W trzecim bloku otrzymujemy tłumaczenie sc4 = new StringBuilder().append(sc1).append(sc2).toString(), które tworzy nowy obiekt.

0

Czy to znaczy wynik konkatenacji String oceniano przy starcie użyciu StringBuilder nie przechowywane w łańcuchach Pool lecz idzie na stercie (poza basenem)

Tak. To jest poprawne. Utworzy nowy obiekt w stercie.

1

mam rację, mówiąc, że teraz istnieją trzy obiekty w basenie String gdyż zgodnie z powyższym stwierdzeniem: - „A”, „B”, „AB” ???

Nie. Jesteś niepoprawny. Konkatenacja jest wykonywana w czasie kompilacji, a tylko obiekt "ab" będzie przechowywany w puli ciągów.

Czy to znaczy wynik konkatenacji String ocenianym w czasie wykonywania przy użyciu StringBuilder nie przechowywane w łańcuchach basen, ale zamiast tego idzie do sterty (poza basenem)?

Tak, masz rację w tej kwestii.


Podsumowując, literały łańcuchowe i wartości ciągłych wyrażeń ciągów kompilacji będą internowane (i przechowywane w puli ciągów znaków). Wyniki innych konkatenacji ciągów nie będą internowane ... chyba że jednoznacznie nazwiesz String.intern(). (I rzadko należy to robić ... ponieważ strunniki interwencyjne zwykle wyrządzają więcej szkody niż pożytku.)

W obu przypadkach powinieneś unikać używania == do porównywania ciągów.

+0

+1 dla pierwszego oświadczenia – Ankur

+0

Sugerowałbym dodanie uwagi na temat wywoływania tylko "String.intern()", jeśli ekspert twierdzi, że jest to dobry pomysł. –

0

Tak, masz rację, że ciągi oceniane w środowisku wykonawczym przy użyciu StringBuilder nie są zapisywane w puli ciągów. Ponieważ:

  1. Jest new operator (który alokuje nową pamięć dla obiektu na stercie)
  2. I jak widzimy, tworząc kod:

    AbstractStringBuilder (int pojemności) { wartości = nowy char [pojemność]; }

Istnieje nowa referencja do tablicy znaków przydzielona w stercie.