สตริงเป็นวัตถุใน Java ดังนั้นทำไมเราไม่ใช้ 'ใหม่' ในการสร้าง


105

โดยปกติเราจะสร้างวัตถุโดยใช้newคำสำคัญเช่น:

Object obj = new Object();

สตริงเป็นวัตถุ แต่เราไม่ได้ใช้newในการสร้าง:

String str = "Hello World";

ทำไมถึงเป็นแบบนี้? ฉันสามารถสร้าง String ได้newหรือไม่?



1
เนื่องจากตัวอักษรสตริงเป็นวัตถุอยู่แล้ว
Marquis of Lorne

1
โปรดทราบว่าnew String(...)มีการใช้เพื่อหลีกเลี่ยงรายละเอียดการนำไปใช้งานเมื่อสตริงย่อยขนาดใหญ่ สิ่งนี้ได้รับการแก้ไขใน Java 7 และไม่จำเป็นอีกต่อไป
Thorbjørn Ravn Andersen

ฉันชอบคนที่ 100 ของโพสต์นี้ :)
มูกิต 09

คำตอบ:


130

นอกเหนือจากสิ่งที่ได้กล่าวไปแล้ว String literals [เช่น Strings เหมือน"abcd"แต่ไม่เหมือนnew String("abcd")] ใน Java ยังถูก จำกัด ไว้ซึ่งหมายความว่าทุกครั้งที่คุณอ้างถึง "abcd" คุณจะได้รับการอ้างอิงไปยังStringอินสแตนซ์เดียวแทนที่จะเป็นอินสแตนซ์ใหม่ แต่ละครั้ง. ดังนั้นคุณจะมี:

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

a == b; //True

แต่ถ้าคุณมี

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

ก็เป็นไปได้ที่จะมี

a == b; // False

(และในกรณีที่ใครก็ตามต้องการการเตือนให้ใช้.equals()เพื่อเปรียบเทียบสตริงเสมอ==การทดสอบความเท่าเทียมกันทางกายภาพ)

Interning String literals เป็นสิ่งที่ดีเพราะมักใช้มากกว่าหนึ่งครั้ง ตัวอย่างเช่นพิจารณารหัส (ที่สร้างขึ้น):

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

หากเราไม่มีการสร้างสตริงภายใน "การทำซ้ำครั้งต่อไป" จะต้องสร้างอินสแตนซ์ 10 ครั้งในขณะที่ตอนนี้จะสร้างอินสแตนซ์เพียงครั้งเดียว


1
การใช้ String a = new String ("abcd") หมายความว่ามีสตริงสองรายการที่มีเนื้อหาคล้ายกันอยู่ในหน่วยความจำหรือไม่
เปลี่ยนแปลง

1
ถูกต้อง - คอมไพเลอร์ไม่จำเป็นต้องตรวจสอบเพื่อดูว่าสตริงดังกล่าวได้ถูกผูกไว้แล้วหรือไม่ (แม้ว่าคุณจะสามารถเขียนได้)
danben

ใช่การเพิ่มประสิทธิภาพนี้เป็นไปได้เนื่องจากสตริงไม่เปลี่ยนรูปจึงสามารถแชร์ได้โดยไม่มีปัญหา การจัดการ "asdf" ที่ใช้ร่วมกันเป็นการใช้รูปแบบการออกแบบ "ฟลายเวท"
manuel aldana

ไม่มีใครบอกว่าเป็นไปไม่ได้เพียงแต่ว่าไม่รับประกัน นั่นคือการลงคะแนนของคุณหรือไม่?
danben

"== การทดสอบความเท่าเทียมกันของวัตถุ" หมายความว่าอย่างไร สิ่งนี้ดูเหมือนจะไม่เป็นความจริงสำหรับฉัน แต่บางทีคุณอาจหมายถึงสิ่งที่แตกต่างจากที่ดูเหมือนจะหมาย
Dawood ibn Kareem

32

สตริงคืออ็อบเจ็กต์ "พิเศษ" ใน Java นักออกแบบ Java ตัดสินใจอย่างชาญฉลาดว่า Strings ถูกใช้บ่อยจนต้องใช้ไวยากรณ์ของตัวเองรวมถึงกลยุทธ์การแคช เมื่อคุณประกาศสตริงโดยพูดว่า:

String myString = "something";

