คลาส String จะแทนที่โอเปอเรเตอร์ + อย่างไร


129

ทำไมใน Java คุณสามารถเพิ่ม Strings ด้วยตัวดำเนินการ + เมื่อ String เป็นคลาส? ในString.javaรหัสฉันไม่พบการใช้งานสำหรับผู้ประกอบการนี้ แนวคิดนี้ละเมิดการวางวัตถุหรือไม่?


21
เครื่องหมายบวก ( +) เป็นคุณสมบัติภาษาของ Java
adatapost

18
มันเป็นความมหัศจรรย์ของคอมไพเลอร์ คุณไม่สามารถดำเนินการเกินกำลังใน Java
Lion

18
ฉันคิดว่าสิ่งที่แปลกคือ String ถูกนำไปใช้เป็นห้องสมุดนอกภาษา (ใน java.lang.String) แต่มันก็มีการสนับสนุนเฉพาะในภาษา มันไม่ถูกต้อง
13ren


@ 13 คุณผิด String นั้นแตกต่างจากคลาส java.lang ซึ่งย่อมาจากภาษา - java lanaguage !!! คลาสทั้งหมดในแพ็คเกจนี้เป็นพิเศษ สังเกตว่าคุณไม่จำเป็นต้องนำเข้า java.lang เพื่อใช้งาน

คำตอบ:


156

ลองดูนิพจน์ง่าย ๆ ต่อไปนี้ใน Java

int x=15;
String temp="x = "+x;

คอมไพเลอร์แปลง"x = "+x;เป็นStringBuilderภายในและใช้.append(int)เพื่อ "เพิ่ม" จำนวนเต็มสตริง

5.1.11 การแปลงสตริง

ประเภทใด ๆ อาจถูกแปลงเป็นประเภท String โดยการแปลงสตริง

ค่า x ของชนิดดั้งเดิม T ถูกแปลงเป็นค่าอ้างอิงก่อนโดยให้เป็นอาร์กิวเมนต์สำหรับนิพจน์การสร้างอินสแตนซ์คลาสที่เหมาะสม (§15.9):

  • ถ้า T เป็นบูลีนให้ใช้บูลีนใหม่ (x)
  • ถ้า T เป็นตัวอักษรให้ใช้ตัวละครใหม่ (x)
  • ถ้า T เป็นไบต์สั้นหรือ int ให้ใช้จำนวนเต็มใหม่ (x)
  • ถ้า T ยาวให้ใช้ Long (x) ใหม่
  • ถ้า T คือ float ให้ใช้ Float ใหม่ (x)
  • หาก T เป็นสองเท่าให้ใช้ Double ใหม่ (x)

ค่าอ้างอิงนี้จะถูกแปลงเป็นประเภท String โดยการแปลงสตริง

ตอนนี้ต้องพิจารณาเฉพาะค่าอ้างอิงเท่านั้น:

  • หากการอ้างอิงเป็นโมฆะมันจะถูกแปลงเป็นสตริง "null" (อักขระ ASCII สี่ตัว n, u, l, l)
  • มิฉะนั้นการแปลงจะดำเนินการราวกับว่าโดยการเรียกใช้วิธีการ toString ของวัตถุที่อ้างอิงโดยไม่มีข้อโต้แย้ง; แต่ถ้าผลลัพธ์ของการเรียกใช้เมธอด toString เป็นโมฆะจะใช้สตริง "null" แทน

วิธีการ toString ถูกกำหนดโดยวัตถุคลาสดั้งเดิม (§4.3.2) หลายคลาสจะแทนที่มันโดยเฉพาะ Boolean, Character, Integer, Long, Float, Double และ String

ดู§5.4สำหรับรายละเอียดของบริบทการแปลงสตริง

15.18.1

การปรับให้เหมาะสมของสตริงการต่อข้อมูล: การดำเนินการอาจเลือกที่จะทำการแปลงและการต่อข้อมูลในขั้นตอนเดียวเพื่อหลีกเลี่ยงการสร้างแล้วละทิ้งวัตถุสตริงกลาง เพื่อเพิ่มประสิทธิภาพของการต่อสตริงซ้ำซ้ำคอมไพเลอร์ Java อาจใช้คลาส StringBuffer หรือเทคนิคที่คล้ายกันเพื่อลดจำนวนของวัตถุสตริงกลางที่สร้างขึ้นโดยการประเมินผลของการแสดงออก

