2013-07-08 46 views
73

Tôi có một câu hỏi về một số hành vi của nhóm String lạ. Tôi đang sử dụng == để so sánh các Chuỗi bằng nhau để tìm hiểu xem chúng có nằm trong hồ bơi hay không.Hành vi chung của chuỗi lạ

public class StringPoolTest { 
    public static void main(String[] args) { 
    new StringPoolTest().run(); 
    } 

    String giveLiteralString() { 
    return "555"; 
    } 

    void run() { 
    String s1 = giveLiteralString() + ""; 
    System.out.println("555" == "555" + ""); 
    System.out.println(giveLiteralString() == giveLiteralString() + ""); 
    } 
} 

Đầu ra là:

true 
false 

mà là một bất ngờ lớn đối với tôi. Bất cứ ai có thể giải thích điều này xin vui lòng? Tôi nghĩ rằng một cái gì đó về việc này đang diễn ra tại thời gian biên dịch. Nhưng tại sao thêm "" vào một Chuỗi tạo ra bất kỳ sự khác biệt nào?

+0

@MarkoTopolnik Giống như tôi. – johnchen902

+5

@MarkoTopolnik Tôi biết câu hỏi hơi khác một chút. Nhưng câu trả lời luôn giống như "XXX là hằng số biên dịch, trong khi YYY không phải là". Có lẽ tôi đã chọn một câu hỏi sai. – johnchen902

+1

@ johnchen902 Tôi đồng ý, nhưng bạn đã đăng câu hỏi sai là trùng lặp :-) – Thihara

Trả lời

111
"555" + "" 

là một compile-time constant, trong khi

giveLiteralString() + "" 

thì không. Vì vậy, trước đây biên dịch vào chuỗi liên tục "555" và sau đó biên dịch vào lời gọi phương thức thực tế và nối, dẫn đến một cá thể String mới.


Xem thêm JLS §3.10.5 (String Literals):

Strings tính bằng cách nối tại thời gian chạy mới được tạo ra và do đó riêng biệt.

+0

bạn có thể vui lòng cung cấp trích dẫn về trường hợp chuỗi ký tự mới của lời gọi phương thức? bất kỳ liên kết JLS nào? – sanbhat

+0

Ngoài ra, trình tối ưu hóa mã, chạy trước/trong khi biên dịch, có thể đã kết hợp '" 555 "+" "' với một đối tượng chuỗi đơn "" 555 "' Trong khi 'method() +" "' vẫn sẽ là ' method() + "" 'sau khi biên dịch. – Korashen

+3

@sanbhat Nhấp vào "hằng số biên dịch" trong câu trả lời của tôi. –

31

Sau khi dịch ngược dòng này

System.out.println("555" == "555" + ""); 

tôi đã bytecode này

LINENUMBER 8 L0 
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream; 
    ICONST_1 
    INVOKEVIRTUAL java/io/PrintStream.println(Z)V 
    ... 

tương đương với

System.out.println(true); 

đó có nghĩa là biểu hiện "555" == "555" + "" biên dịch để boolean true.

Đối giveLiteralString() == giveLiteralString() + "" javac xây dựng bytecode này

LINENUMBER 8 L0 
    INVOKESTATIC Test1.giveLiteralString()Ljava/lang/String; 
    NEW java/lang/StringBuilder 
    DUP 
    INVOKESTATIC Test1.giveLiteralString()Ljava/lang/String; 
    INVOKESTATIC java/lang/String.valueOf(Ljava/lang/Object;)Ljava/lang/String; 
    INVOKESPECIAL java/lang/StringBuilder.<init>(Ljava/lang/String;)V 
    INVOKEVIRTUAL java/lang/StringBuilder.toString()Ljava/lang/String; 
    IF_ACMPNE L1 
    ... 

tương đương với

if (giveLiteralString() == new StringBuilder(giveLiteralString()).append("").toString()) { 
... 

mà sẽ luôn luôn tạo ra sai vì ở đây chúng ta đang so sánh 2 đối tượng disctinct.

+2

"sẽ luôn sản xuất sai" - Về mặt kỹ thuật, StringBuilder không phải là * bắt buộc * để tạo chuỗi không phải là tập trung. Nó chỉ đơn giản là không có lý do chính đáng để nó cố gắng để sản xuất một thực tập. –

+1

Từ StringBuilder.toString API - Một đối tượng String mới được cấp phát và khởi tạo để chứa chuỗi ký tự hiện được đại diện bởi đối tượng này. Chuỗi này sau đó được trả lại. –

+0

Nhưng không có gì nói rằng nó không thể được interned. –

4

Trong trường hợp thứ hai, COULD trình biên dịch đã nhận ra rằng + "" là không có loại, vì "" là giá trị thời gian biên dịch được biết là có độ dài bằng không. Nhưng trình biên dịch vẫn được yêu cầu để kiểm tra kết quả từ giveLiteralString cho null (vì việc kiểm tra null sẽ xảy ra do hoạt động + trong trường hợp không được tối ưu hóa), do đó, đơn giản nhất là không cố gắng tối ưu hóa.

Kết quả là trình biên dịch tạo mã để thực hiện phép nối và chuỗi mới được tạo.

+5

Trình biên dịch phải tuân thủ JLS, trong đó nêu rõ rằng kết quả của việc ghép nối thời gian chạy là một chuỗi mới. –

0

Compile Time Concatenation Chuỗi tính bằng biểu thức hằng số được thực hiện tại thời gian biên dịch và hạch toán như hằng số hoặc chữ nghĩa là giá trị của chuỗi hay biểu hiện được biết đến hoặc đánh giá tại thời gian biên dịch do đó trình biên dịch có thể kiểm tra giá trị như nhau trong string pool và trả về cùng một tham chiếu đối tượng chuỗi.

Runtime Concatenation biểu thức chuỗi có giá trị được biết đến hoặc không thể được đánh giá lúc biên dịch, nhưng phụ thuộc vào đầu vào hoặc điều kiện của thời gian chạy sau đó trình biên dịch sẽ không biết giá trị của chuỗi và do đó luôn hạ cánh bằng cách sử dụng StringBuilder để nối chuỗi và luôn trả về một chuỗi mới. Tôi đoán ví dụ này sẽ làm rõ hơn.

public static void main(String[] args) { 
    new StringPoolTest().run(); 
    } 
    String giveLiteralString() { 
    return "555"; 
    } 

    void run() { 
    System.out.println("555" + 9 == "555" + 9); 
    System.out.println("555"+Integer.valueOf(9) == "555" + Integer.valueOf(9)); 
    System.out.println(giveLiteralString() == giveLiteralString()); 
    // The result of runtime concatenation is a fresh string. 
    System.out.println(giveLiteralString() == giveLiteralString() + ""); 
    }