myString คือการอ้างอิงถึงออบเจ็กต์ String ที่มีค่าเป็น "something" หากคุณประกาศในภายหลัง:

String myOtherString = "something";

Java ฉลาดพอที่จะหาว่า myString และ myOtherString เหมือนกันและจะจัดเก็บไว้ในตารางสตริงส่วนกลางเป็นวัตถุเดียวกัน มันขึ้นอยู่กับข้อเท็จจริงที่ว่าคุณไม่สามารถแก้ไข Strings เพื่อทำสิ่งนี้ได้ ซึ่งจะช่วยลดจำนวนหน่วยความจำที่ต้องการและสามารถเปรียบเทียบได้เร็วขึ้น

ถ้าคุณเขียนแทน

String myOtherString = new String("something");

Java จะสร้างออบเจ็กต์ใหม่สำหรับคุณซึ่งแตกต่างจากอ็อบเจ็กต์ myString


เฮ้ ... มันไม่ต้องการ "ภูมิปัญญาที่ไม่มีที่สิ้นสุด" ในการตระหนักถึงความจำเป็นในการรองรับวากยสัมพันธ์สำหรับตัวอักษรสตริง การออกแบบภาษาการเขียนโปรแกรมที่จริงจังอื่น ๆ เกือบทั้งหมดรองรับสตริงลิเทอรัลบางประเภท
Stephen C

12
Hyperbole ถูกลดความมึนงงกัปตัน :)
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").

หวังว่านี่จะคลายข้อสงสัยบางประการ :)


สตริง d = สตริงใหม่ ("def"); // 1 Object + "def" ถูกเพิ่มลงในพูล -> ที่นี่ "def" จะถูกเพิ่มลงในพูลก็ต่อเมื่อยังไม่มี
southerton

@southerton ไม่มีความหมาย มันอยู่ในสระว่ายน้ำแล้ว คอมไพเลอร์วางไว้ที่นั่น
Marquis of Lorne

@EJP ทำไม (e == d) เป็นเท็จที่นี่? ทั้งคู่กำลังอ้างถึงวัตถุเดียวกัน "def" ในพูลใช่ไหม
ราชา

สตริง c = สตริงใหม่ ("abc"); // 1 Object ... คำสั่งนี้ถูกต้องหรือไม่? ถ้า "abc" อ้างอิงจากพูลค่าคงที่แล้วจะใช้ inter method อย่างไร?
Prashanth Debbadwar

6

มันเป็นทางลัด เดิมมันไม่ได้เป็นแบบนั้น แต่ Java เปลี่ยนมัน

คำถามที่พบบ่อยนี้พูดถึงสั้น ๆ คู่มือข้อมูลจำเพาะ Java พูดถึงเรื่องนี้ด้วย แต่ฉันไม่สามารถหาได้ทั่วไป


2
ลิงค์เสียและฉันไม่ทราบถึงหลักฐานอื่นใดที่แสดงว่ามันเคยเปลี่ยนแปลง
Marquis of Lorne

1
@EJP มันยังอยู่ในเครื่องย้อนกลับถ้ามันมีประโยชน์
Arjan

6

String ขึ้นอยู่กับการเพิ่มประสิทธิภาพสองสามอย่าง (เพื่อต้องการวลีที่ดีกว่า) โปรดทราบว่า String ยังมีตัวดำเนินการมากเกินไป (สำหรับตัวดำเนินการ +) ซึ่งแตกต่างจากวัตถุอื่น ๆ ดังนั้นจึงเป็นกรณีพิเศษมาก


1
จริงๆแล้ว + เป็นโอเปอเรเตอร์ที่ถูกแปลเป็นการเรียก StringBuilder.append (.. )
วิสกี้เซียร์รา

5

โดยปกติเราจะใช้ String literals เพื่อหลีกเลี่ยงการสร้างวัตถุที่ไม่จำเป็น หากเราใช้โอเปอเรเตอร์ใหม่ในการสร้างอ็อบเจกต์ String มันจะสร้างอ็อบเจกต์ใหม่ทุกครั้ง

ตัวอย่าง:

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

สำหรับรหัสด้านบนในหน่วยความจำ:

ใส่คำอธิบายภาพที่นี่


2

