Chuỗi là các đối tượng trong Java, vậy tại sao chúng ta không sử dụng 'new' để tạo chúng?


104

Chúng tôi thường tạo các đối tượng bằng newtừ khóa, như:

Object obj = new Object();

Chuỗi là các đối tượng, nhưng chúng tôi không sử dụng newđể tạo chúng:

String str = "Hello World";

Tại sao thế này? Tôi có thể tạo một chuỗi với new?



1
Bởi vì chuỗi ký tự đã là đối tượng.
Marquis of Lorne,

1
Lưu ý rằng new String(...)đã được sử dụng để phá vỡ một chi tiết triển khai khi tạo chuỗi con lớn. Điều này đã được sửa trong Java 7 và không cần thiết nữa.
Thorbjørn Ravn Andersen

Tôi là người thích thứ 100 của bài đăng này. :)
Mukit09

Câu trả lời:


130

Ngoài những gì đã nói, các chuỗi ký tự [tức là các chuỗi giống "abcd"nhưng không giống new String("abcd")] trong Java được thực hiện - điều này có nghĩa là mỗi khi bạn tham chiếu đến "abcd", bạn sẽ nhận được tham chiếu đến một phiên bản duy nhất String, thay vì một phiên bản mới mỗi lần. Vì vậy, bạn sẽ có:

String a = "abcd";
String b = "abcd";

a == b; //True

nhưng nếu bạn có

String a = new String("abcd");
String b = new String("abcd");

thì có thể có

a == b; // False

(và trong trường hợp bất kỳ ai cần nhắc nhở, hãy luôn sử dụng .equals()để so sánh các Chuỗi; ==kiểm tra sự bình đẳng vật lý).

Interning chuỗi ký tự là tốt vì chúng thường được sử dụng nhiều hơn một lần. Ví dụ: hãy xem xét mã (tiếp theo):

for (int i = 0; i < 10; i++) {
  System.out.println("Next iteration");
}

Nếu chúng ta không có quá trình thực hiện các Chuỗi, thì "Lần lặp tiếp theo" sẽ cần phải được khởi tạo 10 lần, trong khi bây giờ nó sẽ chỉ được khởi tạo một lần.


1
Bằng cách sử dụng String a = new String ("abcd"), điều đó có nghĩa là hai chuỗi có nội dung tương tự có trong bộ nhớ.
thay đổi

1
Đúng - trình biên dịch sẽ không nhất thiết phải kiểm tra xem một Chuỗi như vậy đã được thực hiện chưa (mặc dù bạn chắc chắn có thể viết một chuỗi đã được thực hiện).
danben

vâng, tối ưu hóa này có thể thực hiện được vì các chuỗi là bất biến và do đó có thể được chia sẻ mà không gặp vấn đề gì. việc xử lý "asdf" được chia sẻ là một triển khai của mẫu thiết kế 'Flyweight'.
manuel aldana

Không ai nói rằng nó không thể, chỉ là nó không được đảm bảo. Đó có phải là lời tán thành của bạn không?
danben

Ý bạn là gì khi "== kiểm tra bình đẳng đối tượng"? Điều này có vẻ không đúng với tôi, nhưng có lẽ bạn muốn nói điều gì đó khác với ý nghĩa của điều này.
Dawood ibn Kareem,

32

Chuỗi là đối tượng "đặc biệt" trong Java. Các nhà thiết kế Java đã quyết định một cách khôn ngoan rằng các Chuỗi được sử dụng thường xuyên đến mức họ cần có cú pháp riêng cũng như chiến lược bộ nhớ đệm. Khi bạn khai báo một chuỗi bằng cách nói:

String myString = "something";

myString là một tham chiếu đến đối tượng String có giá trị là "something". Nếu sau này bạn khai báo:

String myOtherString = "something";

Java đủ thông minh để tìm ra rằng myString và myOtherString là giống nhau và sẽ lưu trữ chúng trong một bảng Chuỗi chung như cùng một đối tượng. Nó dựa trên thực tế là bạn không thể sửa đổi Chuỗi để thực hiện điều này. Điều này làm giảm dung lượng bộ nhớ cần thiết và có thể so sánh nhanh hơn.

Thay vào đó, nếu bạn viết

String myOtherString = new String("something");

Java sẽ tạo một đối tượng hoàn toàn mới cho bạn, khác biệt với đối tượng myString.


Này ... nó không đòi hỏi "trí tuệ vô hạn" để nhận ra sự cần thiết của một số loại hỗ trợ cú pháp cho các ký tự chuỗi. Hầu như mọi thiết kế ngôn ngữ lập trình nghiêm túc khác đều hỗ trợ một số loại ký tự chuỗi.
Stephen C

12
Cường điệu đã được giảm xuống còn gây choáng, Captain :)
Jamie McCrindle

16
String a = "abc"; // 1 Object: "abc" added to pool

String b = "abc"; // 0 Object: because it is already in the pool

String c = new String("abc"); // 1 Object

String d = new String("def"); // 1 Object + "def" is added to the Pool