สำหรับชนิดดั้งเดิมการใช้งานอาจปรับการสร้างวัตถุห่อหุ้มให้เหมาะสมโดยการแปลงจากชนิดดั้งเดิมเป็นสตริงโดยตรง

เวอร์ชันที่ปรับปรุงแล้วจะไม่ทำการแปลงสตริงแบบเต็มก่อน

นี่เป็นภาพประกอบที่ดีของเวอร์ชันที่ปรับให้เหมาะสมซึ่งคอมไพเลอร์ใช้ถึงแม้ว่าจะไม่มีการแปลงค่าดั้งเดิมซึ่งคุณสามารถเห็นคอมไพเลอร์เปลี่ยนสิ่งต่าง ๆ ให้เป็น StringBuilder ในพื้นหลัง:

http://caprazzi.net/posts/java-bytecode-string-concatenation-and-stringbuilder/


รหัส java นี้:

public static void main(String[] args) {
    String cip = "cip";
    String ciop = "ciop";
    String plus = cip + ciop;
    String build = new StringBuilder(cip).append(ciop).toString();
}

สร้างสิ่งนี้ - ดูว่ารูปแบบการต่อข้อมูลสองแบบนำไปสู่โหมดไบต์เดียวกันมากอย่างไร:

 L0
    LINENUMBER 23 L0
    LDC "cip"
    ASTORE 1
   L1
    LINENUMBER 24 L1
    LDC "ciop"
    ASTORE 2

   // cip + ciop

   L2
    LINENUMBER 25 L2

    NEW java/lang/StringBuilder
    DUP
    ALOAD 1
    INVOKESTATIC java/lang/String.valueOf(Ljava/lang/Object;)Ljava/lang/String;
    INVOKESPECIAL java/lang/StringBuilder.<init>(Ljava/lang/String;)V
    ALOAD 2
    INVOKEVIRTUAL java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString()Ljava/lang/String;

    ASTORE 3

    // new StringBuilder(cip).append(ciop).toString()

   L3
    LINENUMBER 26 L3

    NEW java/lang/StringBuilder
    DUP
    ALOAD 1
    INVOKESPECIAL java/lang/StringBuilder.<init>(Ljava/lang/String;)V
    ALOAD 2
    INVOKEVIRTUAL java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString()Ljava/lang/String;

    ASTORE 4
   L4
    LINENUMBER 27 L4
    RETURN

ดูตัวอย่างด้านบนและวิธีการสร้างรหัสไบต์ตามรหัสแหล่งที่มาในตัวอย่างที่กำหนดคุณจะสามารถสังเกตเห็นว่าคอมไพเลอร์ได้ทำการแปลงข้อความต่อไปนี้ภายใน

cip+ciop; 

เข้าไป

new StringBuilder(cip).append(ciop).toString();

กล่าวอีกนัยหนึ่งตัวดำเนินการ+ในการต่อสตริงเป็นชวเลขอย่างมีประสิทธิภาพสำหรับStringBuilderสำนวนverbose ที่มากขึ้น


3
ขอบคุณมากฉันไม่คุ้นเคยกับรหัส jvm แต่สร้างรหัสสำหรับ String plus = cip + ciop; และ String build = StringBuilder ใหม่ (cip). append (ciop) .toString (); เหมือนกัน และคำถามของฉันคือการดำเนินการนี้เป็นการละเมิดการวางวัตถุหรือไม่
Pooya

7
ไม่เลย ตัวดำเนินการโอเวอร์โหลด (เช่นใน C ++ และบางภาษา) มีข้อบกพร่องบางอย่างและผู้ออกแบบ Java รู้สึกว่ามันค่อนข้างสับสนและไม่สนใจ Java สำหรับฉันแล้วภาษาเชิงวัตถุต้องมีแนวคิดที่สำคัญเกี่ยวกับการสืบทอดพหุนิยมและการห่อหุ้มที่ Java มี
Lion

2
ใช่ แต่ฉันคิดว่าตัวดำเนินการนี้มีการใช้งานมากเกินไปสำหรับคลาส String
Pooya