ใน Java Strings เป็นกรณีพิเศษโดยมีกฎมากมายที่ใช้กับ Strings เท่านั้น เครื่องหมายคำพูดคู่ทำให้คอมไพเลอร์สร้างวัตถุสตริง เนื่องจากอ็อบเจ็กต์ String ไม่เปลี่ยนรูปจึงอนุญาตให้คอมไพเลอร์ภายในหลาย ๆ สตริงและสร้างพูลสตริงที่ใหญ่ขึ้น ค่าคงที่ของสตริงที่เหมือนกันสองค่าจะมีการอ้างอิงอ็อบเจ็กต์เดียวกันเสมอ หากคุณไม่ต้องการให้เป็นกรณีนี้คุณสามารถใช้ String ใหม่ ("") และจะสร้างวัตถุ String ในขณะรันไทม์ วิธีการ intern () ใช้เป็นเรื่องธรรมดาเพื่อทำให้สตริงที่สร้างแบบไดนามิกถูกตรวจสอบกับตารางการค้นหาสตริง เมื่อสตริงอยู่ภายในการอ้างอิงอ็อบเจ็กต์จะชี้ไปที่อินสแตนซ์สตริงที่บัญญัติ

    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

เมื่อ classloader โหลดคลาสค่าคงที่สตริงทั้งหมดจะถูกเพิ่มลงในพูล String


"เครื่องหมายคำพูดคู่ทำให้คอมไพเลอร์สร้างวัตถุ String" ความคิดเห็นต่ำกว่ามูลค่า
csguy

0

น้ำตาลสังเคราะห์

String s = new String("ABC");

ไวยากรณ์ยังคงมีอยู่


1
นี่ไม่ถูกต้องทีเดียว s = new String ("ABC") จะไม่ให้ผลลัพธ์เหมือนกับ s = "ABC" ดูความคิดเห็นของ danben
Steve B.

2
นอกจากนี้ค่อนข้างแดกดันมันจะสร้างอินสแตนซ์สตริงที่แสดง "ABC" แบบอินไลน์ก่อนแล้วจึงส่งต่อสิ่งนั้นเป็นอาร์กิวเมนต์ไปยังการเรียกตัวสร้างซึ่งจะสร้างสตริงที่มีค่าเหมือนกันกลับมา
Andrzej Doyle

1
กรณีการใช้งานที่ถูกต้องสำหรับตัวสร้างนี้คือString small = new String(huge.substring(int, int));ซึ่งช่วยให้คุณสามารถรีไซเคิลพื้นฐานขนาดใหญ่char[]จากhugeสตริงดั้งเดิมได้
Pascal Thivent

1
@PascalThivent ใช่ แต่ไม่ใช่อีกต่อไปกับ Java 8 โดยจะไม่แชร์อาร์เรย์อีกต่อไป (เพื่อเตรียมการเพิ่มประสิทธิภาพอื่น ๆ เช่นการขจัดข้อมูลซ้ำซ้อนสตริงอัตโนมัติด้วย G1 หรือการบีบอัดสตริงที่จะเกิดขึ้น)
eckes

@AndrzejDoyle ไม่ถูกต้อง คอมไพเลอร์สร้างอ็อบเจ็กต์สำหรับลิเทอรัล
Marquis of Lorne

0

คุณยังคงสามารถใช้งานnew String("string")ได้ แต่การสร้างสตริงใหม่โดยไม่มีตัวอักษรสตริงจะยากกว่า ... คุณจะต้องใช้อาร์เรย์อักขระหรือไบต์ :-) สตริงลิเทอรัลมีคุณสมบัติเพิ่มเติมหนึ่งคุณสมบัติ: ลิเทอรัลสตริงเดียวกันทั้งหมดจากคลาสชี้ไปยังสตริงเดียวกัน อินสแตนซ์ (พวกเขาถูกคุมขัง)


0

แทบไม่จำเป็นต้องสร้างสตริงใหม่เนื่องจากลิเทอรัล (อักขระในเครื่องหมายอัญประกาศ) เป็นอ็อบเจ็กต์ String ที่สร้างขึ้นเมื่อโหลดคลาสโฮสต์ มันถูกกฎหมายอย่างสมบูรณ์ที่จะเรียกใช้วิธีการบนตัวอักษรและดอนความแตกต่างหลักคือความสะดวกที่มีให้โดยตัวอักษร มันจะเป็นความเจ็บปวดครั้งใหญ่และสิ้นเปลืองเวลาถ้าเราต้องสร้างอาร์เรย์ของตัวอักษรและเติมถ่านด้วยอักขระและพวกเขาก็สร้างสตริงใหม่ (อาร์เรย์ถ่าน)