String e = d.intern(); // (e==d) is "false" because e refers to the String in pool

String f = e.intern(); // (f==e) is "true" 

//Total Objects: 4 ("abc", c, d, "def").

Hy vọng điều này xóa một vài nghi ngờ. :)


String d = new String ("def"); // 1 Object + "def" được thêm vào Pool -> ở đây "def" sẽ chỉ được thêm vào pool nếu nó chưa ở đó
southherton

@southerton Vô nghĩa. Nó đã ở trong hồ bơi. Nó đã được đặt ở đó bởi trình biên dịch.
Marquis of Lorne,

@EJP tại sao (e == d) là sai ở đây? cả hai đều tham chiếu đến cùng một đối tượng "def" trong hồ bơi phải không?
Raja,

String c = new String ("abc"); // 1 Object ... câu lệnh này có đúng không? Nếu "abc" đã được tham chiếu từ nhóm hằng số thì việc sử dụng phương thức inter là gì?
Prashanth Debbadwar

6

Đó là một lối tắt. Ban đầu nó không phải như vậy, nhưng Java đã thay đổi nó.

Câu hỏi thường gặp này nói về nó một cách ngắn gọn. Hướng dẫn Đặc tả Java cũng nói về nó. Nhưng tôi không thể tìm thấy nó trên mạng.


2
Liên kết bị hỏng và tôi không biết bất kỳ bằng chứng nào khác cho thấy nó đã từng bị thay đổi.
Marquis of Lorne,

1
@EJP Nó vẫn nằm trong máy quay lui nếu điều đó hữu ích.
Arjan

6

Chuỗi có một vài cách tối ưu (vì muốn có một cụm từ tốt hơn). Lưu ý rằng Chuỗi cũng có tính năng nạp chồng toán tử (đối với toán tử +) - không giống như các đối tượng khác. Vì vậy, nó rất là một trường hợp đặc biệt.


1
Dấu + thực sự là một toán tử được dịch thành một lệnh gọi StringBuilder.append (..).
whiskyierra

5

Chúng tôi thường sử dụng các chuỗi ký tự để tránh tạo các đối tượng không cần thiết. Nếu chúng ta sử dụng toán tử new để tạo đối tượng String, thì nó sẽ tạo đối tượng mới mọi lúc.

Thí dụ:

String s1=“Hello“;
String s2=“Hello“;
String s3= new String(“Hello“);
String s4= new String(“Hello“);

Đối với mã trên trong bộ nhớ:

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


2

Trong Java, Chuỗi là một trường hợp đặc biệt, với nhiều quy tắc chỉ áp dụng cho Chuỗi. Dấu ngoặc kép khiến trình biên dịch tạo đối tượng Chuỗi. Vì các đối tượng chuỗi là bất biến, điều này cho phép trình biên dịch đan xen nhiều chuỗi và xây dựng một nhóm chuỗi lớn hơn. Hai hằng chuỗi giống nhau sẽ luôn có cùng một tham chiếu đối tượng. Nếu bạn không muốn trường hợp này xảy ra, thì bạn có thể sử dụng String mới ("") và điều đó sẽ tạo ra một đối tượng String trong thời gian chạy. Phương thức intern () được sử dụng phổ biến để kiểm tra các chuỗi được tạo động dựa trên bảng tra cứu chuỗi. Sau khi một chuỗi được thực hiện, tham chiếu đối tượng sẽ trỏ đến cá thể Chuỗi chính tắc.

    String a = "foo";
    String b = "foo";
    System.out.println(a == b); // true
    String c = new String(a);
    System.out.println(a == c); // false
    c = c.intern();
    System.out.println(a == c); // true

Khi trình nạp lớp tải một lớp, tất cả các hằng số Chuỗi được thêm vào nhóm Chuỗi.


"Dấu ngoặc kép khiến trình biên dịch tạo một đối tượng Chuỗi." nhận xét đánh giá thấp
csguy

0

Cú pháp đặc biệt. Các

String s = new String("ABC");

cú pháp vẫn có sẵn.


1
Điều này không hoàn toàn đúng. s = new String ("ABC") sẽ không cho bạn kết quả tương tự như s = "ABC". Xem bình luận của danben.
Steve B.

2
Ngoài ra, hơi trớ trêu, trước tiên nó sẽ tạo một cá thể Chuỗi đại diện cho nội dòng "ABC" - và sau đó chuyển nó làm đối số cho lời gọi hàm tạo sẽ tạo ra một chuỗi có giá trị giống hệt nhau.
Andrzej Doyle

1
Trường hợp sử dụng hợp lệ cho hàm tạo này là String small = new String(huge.substring(int, int));, cho phép bạn tái chế lớn cơ bản char[]từ hugeChuỗi ban đầu .
Pascal Thivent

1
@PascalThivent có nhưng không còn nữa với Java 8. Nó không chia sẻ mảng nữa (để chuẩn bị cho các tối ưu hóa khác như tự động khử trùng lặp chuỗi với G1 hoặc nén chuỗi sắp tới).
eckes

