Sự khác biệt giữa văn bản Tiếng Việt và Chuỗi mới (Văn bản văn bản) là gì?


195

Sự khác biệt giữa hai tuyên bố sau đây là gì?

String s = "text";

String s = new String("text");


Bất cứ ai xin vui lòng trả lời này. Chuỗi a = "Java"; Chuỗi b = "Java"; System.out.println (a == b); đúng // nhưng System.out.println ("a == b?" + a == b); // sai ...
Năng lượng

tôi không hiểu khi tôi thêm một số nhận xét ("a == b?) => kết quả của tôi trở thành SAI. Tại sao?
Năng lượng

@Energy Kết quả là falsedo thứ tự các thao tác ra lệnh rằng toán tử + đi trước, nối "a == b?" với a để tạo Chuỗi "a == b? Java". Sau đó biểu thức "a==b?Java" == bước lượng thành sai.
Allison B

@ ALLisonB hiểu rồi, cảm ơn bạn rất nhiều!
Năng lượng

Câu trả lời:


187

new String("text"); rõ ràng tạo ra một thể hiện mới và khác biệt của một Stringđối tượng; String s = "text";có thể sử dụng lại một thể hiện từ nhóm hằng số chuỗi nếu có sẵn.

Bạn rất hiếm khi muốn sử dụng hàm new String(anotherString)tạo. Từ API:

String(String original): Khởi tạo một đối tượng mới được tạo String để nó thể hiện cùng một chuỗi các ký tự như đối số; nói cách khác, chuỗi mới được tạo là một bản sao của chuỗi đối số. Trừ khi cần một bản sao rõ ràng của bản gốc, việc sử dụng hàm tạo này là không cần thiết vì các chuỗi là bất biến.

Câu hỏi liên quan


Phân biệt tham chiếu có nghĩa là gì

Kiểm tra đoạn mã sau:

    String s1 = "foobar";
    String s2 = "foobar";

    System.out.println(s1 == s2);      // true

    s2 = new String("foobar");
    System.out.println(s1 == s2);      // false
    System.out.println(s1.equals(s2)); // true

==trên hai loại tham chiếu là một so sánh danh tính tham chiếu. Hai đối tượng equalskhông nhất thiết phải có ==. Nó thường sai khi sử dụng ==trên các loại tham chiếu; hầu hết thời gian equalscần phải được sử dụng thay thế.

Tuy nhiên, nếu vì bất kỳ lý do gì bạn cần tạo hai equalsnhưng không phải ==chuỗi, bạn có thể sử dụng hàm new String(anotherString)tạo. Tuy nhiên, cần phải nói lại rằng điều này rất đặc biệt và hiếm khi có ý định.

Người giới thiệu

Các vấn đề liên quan


3
Nếu tôi viết: Chuỗi s = Chuỗi mới ("abc"); Và bây giờ tôi viết: String s = "abc"; Chuỗi sẽ s = "abc"; tạo một chuỗi ký tự mới trong nhóm Chuỗi?
Kaveesh Kanwal

Tại sao không ai trả lời câu hỏi trước?
zed

2
@KaveeshKanwal Không, nghĩa đen sẽ không bị trùng lặp. Như bạn có thể thấy có 2 "abc"s. Chỉ một trong số họ sẽ đi đến nhóm String và người còn lại sẽ đề cập đến nó. Sau đó, sđó sẽ là đối tượng mới thích hợp.
Kayaman

1
@Kaveesh Kanwal - Chuỗi s = new String ("abc") sẽ chỉ tạo một đối tượng String mới có giá trị "abc". Và câu lệnh thứ 2 sẽ kiểm tra xem có bất kỳ chuỗi ký tự "abc" nào đã có trong Chuỗi Pool hay không. Nếu đã có thì tham chiếu đến cái hiện có được trả về và nếu không thì chữ mới ("abc") được tạo trong nhóm Chuỗi. Hy vọng nó giải quyết truy vấn của bạn !!
user968813

Không có "có thể" về nó. Trình biên dịch phải gộp chuỗi ký tự. JLS 3.10.5 .
Hầu tước Lorne

119

Chuỗi ký tự sẽ đi vào Chuỗi liên tục Pool .

Ảnh chụp nhanh dưới đây có thể giúp bạn hiểu nó một cách trực quan để ghi nhớ nó lâu hơn.

nhập mô tả hình ảnh ở đây


Dòng tạo đối tượng theo dòng:

String str1 = new String("java5");

Sử dụng chuỗi ký tự "java5" trong hàm tạo, một giá trị chuỗi mới được lưu trữ trong nhóm hằng chuỗi. Sử dụng toán tử mới, một đối tượng chuỗi mới được tạo trong heap với giá trị "java5".

String str2 = "java5"

Tham chiếu "str2" được chỉ đến giá trị đã được lưu trữ trong nhóm hằng số chuỗi

String str3 = new String(str2);

Một đối tượng chuỗi mới được tạo trong heap với cùng giá trị như tham chiếu bởi "str2"

String str4 = "java5";

Tham chiếu "str4" được chỉ đến giá trị đã được lưu trữ trong nhóm hằng số chuỗi

Tổng số đối tượng: Heap - 2, Pool - 1

Đọc thêm về cộng đồng Oracle


1
Câu trả lời tốt .. nhưng muốn biết rằng bây giờ tôi đang thay đổi giá trị của str1 = "java6" thì nó sẽ thay đổi giá trị của str4?
CoronaPintu

2
vâng tôi đã kiểm tra nó sẽ không thay đổi giá trị của str4
CoronaPintu

@Braj Bạn có thể cung cấp tài liệu về khẳng định của Người trả lời không?
Basil Bourque

@Braj: Các tiêu đề cho 'Heap' & 'pool' trong bảng có phải là đảo ngược không?
Rahul Kurup

Không chính xác. Nhóm hằng số được tạo tại thời gian biên dịch, không phải lúc thực hiện. Không sử dụng định dạng trích dẫn cho văn bản không được trích dẫn.
Hầu tước Lorne

15

Người ta tạo một chuỗi trong chuỗi hằng số chuỗi

String s = "text";

một chuỗi khác tạo một chuỗi trong nhóm hằng số ( "text") và một chuỗi khác trong không gian heap bình thường ( s). Cả hai chuỗi sẽ có cùng một giá trị, đó là "văn bản".

String s = new String("text");

s sau đó bị mất (đủ điều kiện cho GC) nếu sau đó không được sử dụng.

Mặt khác, chuỗi ký tự được sử dụng lại. Nếu bạn sử dụng "text"ở nhiều nơi trong lớp thì thực tế nó sẽ là một và chỉ một Chuỗi (tức là nhiều tham chiếu đến cùng một chuỗi trong nhóm).


Chuỗi trong nhóm liên tục không bao giờ bị mất. Ý của bạn là nói 's' bị mất nếu sau này không được sử dụng?
Hầu tước Lorne

@EJP: vâng, tôi có nghĩa là "s". Cảm ơn đã chú ý. Tôi sẽ sửa câu hỏi.

9

JLS

Khái niệm này được gọi là "thực tập" bởi JLS.

Đoạn văn có liên quan từ JLS 7 3.10.5 :

Hơn nữa, một chuỗi ký tự luôn luôn đề cập đến cùng một thể hiện của Chuỗi lớp. Điều này là do các chuỗi ký tự - hay nói chung hơn là các chuỗi là các giá trị của biểu thức hằng (§15.28) - được "thực hiện" để chia sẻ các thể hiện duy nhất, sử dụng phương thức String.i INTERN.

Ví dụ 3.10.5-1. Chuỗi ký tự

Chương trình bao gồm đơn vị biên dịch (§7.3):

package testPackage;
class Test {
    public static void main(String[] args) {
        String hello = "Hello", lo = "lo";
        System.out.print((hello == "Hello") + " ");
        System.out.print((Other.hello == hello) + " ");
        System.out.print((other.Other.hello == hello) + " ");
        System.out.print((hello == ("Hel"+"lo")) + " ");
        System.out.print((hello == ("Hel"+lo)) + " ");
        System.out.println(hello == ("Hel"+lo).intern());
    }
}
class Other { static String hello = "Hello"; }

và đơn vị biên dịch:

package other;
public class Other { public static String hello = "Hello"; }

tạo ra đầu ra:

true true true true false true

Liên doanh

JVMS 7 5.1 nói :

Một chuỗi ký tự là một tham chiếu đến một thể hiện của Chuỗi lớp và được lấy từ cấu trúc CONSTANT_String_info (§4.4.3) trong biểu diễn nhị phân của một lớp hoặc giao diện. Cấu trúc CONSTANT_String_info đưa ra chuỗi các điểm mã Unicode cấu thành chuỗi ký tự.

Ngôn ngữ lập trình Java yêu cầu các chuỗi ký tự chuỗi giống hệt nhau (nghĩa là các chữ có chứa cùng một chuỗi các điểm mã) phải tham chiếu đến cùng một thể hiện của Chuỗi lớp (JLS §3.10.5). Ngoài ra, nếu phương thức String.i INTERN được gọi trên bất kỳ chuỗi nào, kết quả là một tham chiếu đến cùng thể hiện của lớp sẽ được trả về nếu chuỗi đó xuất hiện dưới dạng một chữ. Do đó, biểu thức sau phải có giá trị đúng:

("a" + "b" + "c").intern() == "abc"

Để lấy được một chuỗi ký tự, Máy ảo Java kiểm tra chuỗi các điểm mã được đưa ra bởi cấu trúc CONSTANT_String_info.

  • Nếu phương thức String.i INTERN trước đây đã được gọi trong một thể hiện của Chuỗi lớp có chứa một chuỗi các điểm mã Unicode giống hệt với cấu trúc CONSTANT_String_info, thì kết quả của dẫn xuất chuỗi ký tự là một tham chiếu đến cùng thể hiện của Chuỗi lớp.

  • Mặt khác, một thể hiện mới của Chuỗi lớp được tạo có chứa chuỗi các điểm mã Unicode được cung cấp bởi cấu trúc CONSTANT_String_info; một tham chiếu đến thể hiện của lớp đó là kết quả của đạo hàm chuỗi. Cuối cùng, phương thức intern của thể hiện String mới được gọi.

Mã byte

Nó cũng được khuyến khích để xem việc triển khai mã byte trên OpenJDK 7.

Nếu chúng ta dịch ngược:

public class StringPool {
    public static void main(String[] args) {
        String a = "abc";
        String b = "abc";
        String c = new String("abc");
        System.out.println(a);
        System.out.println(b);
        System.out.println(a == c);
    }
}

chúng tôi có trên nhóm liên tục:

#2 = String             #32   // abc
[...]
#32 = Utf8               abc

main:

 0: ldc           #2          // String abc
 2: astore_1
 3: ldc           #2          // String abc
 5: astore_2
 6: new           #3          // class java/lang/String
 9: dup
10: ldc           #2          // String abc
12: invokespecial #4          // Method java/lang/String."<init>":(Ljava/lang/String;)V
15: astore_3
16: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
19: aload_1
20: invokevirtual #6          // Method java/io/PrintStream.println:(Ljava/lang/String;)V
23: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
26: aload_2
27: invokevirtual #6          // Method java/io/PrintStream.println:(Ljava/lang/String;)V
30: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
33: aload_1
34: aload_3
35: if_acmpne     42
38: iconst_1
39: goto          43
42: iconst_0
43: invokevirtual #7          // Method java/io/PrintStream.println:(Z)V

Lưu ý cách làm:

  • 03: ldc #2hằng số tương tự được tải (bằng chữ)
  • 12: một phiên bản chuỗi mới được tạo (với #2đối số)
  • 35: acđược so sánh như các đối tượng thông thường vớiif_acmpne

Việc biểu diễn các chuỗi không đổi là khá kỳ diệu trên mã byte:

  • nó có cấu trúc CONSTANT_String_info chuyên dụng , không giống như các đối tượng thông thường (ví dụ new String)
  • cấu trúc trỏ đến Cấu trúc CONSTANT_Utf8_info có chứa dữ liệu. Đó là dữ liệu cần thiết duy nhất để đại diện cho chuỗi.

và trích dẫn của JVMS ở trên dường như nói rằng bất cứ khi nào Utf8 được chỉ ra là giống nhau, thì các thể hiện giống hệt nhau được tải bởi ldc.

Tôi đã thực hiện các bài kiểm tra tương tự cho các trường và:

  • static final String s = "abc"trỏ đến bảng hằng số thông qua Thuộc tính ConstantValue
  • các trường không phải là cuối cùng không có thuộc tính đó, nhưng vẫn có thể được khởi tạo với ldc

Kết luận : có hỗ trợ mã byte trực tiếp cho nhóm chuỗi và biểu diễn bộ nhớ là hiệu quả.

Phần thưởng: so sánh với nhóm Số nguyên , không có hỗ trợ mã byte trực tiếp (nghĩa là không có tín hiệu CONSTANT_String_infotương tự).


2

@Braj: tôi nghĩ bạn đã đề cập đến cách khác. Xin hãy sửa tôi nếu tôi sai

Dòng tạo đối tượng theo dòng:

Chuỗi str1 = Chuỗi mới ("java5")

   Pool- "java5" (1 Object)

   Heap - str1 => "java5" (1 Object)

Chuỗi str2 = "java5"

  pool- str2 => "java5" (1 Object)

  heap - str1 => "java5" (1 Object)

Chuỗi str3 = Chuỗi mới (str2)

  pool- str2 => "java5" (1 Object)

  heap- str1 => "java5", str3 => "java5" (2 Objects)

Chuỗi str4 = "java5"

  pool - str2 => str4 => "java5" (1 Object)

  heap - str1 => "java5", str3 => "java5" (2 Objects)

str1không được tham gia vào các giá trị str2hoặc str3hoặc str4trong bất kỳ cách nào.
Hầu tước Lorne

1

Hãy nghĩ về "bla"việc trở thành một nhà máy ma thuật như Strings.createString("bla")(giả). Nhà máy giữ một nhóm tất cả các chuỗi được tạo theo cách này.

Nếu nó được gọi, nó sẽ kiểm tra xem đã có chuỗi trong nhóm có giá trị này chưa. Nếu đúng, nó trả về đối tượng chuỗi này, do đó các chuỗi thu được theo cách này thực sự là cùng một đối tượng.

Nếu không, nó tạo một đối tượng chuỗi mới bên trong, lưu nó trong nhóm và sau đó trả về nó. Do đó, khi cùng một giá trị chuỗi được truy vấn vào lần tiếp theo, nó sẽ trả về cùng một thể hiện.

Tạo thủ công new String("")ghi đè hành vi này bằng cách bỏ qua chuỗi ký tự chuỗi. Vì vậy, luôn luôn phải kiểm tra sự bằng nhau bằng cách equals()so sánh chuỗi ký tự thay vì đẳng thức tham chiếu đối tượng.


'Nhà máy ma thuật' mà bạn đề cập không có gì nhiều hơn hoặc ít hơn trình biên dịch Java. Đó là một sai lầm khi viết quá trình này như thể nó xảy ra trong thời gian chạy.
Hầu tước Lorne

1

Một cách đơn giản để hiểu sự khác biệt là dưới đây: -

String s ="abc";
String s1= "abc";
String s2=new String("abc");

        if(s==s1){
            System.out.println("s==s1 is true");
        }else{
            System.out.println("s==s1 is false");
        }
        if(s==s2){
            System.out.println("s==s2 is true");
        }else{
            System.out.println("s==s2 is false");
        }

đầu ra là

s==s1 is true
s==s2 is false

Do đó, String () mới sẽ luôn tạo một thể hiện mới.


1

Bất kỳ chuỗi ký tự nào được tạo bên trong nhóm ký tự chuỗi và nhóm không cho phép bất kỳ bản sao nào. Do đó, nếu hai hoặc nhiều đối tượng chuỗi được khởi tạo với cùng một giá trị bằng chữ thì tất cả các đối tượng sẽ trỏ đến cùng một nghĩa đen.

String obj1 = "abc";
String obj2 = "abc";

"Obj1" và "obj2" sẽ trỏ đến cùng một chuỗi ký tự và chuỗi ký tự chuỗi sẽ chỉ có một chữ "abc".

Khi chúng ta tạo một đối tượng lớp String bằng cách sử dụng từ khóa mới, chuỗi do đó được tạo sẽ được lưu trữ trong bộ nhớ heap. Bất kỳ chuỗi ký tự nào được truyền dưới dạng tham số cho hàm tạo của lớp String tuy nhiên được lưu trữ trong nhóm chuỗi. Nếu chúng ta tạo nhiều đối tượng sử dụng cùng một giá trị với toán tử mới, một đối tượng mới sẽ được tạo trong heap mỗi lần, vì toán tử mới này nên tránh.

String obj1 = new String("abc");
String obj2 = new String("abc");

"Obj1" và "obj2" sẽ trỏ đến hai đối tượng khác nhau trong heap và chuỗi ký tự chuỗi sẽ chỉ có một chữ "abc".

Ngoài ra, một điều đáng chú ý liên quan đến hành vi của các chuỗi là bất kỳ sự gán hoặc ghép mới nào được thực hiện trên chuỗi đều tạo ra một đối tượng mới trong bộ nhớ.

String str1 = "abc";
String str2 = "abc" + "def";
str1 = "xyz";
str2 = str1 + "ghi";

Bây giờ trong trường hợp trên:
Dòng 1: "abc" bằng chữ được lưu trữ trong nhóm chuỗi.
Dòng 2: "abcdef" nghĩa đen được lưu trữ trong nhóm chuỗi.
Dòng 3: Một chữ "xyz" mới được lưu trữ trong nhóm chuỗi và "str1" bắt đầu trỏ đến nghĩa đen này.
Dòng 4: Vì giá trị được tạo bằng cách nối thêm vào một biến khác, kết quả được lưu trữ trong bộ nhớ heap và chữ "ghi" được thêm vào sẽ được kiểm tra sự tồn tại của nó trong nhóm chuỗi và sẽ được tạo vì nó không tồn tại trường hợp trên.


0

Mặc dù nó trông giống nhau từ quan điểm của các lập trình viên, nhưng nó có tác động hiệu suất lớn. Bạn sẽ muốn sử dụng mẫu đầu tiên hầu như luôn luôn.


0
String str = new String("hello")

Nó sẽ kiểm tra xem chuỗi hằng số chuỗi có chứa Chuỗi "xin chào" không? Nếu có thì nó sẽ không thêm một mục trong nhóm hằng chuỗi. Nếu không có thì nó sẽ thêm một mục trong nhóm hằng chuỗi.

Một đối tượng sẽ được tạo trong vùng nhớ heap và strcác điểm tham chiếu đến đối tượng được tạo ở vị trí bộ nhớ heap.

nếu bạn muốn strtham chiếu đến đối tượng điểm chứa trong nhóm hằng chuỗi thì người ta phải gọi một cách rõ ràngstr.intern();

String str = "world";

Nó sẽ kiểm tra xem chuỗi hằng số chuỗi có chứa Chuỗi "xin chào" không? Nếu có thì nó sẽ không thêm một mục trong nhóm hằng chuỗi. Nếu không có thì nó sẽ thêm một mục trong nhóm hằng chuỗi.

Trong cả hai trường hợp trên, strcác điểm tham chiếu đến Chuỗi "world"có trong nhóm Hằng.


'Nó' là trình biên dịch Java. Chuỗi ký tự tạo ra một mục duy nhất trong nhóm hằng số, tại thời gian biên dịch. Đó là một sai lầm khi mô tả quá trình này như thể nó xảy ra trong thời gian chạy ..
Hầu tước Lorne

Bạn có thể vui lòng giải thích những gì sai trong bài viết này rõ ràng?
Jayesh 7/2/2016

Điều sai trong bài viết này là chuỗi ký tự được gộp vào thời gian tuân thủ, như tôi đã nói. Không phải khi thực thi mã, như trong câu trả lời của bạn.
Hầu tước Lorne

@EJP Tôi đánh giá cao phản ứng của bạn. Bạn có thể vui lòng chỉ ra dòng chính xác sai trong câu trả lời. Tôi thấy tất cả các câu trả lời ở trên giống như những gì tôi đã viết. Xin hãy giúp đỡ, tôi muốn sửa chữa sự hiểu biết của tôi. Cảm ơn.
Jayesh

Bạn đã viết về toàn bộ quá trình như thể tất cả diễn ra khi dòng mã được thực thi, mà như tôi đã nhiều lần nói với bạn, không phải là trường hợp. Bạn không thể giảm tất cả những điều đó thành một "dòng chính xác" sai trong câu trả lời của bạn.
Hầu tước Lorne

0

Khi bạn lưu trữ một Chuỗi như

String string1 = "Hello";

trực tiếp, sau đó JVM tạo một đối tượng String với giá đã cho trong một khối bộ nhớ riêng gọi là hằng số chuỗi.

Và bất cứ khi nào chúng ta có xu hướng thử và tạo ra một Chuỗi khác như

String string2 = "Hello";

JVM xác minh xem có hay không bất kỳ đối tượng Chuỗi nào có giá không đổi tồn tại trong nhóm hằng số Chuỗi, nếu vậy, thay vì tạo một đối tượng hoàn toàn mới, JVM gán tham chiếu của đối tượng hiện có cho biến mới.

Và khi chúng tôi lưu trữ String như

String string = new String("Hello");

sử dụng từ khóa mới, một đối tượng hoàn toàn mới với giá đã cho được thực hiện bất kể nội dung của nhóm hằng số Chuỗi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.