0

อย่าลังเลที่จะสร้างสตริงใหม่ด้วย

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

สัญกรณ์ปกติs = "new String";จะมากหรือน้อยทางลัดสะดวก - ซึ่งควรจะใช้สำหรับเหตุผลประสิทธิภาพยกเว้นสำหรับผู้ที่กรณีที่หายากสวยที่คุณจริงๆต้องสตริงที่มีคุณสมบัติสำหรับสมการ

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

แก้ไข

ในการตอบกลับความคิดเห็นนี้ไม่ได้มีไว้เพื่อให้คำแนะนำ แต่เป็นเพียงการตอบสนองโดยตรงต่อวิทยานิพนธ์ของผู้ถามเท่านั้นที่เราไม่ได้ใช้คีย์เวิร์ด 'new'สำหรับ Strings ซึ่งไม่เป็นความจริง หวังว่าการแก้ไขนี้ (รวมถึงด้านบน) จะทำให้กระจ่างขึ้นเล็กน้อย BTW - มีคำตอบที่ดีและดีกว่ามากสำหรับคำถามข้างต้นใน SO


4
-1 - คำแนะนำที่ไม่ดี คุณไม่ควร "รู้สึกอิสระ" ในการใช้งานnew String(...)เว้นแต่แอปพลิเคชันของคุณต้องการให้คุณสร้างสตริงที่มีเอกลักษณ์เฉพาะ
Stephen C

1
ฉันรู้แล้ว. แก้ไขโพสต์เพื่อชี้แจง
Andreas Dolk

0

สระว่ายน้ำที่แท้จริงมีสายใด ๆ newที่ถูกสร้างขึ้นโดยไม่ต้องใช้คำว่า

มีความแตกต่าง: String ที่ไม่มีการอ้างอิงใหม่จะถูกเก็บไว้ใน String literal pool และ String โดย new บอกว่าอยู่ในหน่วยความจำฮีป

สตริงใหม่อยู่ที่อื่นในหน่วยความจำเช่นเดียวกับวัตถุอื่น ๆ


0

เนื่องจาก String เป็นคลาสที่ไม่เปลี่ยนรูปใน java

ทำไมมันไม่เปลี่ยนรูป? เนื่องจาก String ไม่เปลี่ยนรูปจึงสามารถใช้ร่วมกันระหว่างหลายเธรดและเราไม่จำเป็นต้องซิงโครไนซ์การทำงานของ String จากภายนอก เนื่องจาก String ยังใช้ในกลไกการโหลดคลาส ดังนั้นถ้า String ไม่แน่นอน java.io.writer อาจถูกเปลี่ยนเป็น 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");

เอาท์พุต:

จริง

ฉันสร้างวัตถุสองชิ้นแยกกันทั้งสองมีฟิลด์ (อ้างอิง) 'ชื่อ' ดังนั้นแม้ในกรณีนี้จะมีการแชร์ "Jan Peter" แต่ถ้าฉันเข้าใจวิธีการจัดการ java ..


0

StringPool ถูกนำไปใช้โดยใช้ Hashmap ใน java หากเราสร้างคำหลักใหม่เสมอโดยจะไม่ค้นหาใน String Pool และสร้างหน่วยความจำใหม่ซึ่งอาจจำเป็นในภายหลังหากเรามีการดำเนินการที่ใช้หน่วยความจำมากและหากเรากำลังสร้างสตริงทั้งหมดด้วยคำหลักใหม่ที่จะส่งผลต่อประสิทธิภาพ ของแอปพลิเคชันของเรา ดังนั้นจึงไม่แนะนำให้ใช้คีย์เวิร์ดใหม่ในการสร้างสตริงเพราะจะไปที่ String pool ซึ่งจะเป็น Hashmap (บันทึกหน่วยความจำลองนึกดูว่าเรามีสตริงจำนวนมากที่สร้างด้วยคีย์เวิร์ดใหม่) ที่นี่จะถูกเก็บไว้และ หากสตริงมีการอ้างอิงอยู่แล้ว (ซึ่งโดยปกติจะอยู่ในหน่วยความจำ Stack) จะถูกส่งกลับไปยังสตริงที่สร้างขึ้นใหม่ ดังนั้นจึงทำเพื่อปรับปรุงประสิทธิภาพ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.