@AndrzejDoyle Không đúng. Trình biên dịch tạo đối tượng cho chữ.
Marquis of Lorne,

0

Bạn vẫn có thể sử dụng new String("string"), nhưng sẽ khó hơn để tạo chuỗi mới mà không có ký tự chuỗi ... bạn sẽ phải sử dụng mảng ký tự hoặc byte :-) Các ký tự chuỗi có một thuộc tính bổ sung: tất cả các ký tự chuỗi giống nhau từ bất kỳ lớp nào đều trỏ đến cùng một chuỗi dụ (họ được thực tập).


0

Hầu như không cần phải tạo mới một chuỗi vì chữ (các ký tự trong dấu ngoặc kép) đã là một đối tượng Chuỗi được tạo khi lớp chủ được tải. Hoàn toàn hợp pháp khi gọi các phương thức theo nghĩa đen và không, điểm khác biệt chính là sự tiện lợi do các nghĩa đen cung cấp. Sẽ là một nỗi đau lớn và lãng phí thời gian nếu chúng ta phải tạo một mảng ký tự và điền nó bằng ký tự char và chúng thực hiện một Chuỗi mới (mảng ký tự).


0

Hãy thoải mái tạo một Chuỗi mới với

String s = new String("I'm a new String");

Ký hiệu thông thường s = "new String";ít nhiều là một phím tắt thuận tiện - nên được sử dụng vì lý do hiệu suất ngoại trừ những trường hợp khá hiếm, khi bạn thực sự cần Chuỗi đủ điều kiện cho phương trình

(string1.equals(string2)) && !(string1 == string2)

BIÊN TẬP

Đáp lại nhận xét: đây không phải là một lời khuyên mà chỉ là một câu trả lời trực tiếp cho luận điểm của người hỏi, rằng chúng tôi không sử dụng từ khóa 'mới' cho Chuỗi, điều này đơn giản là không đúng. Hy vọng bản chỉnh sửa này (bao gồm cả phần trên) làm rõ điều này một chút. BTW - có một vài câu trả lời hay và tốt hơn nhiều cho câu hỏi trên trên SO.


4
-1 - Lời khuyên tồi. Bạn KHÔNG nên "thoải mái" sử dụng new String(...)BỎ ứng dụng của bạn BẮT BUỘC bạn phải tạo một Chuỗi với một danh tính riêng biệt.
Stephen C

1
Tôi biết điều đó. Đã chỉnh sửa bài viết để làm rõ.
Andreas Dolk

0

Nhóm chữ chứa bất kỳ Chuỗi nào được tạo mà không sử dụng từ khóa new.

Có một sự khác biệt: Chuỗi không có tham chiếu mới được lưu trữ trong nhóm chữ String và Chuỗi có mới nói rằng chúng nằm trong bộ nhớ heap.

Chuỗi với mới nằm ở nơi khác trong bộ nhớ giống như bất kỳ đối tượng nào khác.


0

Vì String là một lớp bất biến trong java.

Bây giờ tại sao nó là bất biến? Vì Chuỗi là bất biến nên nó có thể được chia sẻ giữa nhiều luồng và chúng ta không cần đồng bộ hóa hoạt động của Chuỗi ra bên ngoài. As String cũng được sử dụng trong cơ chế tải lớp. Vì vậy, nếu Chuỗi có thể thay đổi thì java.io.writer có thể được đổi thành abc.xyz.mywriter


0
TString obj1 = new TString("Jan Peter");                
TString obj2 = new TString("Jan Peter");                    

if (obj1.Name == obj2.Name)                 
    System.out.println("True");
else                
    System.out.println("False");

Đầu ra:

Thật

Tôi đã tạo hai đối tượng riêng biệt, cả hai đều có trường (tham chiếu) 'Tên'. Vì vậy, ngay cả trong trường hợp này, "Jan Peter" được chia sẻ, nếu tôi hiểu cách java giao dịch ..


0

Vâng, StringPool được triển khai bằng The Hashmap trong java. Nếu chúng ta luôn tạo với một từ khóa mới, nó không tìm kiếm trong String Pool và tạo một bộ nhớ mới cho nó, có thể cần sau này nếu chúng ta đang chạy thao tác tốn nhiều bộ nhớ và nếu chúng ta đang tạo tất cả các chuỗi với từ khóa mới sẽ ảnh hưởng đến hiệu suất ứng dụng của chúng tôi. Vì vậy, không nên sử dụng các từ khóa mới để tạo chuỗi vì khi đó chỉ nó mới đi đến Nhóm chuỗi, đến lượt nó là một Hashmap, (bộ nhớ được lưu, hãy tưởng tượng nếu chúng ta có nhiều chuỗi được tạo bằng từ khóa mới) ở đây nó sẽ được lưu trữ và nếu chuỗi đã tồn tại thì tham chiếu của nó (thường nằm trong bộ nhớ Stack) sẽ được trả về chuỗi mới được tạo. Vì vậy, nó đã được thực hiện để cải thiện hiệu suất.

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.