3
ใช่การใช้งานมากเกินไปของผู้ปฏิบัติงานใน Java สำหรับการต่อข้อมูลประเภทสตริง แต่คุณไม่สามารถกำหนดโอเปอเรเตอร์ของคุณเอง (เช่นใน C ++, C # และภาษาอื่น ๆ )
Lion

5
@Poya: จริง ๆ แล้ว "int / int" กับ "int / float" นั้นมีการใช้งานตัวดำเนินการมากเกินไปอยู่แล้วดังนั้นแม้แต่ C ก็มีสิ่งนั้น สิ่งที่ C (และ Java) ไม่มีอยู่นั้นคือการใช้งานตัวดำเนินการมากเกินไป: สิ่งเดียวที่กำหนดวิธีการต่าง ๆ ที่ตัวดำเนินการสามารถใช้ (ทั้งใน C และ Java) คือนิยามภาษา (และความแตกต่างถูกนำมาใช้ใน คอมไพเลอร์) C ++ แตกต่างกันในการที่มันอนุญาตให้ผู้ใช้กำหนดผู้ปฏิบัติงานมากเกินไป (ซึ่งมักจะเรียกว่าเพียงแค่ "ผู้ประกอบการเกินพิกัด")
Joachim Sauer

27

มันเป็นคุณสมบัติของคอมไพเลอร์ Java ที่ตรวจสอบตัวถูกดำเนินการของ+ผู้ประกอบการ และตามตัวถูกดำเนินการมันสร้างรหัสไบต์:

  • สำหรับ String มันสร้างรหัสเพื่อสตริง concat
  • สำหรับเบอร์มันสร้างรหัสเพื่อเพิ่มตัวเลข

นี่คือสิ่งที่ Java spec บอก :

ตัวดำเนินการ + และ-เรียกว่าตัวดำเนินการเสริม การเพิ่มการแสดงออก: การเพิ่มการแสดงออกการบวกการแสดงออกและการคูณการแสดงออกการบวกการแสดงออก - การคูณการแสดงออก

ผู้ประกอบการสารเติมแต่งมีความสำคัญเดียวกันและมีความสัมพันธ์ทางด้านซ้าย syntactically (พวกเขากลุ่มจากซ้ายไปขวา) หากชนิดของตัวถูกดำเนินการของตัว+ดำเนินการคือStringการดำเนินการคือการต่อสายอักขระ

มิฉะนั้นชนิดของตัวถูกดำเนินการแต่ละตัวของตัวดำเนิน+การจะต้องเป็นชนิดที่สามารถแปลงได้ (§5.1.8) เป็นชนิดตัวเลขดั้งเดิมหรือเกิดข้อผิดพลาดในการคอมไพล์เวลา

ในทุกกรณีประเภทของตัวถูกดำเนินการของตัวดำเนินการไบนารี-แต่ละประเภทจะต้องเป็นประเภทที่สามารถแปลงได้ (§5.1.8) เป็นประเภทตัวเลขดั้งเดิมหรือเกิดข้อผิดพลาดในการรวบรวมเวลา


7
ราคาจากสเป็คไม่เกี่ยวข้องกับคำถามนี้เลย
Ernest Friedman-Hill

นี่คือการแยก "ถ้าชนิดของตัวถูกดำเนินการอย่างใดอย่างหนึ่งของ + ผู้ประกอบการเป็น String แล้วการดำเนินการคือการต่อสายสตริงมิฉะนั้นประเภทของตัวถูกดำเนินการของแต่ละตัวถูกดำเนินการของ + ผู้ประกอบการจะต้องเป็นประเภทที่แปลงได้ (§5.1.8 ) เป็นชนิดตัวเลขดั้งเดิมหรือเกิดข้อผิดพลาดในการคอมไพล์เวลา " คุณช่วยบอกฉันหน่อยได้ไหมว่าทำไมมันถึงไม่เกี่ยวข้อง
Ramesh PVK

7
ไม่ได้พูดอะไรเกี่ยวกับวิธีการใช้งานซึ่งเป็นคำถาม ฉันคิดว่าผู้โพสต์เข้าใจว่าคุณสมบัตินี้มีอยู่แล้ว
Ernest Friedman-Hill

14

String class จะแทนที่โอเปอเรเตอร์ + อย่างไร

มันไม่ได้ คอมไพเลอร์ทำมัน คอมไพเลอร์พูดอย่างหนักเกินตัวดำเนินการ + สำหรับตัวถูกดำเนินการสตริง


6

ก่อนอื่น (+) ถูกโหลดมากเกินไปจะไม่ถูกเขียนทับ

ภาษา Java จัดเตรียมการสนับสนุนพิเศษสำหรับตัวดำเนินการต่อสตริง (+) ซึ่งโอเวอร์โหลดสำหรับวัตถุ Java Strings

  1. ถ้าตัวถูกดำเนินการทางด้านซ้ายมือเป็นสตริงมันจะทำงานเป็นการต่อข้อมูล

  2. ถ้าตัวถูกดำเนินการทางด้านซ้ายมือเป็นจำนวนเต็มมันจะทำงานเป็นตัวดำเนินการเพิ่มเติม


3
(2) หากตัวถูกดำเนินการด้านซ้ายเป็นจำนวนเต็มมันจะถูก unboxed อัตโนมัติintและจากนั้นจะใช้กฎปกติของ Java
มาร์ควิสแห่ง Lorne

2
กฎทั้งสองที่ระบุไว้ด้านล่างข้อความอ้างอิงนั้นผิด: ฉันเชื่อว่าควรเป็น: สองภาษาพื้นฐาน (หรือคลาสที่ไม่สามารถบอกได้) = เพิ่มเติม อย่างน้อยหนึ่งสตริง = การต่อข้อมูล
mwfearnley

4

ภาษา Java จัดเตรียมการสนับสนุนพิเศษสำหรับตัวดำเนินการต่อสตริง (+) และสำหรับการแปลงวัตถุอื่นเป็นสตริง การต่อข้อมูลสตริงจะดำเนินการผ่านคลาสStringBuilder(หรือStringBuffer) และappendวิธีการ


4

ความหมายของ+โอเปอเรเตอร์เมื่อใช้กับStringถูกกำหนดโดยภาษาตามที่ทุกคนเขียนไว้แล้ว เนื่องจากคุณดูเหมือนจะไม่พบสิ่งที่น่าเชื่อถือเพียงพอให้พิจารณาสิ่งนี้:

Ints, floats และ doubles ทั้งหมดมีการแทนเลขฐานสองที่แตกต่างกัน, และดังนั้นการเพิ่มสอง ints เป็นการดำเนินการที่แตกต่างกัน, ในแง่ของการจัดการบิต, มากกว่าการเพิ่มสอง float: สำหรับ ints คุณสามารถเพิ่มทีละบิต, สำหรับการลอยคุณต้องจัดการกับ mantissas และ exponents แยกต่างหาก

ตามหลักการแล้ว "การเพิ่ม" ขึ้นอยู่กับลักษณะของวัตถุที่ถูก "เพิ่ม" Java กำหนดไว้สำหรับ Strings รวมถึง ints และ float (longs, doubles, ... )


3

+ผู้ประกอบการมักจะถูกแทนที่ด้วยStringBuilderที่รวบรวมเวลา ตรวจสอบคำตอบนี้สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับเรื่องนั้น


หากเป็นกรณีนี้มีเหตุผลใดที่ทำให้ StringBuilder มีไว้เพื่อประโยชน์สาธารณะ มีกรณีที่+ตัวดำเนินการไม่ได้ถูกแทนที่ด้วยStringBuilderหรือไม่?
Cemre

2
คำถามที่คุณต้องการถามคือ "เหตุใดผู้ประกอบการ + จึงมีอยู่เพื่อประโยชน์สาธารณะ?" เพราะนั่นเป็นสิ่งที่น่ารังเกียจที่นี่ สำหรับคำถามอื่นของคุณฉันไม่ทราบแน่ชัด แต่ฉันเดาว่าคงไม่มีกรณีเช่นนี้
ราศีพิจิก

คอมไพเลอร์อาจใช้ concat () แทนหากมีเพียงสององค์ประกอบ คอมไพเลอร์ล้มเหลวในการแทนที่ concat () ด้วย StringBuilder (หรือใช้หลาย StringBuilders และผนวกเข้าด้วยกัน) เมื่อโปรแกรมเมอร์กำลังสร้างสตริงในรหัสซ้อนกัน / วนลูปยาว - โดยใช้ Single, StringBuilder ชัดเจนจะดีขึ้นสำหรับประสิทธิภาพ
user158037
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.