ต้นฉบับ: สร้างสตริง Java โดยใช้ “ ” หรือตัวสร้าง? โดย X Wang
ใน Java สตริงสามารถสร้างขึ้นได้โดยใช้สองวิธี:
อย่างไรก็ตาม แผนภาพนี้ไม่ถูกต้องทั้งหมด มันไม่ได้แสดงให้เห็นว่าเกิดอะไรขึ้นในฮีป สิ่งที่เกิดขึ้นจริงเมื่อมีการเรียก
โค้ดด้านล่างนี้เรียบง่ายและมีเพียงข้อมูลพื้นฐานที่แสดงถึงปัญหาเท่านั้น
String x = "abc";
String y = new String("abc");
อะไรคือความแตกต่างระหว่างการใช้เครื่องหมายคำพูดคู่และการใช้ตัวสร้าง?
1. ราคาคู่เทียบกับ ตัวสร้าง
คำถามนี้สามารถตอบได้โดยดูตัวอย่างง่ายๆ สองตัวอย่าง ตัวอย่างที่ 1:String a = "abcd";
String b = "abcd";
System.out.println(a == b); // True
System.out.println(a.equals(b)); // True
a==b
จริง เนื่องจากa
ทั้งคู่b
อ้างถึงออบเจ็กต์เดียวกัน - สตริงที่ประกาศเป็นตัวอักษร (สตริงตามตัวอักษรด้านล่าง) ในพื้นที่วิธีการ (เราอ้างอิงผู้อ่านถึงแหล่งที่มาในทรัพยากรของเรา: ไดอะแกรม8 อันดับแรกสำหรับการทำความเข้าใจ Javaไดอะแกรม 8) เมื่อสตริงลิเทอรัลเดียวกันถูกสร้างขึ้นมากกว่าหนึ่งครั้ง จะมีเพียงสำเนาเดียวของสตริงเท่านั้นที่ถูกเก็บไว้ในหน่วยความจำ เพียงอินสแตนซ์เดียวเท่านั้น (ในกรณีของเรา "abcd") สิ่งนี้เรียกว่า "การฝึกงานสตริง" ค่าคงที่สตริงทั้งหมดที่ประมวลผล ณ เวลาคอมไพล์จะถูกรวมไว้ใน Java โดยอัตโนมัติ ตัวอย่างที่ 2:
String c = new String("abcd");
String d = new String("abcd");
System.out.println(c == d); // False
System.out.println(c.equals(d)); // True
c==d
เท็จเนื่องจากc
อ้างd
ถึงวัตถุสองวัตถุที่แตกต่างกันในหน่วยความจำ (บนฮีป) อ็อบเจ็กต์ที่ต่างกันจะมีการอ้างอิงที่แตกต่างกันเสมอ แผนภาพนี้แสดงให้เห็นสองสถานการณ์ที่อธิบายไว้ข้างต้น: 
2. สตริงการฝึกงานในขั้นตอนการทำงานของโปรแกรม
ผู้เขียนขอบคุณ LukasEder (ความคิดเห็นด้านล่างเป็นของเขา): การฝึกงานสตริงสามารถเกิดขึ้นได้ในระหว่างการรันโปรแกรม แม้ว่าจะสร้างสองสตริงโดยใช้คอนสตรัคเตอร์ก็ตาม:String c = new String("abcd").intern();
String d = new String("abcd").intern();
System.out.println(c == d); // Now true
System.out.println(c.equals(d)); // True
3. เมื่อใดควรใช้เครื่องหมายคำพูดคู่ และเมื่อใดควรใช้ตัวสร้าง
เนื่องจากความจริงที่ว่า "abcd" ตามตัวอักษรนั้นเป็นประเภท String เสมอ การใช้ Constructor จะสร้างอ็อบเจ็กต์ที่ไม่จำเป็นเพิ่มเติม ดังนั้นควรใช้เครื่องหมายคำพูดคู่หากคุณต้องการสร้างสตริง หากคุณต้องการสร้างวัตถุใหม่บนฮีปจริงๆ คุณควรใช้ตัวสร้าง กรณีการใช้งานแสดงไว้ที่นี่ (ต้นฉบับ) (ฉันให้ข้อความที่แปลไว้ด้านล่าง แต่ฉันยังคงขอแนะนำอย่างยิ่งให้คุณทำความคุ้นเคยกับโค้ดของผู้แสดงความเห็นที่ลิงก์นี้)วิธีการ substring() ใน JDK 6 และ JDK 7
วิธีการสตริงย่อย () ใน JDK 6 และ JDK 7โดย X Wang วิธีการsubstring(int beginIndex, int endIndex)
ใน JDK 6 และ JDK 7 นั้นแตกต่างกัน การทราบความแตกต่างเหล่านี้จะช่วยให้คุณใช้วิธีนี้ได้ดีขึ้น เพื่อความสะดวกในการอ่าน ด้านล่างนี้substring()
เราจะหมายถึงไวยากรณ์แบบเต็ม เช่น substring(int beginIndex, int endIndex)
.
1. substring() ทำหน้าที่อะไร?
วิธีการsubstring(int beginIndex, int endIndex)
ส่งคืนสตริงที่ขึ้นต้นด้วยหมายเลขอักขระ และลงท้ายด้วย หมายเลข beginIndex
อักขระendIndex-1
String x = "abcdef";
x = x.substring(1,3);
System.out.println(x);
เอาท์พุท:
bc
2. จะเกิดอะไรขึ้นเมื่อมีการเรียก substring()?
คุณอาจรู้ว่า เนื่องจากความไม่เปลี่ยนรูปx
เมื่อกำหนด x ผลลัพธ์ของx.substring(1,3)
มันx
จะชี้ไปที่แถวใหม่ทั้งหมด (ดูแผนภาพ): 
substring()
จะแตกต่างกันใน JDK 6 และ JDK 7
3. สตริงย่อย () ใน JDK 6
ประเภทสตริงได้รับการสนับสนุนตามประเภทchar
อาร์เรย์ ใน JDK 6 คลาสString
ประกอบด้วย 3 ฟิลด์: char value[]
, int offset
, int count
. ใช้เพื่อจัดเก็บอาร์เรย์จริงของอักขระ ดัชนีของอักขระตัวแรกในอาร์เรย์ จำนวนอักขระในบรรทัด เมื่อเรียกใช้เมธอดsubstring()
มันจะสร้างแถวใหม่ แต่ค่าของตัวแปรยังคงชี้ไปที่อาร์เรย์เดียวกันบนฮีป ความแตกต่างระหว่างสองสายคือจำนวนอักขระและค่าดัชนีของอักขระเริ่มต้นในอาร์เรย์ 
//JDK 6
String(int offset, int count, char value[]) {
this.value = value;
this.offset = offset;
this.count = count;
}
public String substring(int beginIndex, int endIndex) {
//check boundary
return new String(offset + beginIndex, endIndex - beginIndex, value);
}
4. ปัญหาที่เกิดจาก substring() ใน JDK 6
หากคุณมีสายอักขระที่ยาวมาก แต่คุณต้องการเพียงส่วนเล็กๆ เท่านั้น ซึ่งคุณจะได้ทุกครั้งที่ใช้substring()
. ซึ่งจะทำให้เกิดปัญหาในการดำเนินการ เนื่องจากคุณต้องการเพียงส่วนเล็กๆ เท่านั้น แต่คุณยังคงต้องจัดเก็บสตริงทั้งหมด สำหรับ JDK 6 วิธีแก้ไขคือโค้ดด้านล่าง ซึ่งจะแปลงสตริงเป็นสตริงย่อยจริง:
x = x.substring(x, y) + ""
ผู้ใช้STepeRกำหนดคำถาม (ดูความคิดเห็นของเขา) และดูเหมือนว่าจำเป็นต้องเพิ่มจุดที่ 4 "ปัญหาที่เกิดขึ้นsubstring()
ใน JDK 6" เป็นตัวอย่างที่ครอบคลุมมากขึ้น ฉันหวังว่านี่จะเป็นคำตอบและจะช่วยให้ผู้อื่นทราบได้อย่างรวดเร็วว่าปัญหาคืออะไร นี่คือรหัส:
String a = "aLongLongString";
String b = a.substring(1, 2);
String c = a.substring(2, 6);
ดังนั้นใน JDK 7 b
อ อบเจ็กต์ с
ประเภท a ที่สร้างขึ้นโดยการ เรียกString
ใช้เมธอดsubstring()
บนออบเจ็กต์ประเภท a String
จะอ้างอิงอาร์เรย์ที่สร้างขึ้นใหม่สองตัวในฮีป - L
สำหรับb
, ongL
สำหรับ c
อาร์เรย์ใหม่ทั้งสองนี้จะถูกเก็บไว้ในฮีปพร้อมกับอาร์เรย์ดั้งเดิมaLongLongString
ที่อ้างอิงโดย a เหล่านั้น. อาเรย์ดั้งเดิมไม่ได้หายไปไหน ตอนนี้กลับมาที่ JDK 6 กัน ใน JDK 6 ฮีปจะมีอาร์เรย์aLongLongString
เดียว หลังจากรันโค้ดบรรทัดแล้ว
String b = a.substring(1, 2);
String c = a.substring(2, 6);
วัตถุb
อ้างc
ถึงอาร์เรย์เดียวกันในฮีปซึ่งสอดคล้องกับวัตถุa
: b
- ถึงองค์ประกอบจากดัชนีที่ 1 ถึงดัชนีที่ 2 c
- ถึงองค์ประกอบจากดัชนีที่ 2 ถึงอันดับที่ 6 (การกำหนดหมายเลขเริ่มจาก 0 การแจ้งเตือน) เหล่านั้น. แน่นอนว่าการเข้าถึงตัวแปรหรือ c ใน JDK 6 เพิ่มเติมb
จะส่งผลให้องค์ประกอบที่ต้องการของอาร์เรย์ดั้งเดิมถูก "นับ" ลงในฮีป ใน JDK 7 การเข้าถึงตัวแปรหรือ c เพิ่มเติมb
จะทำให้เกิดการเข้าถึงอาร์เรย์ขนาดเล็กที่จำเป็นซึ่งได้ถูกสร้างขึ้นแล้วและ "ใช้งานจริง" ในฮีป เหล่านั้น. เห็นได้ชัดว่า JDK 7 ใช้หน่วยความจำมากขึ้นในสถานการณ์เช่นนี้ แต่ลองจินตนาการถึงตัวเลือกที่เป็นไปได้: สตริงย่อยบางตัวของตัวแปรถูกกำหนดให้กับตัวแปรb
และในอนาคตทุกคนจะใช้เฉพาะพวกมันเท่านั้น - อ็อบเจ็กต์และ. ไม่มีใครเข้าถึงตัวแปร a อีกต่อไป ไม่มีการอ้างอิงถึงมัน (นี่คือความหมายของผู้เขียนบทความ) เป็นผลให้ ณ จุดหนึ่งตัวรวบรวมขยะถูกกระตุ้นและ (ในรูปแบบทั่วไปที่สุดแน่นอน) เราจะได้รับ 2 สถานการณ์ที่แตกต่างกัน: JDK 6 : วัตถุถูกทำลาย (เก็บขยะ) แต่ - อาเรย์ขนาดใหญ่ดั้งเดิม ในกองนั้นยังมีชีวิตอยู่ มีการใช้อย่างต่อเนื่อง และ JDK 7:วัตถุ a ถูกทำลายพร้อมกับอาร์เรย์ดั้งเดิมในฮีป นี่คือประเด็นใน JDK 6 ที่อาจทำให้หน่วยความจำรั่ว c
a
b
c
a
b
c
5. สตริงย่อย () ใน JDK 7
วิธีการได้รับการปรับปรุงใน JDK 7 ใน JDK 7substring()
มันสร้างอาร์เรย์ใหม่บนฮีปจริงๆ 
//JDK 7
public String(char value[], int offset, int count) {
//check boundary
this.value = Arrays.copyOfRange(value, offset, offset + count);
}
public String substring(int beginIndex, int endIndex) {
//check boundary
int subLen = endIndex - beginIndex;
return new String(value, beginIndex, subLen);
}
GO TO FULL VERSION