การสืบทอด: เป็นรหัสจาก superclass จริง * คัดลอก * ไปยังคลาสย่อยหรือเป็น * อ้างถึงโดย subclass *?


10

ชั้นSubเป็น subclass Supของชั้นเรียน นั่นหมายความว่าในทางปฏิบัติจริง ๆ ? หรือกล่าวอีกนัยหนึ่งความหมายในทางปฏิบัติของ "การสืบทอด" คืออะไร?

ตัวเลือกที่ 1:รหัสจาก Sup จะถูกคัดลอกไปยัง Sub อย่างแท้จริง (เหมือนใน 'copy-paste' แต่ไม่มีโค้ดที่คัดลอกที่มองเห็นได้ในคลาสย่อย)

ตัวอย่าง: methodA()เป็นวิธีการดั้งเดิมใน Sup Sub ขยาย Sup ดังนั้นจึงmethodA()เป็น (แทบ) คัดลอกวางไปที่ Sub methodA()ตอนนี้ตมีวิธีการตั้งชื่อ มันเหมือนกับของ Sup methodA()ในทุกบรรทัดของโค้ด แต่เป็นของ Sub - ทั้งหมดและไม่ขึ้นกับ Sup หรือเกี่ยวข้องกับ Sup ในทางใดทางหนึ่ง

ตัวเลือกที่ 2:รหัสจาก Sup ไม่ได้ถูกคัดลอกไปยัง Sub จริง ๆ มันยังคงอยู่ในซูเปอร์คลาสเท่านั้น แต่โค้ดนั้นสามารถเข้าถึงได้ผ่านคลาสย่อยและสามารถใช้งานได้โดยคลาสย่อย

ตัวอย่าง: methodA()เป็นวิธีการใน Sup ตำบลขยาย Sup ดังนั้นตอนนี้สามารถเข้าถึงได้ผ่านตำบลชอบโดย:methodA() subInstance.methodA()แต่ที่จริงแล้วจะเรียกใช้methodA()ในซูเปอร์คลาส ซึ่งหมายความว่าmethodA () จะทำงานในบริบทของซูเปอร์คลาสแม้ว่ามันจะถูกเรียกโดยคลาสย่อย

คำถาม: ตัวเลือกใดในสองตัวเลือกที่เป็นจริงว่าสิ่งต่าง ๆ ทำงานอย่างไร หากไม่มีสิ่งเหล่านี้โปรดอธิบายว่าสิ่งเหล่านี้ทำงานอย่างไร


นี่เป็นเรื่องง่ายที่จะทดสอบด้วยตัวคุณเอง - เขียนรหัสตรวจสอบไฟล์คลาส (แม้การตรวจสอบจะทำ) แก้ไขซุปเปอร์คลาสคอมไพล์อีกครั้งดูไฟล์คลาสอีกครั้ง คุณอาจพบว่าการอ่านบทที่ 3 การคอมไพล์สำหรับข้อมูลจำเพาะJava Virtual Machineของ JVM จะมีประโยชน์ในการทำความเข้าใจ (โดยเฉพาะส่วนที่ 3.7)

@MichaelT " ลอกเลียนแบบจริง " เป็นคำหลัก นอกจากนี้แม้ว่ารหัสจะถูกคัดลอกอย่างแท้จริงนี้อาจเกิดขึ้นหลังจากการโหลดคลาส

@delnan มันจะอยากรู้ถ้าฮอตสปอต (หรือเพิ่มประสิทธิภาพรันไทม์อื่น ๆ ) จะอินไลน์รหัสในบางจุด แต่นั่นกลายเป็นรายละเอียดการใช้งานของ JVM ที่อาจแตกต่างจาก JVM หนึ่งไปยังอีกและไม่สามารถตอบได้อย่างถูกต้อง ที่ดีที่สุดที่สามารถทำได้คือการมองที่ bytecode รวบรวม (และinvokespecialใช้ opcode ซึ่งอธิบายถึงสิ่งที่เกิดขึ้นจริง)

คำตอบ:


13

ตัวเลือก 2

รหัสไบต์มีการอ้างอิงแบบไดนามิกที่รันไทม์: นี่คือสาเหตุที่เกิดขึ้นเช่นLinkageErrors

ตัวอย่างเช่นสมมติว่าคุณรวบรวมสองคลาส:

public class Parent {
  public void doSomething(String x) { ... }
}

public class Child extends Parent {
  @Override
  public void doSomething(String x) {
    super.doSomething(x);
    ...
  }
}

ตอนนี้แก้ไขและคอมไพล์คลาสพาเรนต์ใหม่โดยไม่ต้องแก้ไขหรือคอมไพล์คลาสย่อยอีกครั้ง :

public class Parent {
  public void doSomething(Collection<?> x) { ... }
}

ในที่สุดให้เรียกใช้โปรแกรมที่ใช้คลาสลูก คุณจะได้รับNoSuchMethodError :

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

โดยปกติข้อผิดพลาดนี้จะถูกรวบรวมโดยคอมไพเลอร์; ข้อผิดพลาดนี้สามารถเกิดขึ้นได้ในขณะดำเนินการถ้าคำจำกัดความของคลาสมีการเปลี่ยนแปลงเข้ากันไม่ได้


7

ให้เริ่มด้วยคลาสง่าย ๆ สองคลาส:

package com.michaelt.so.supers;

public class Sup {
    int methodA(int a, int b) {
        return a + b;
    }
}

แล้ว

package com.michaelt.so.supers;

public class Sub extends Sup {
    @Override
    int methodA(int a, int b) {
        return super.methodA(a, b);
    }
}

รวบรวม methodA และดูรหัสไบต์ที่ได้รับ:

  methodA(II)I
   L0
    LINENUMBER 6 L0
    ALOAD 0
    ILOAD 1
    ILOAD 2
    INVOKESPECIAL com/michaelt/so/supers/Sup.methodA (II)I
    IRETURN
   L1
    LOCALVARIABLE this Lcom/michaelt/so/supers/Sub; L0 L1 0
    LOCALVARIABLE a I L0 L1 1
    LOCALVARIABLE b I L0 L1 2
    MAXSTACK = 3
    MAXLOCALS = 3

และคุณสามารถดูได้ที่นั่นด้วยวิธีการที่เรียกว่าการค้นหามันเทียบกับวิธีการเรียน Sup ()

opcode ที่ตอบสนองต่อการร้องขอมีตรรกะดังต่อไปนี้:

  • ถ้า C มีการประกาศสำหรับวิธีการอินสแตนซ์ที่มีชื่อและตัวอธิบายเป็นวิธีการแก้ไขแล้ววิธีการนี้จะถูกเรียก กระบวนการค้นหาสิ้นสุดลง
  • มิฉะนั้นถ้า C มีซูเปอร์คลาสขั้นตอนการค้นหาเดียวกันนี้จะดำเนินการซ้ำโดยใช้ซูเปอร์คลาสโดยตรงของ C วิธีการที่จะเรียกใช้เป็นผลมาจากการเรียกซ้ำของขั้นตอนการค้นหานี้ซ้ำ
  • มิฉะนั้นจะมีการเพิ่ม AbstractMethodError

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

คอมไพเลอร์ไม่ได้อินไลน์สิ่งนี้และไม่มีสำเนาของแหล่งที่มาของ Sup ในคลาส

อย่างไรก็ตามเรื่องยังไม่เสร็จ นี่เป็นเพียงรหัสที่คอมไพล์แล้ว เมื่อโค้ดเข้าสู่ JVM แล้ว HotSpotสามารถเข้าร่วมได้

น่าเสียดายที่ฉันไม่รู้มากเกี่ยวกับเรื่องนี้ดังนั้นฉันจะอุทธรณ์ต่อผู้มีอำนาจในเรื่องนี้และไปที่Inlining ใน Javaที่มีการกล่าวว่า HotSpot สามารถใช้วิธีการแบบอินไลน์ (แม้ไม่ใช่วิธีสุดท้าย)

การไปที่เอกสารนั้นมีข้อสังเกตว่าหากการเรียกใช้เมธอดเฉพาะกลายเป็นฮอตสปอตแทนที่จะทำการค้นหาในแต่ละครั้งข้อมูลนี้สามารถ inline - คัดลอกโค้ดจาก Sup methodA () ไปยัง Sub methodA () ได้อย่างมีประสิทธิภาพ

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

ตามที่ระบุไว้ในHotSpot Internals สำหรับ OpenJDK "วิธีการมักจะ inline การร้องขอแบบคงที่ส่วนตัวสุดท้ายและ / หรือ" พิเศษ "ง่ายต่อการแทรกแบบอินไลน์

หากคุณขุดลงไปในตัวเลือกสำหรับ JVMคุณจะพบตัวเลือก-XX:MaxInlineSize=35(35 เป็นค่าเริ่มต้น) ซึ่งเป็นจำนวนไบต์สูงสุดที่สามารถ inlined ฉันจะชี้ให้เห็นว่านี่คือเหตุผลที่ Java ชอบที่จะมีวิธีการเล็ก ๆ น้อย ๆ มากมาย - เพราะพวกเขาสามารถอินไลน์ได้ง่าย วิธีการขนาดเล็กเหล่านั้นจะเร็วขึ้นเมื่อพวกเขาถูกเรียกใช้มากขึ้นเพราะพวกเขาสามารถ inline และในขณะที่เราสามารถเล่นกับหมายเลขนั้นและทำให้ใหญ่ขึ้นมันอาจทำให้การเพิ่มประสิทธิภาพอื่น ๆ มีประสิทธิภาพน้อยลง (คำถาม SO ที่เกี่ยวข้อง: HotSpot JIT อินไลน์กลยุทธ์ซึ่งชี้ให้เห็นตัวเลือกอื่น ๆ จำนวนมากที่จะมองหาที่ภายในของการทำอินไลน์ที่ HotSpot กำลังทำอยู่)

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

และสิ่งที่ฉันเขียนเกี่ยวกับ HotSpot inlining นั้นใช้ได้กับ HotSpot JVM ที่ Oracle จัดจำหน่ายเท่านั้น หากคุณดูรายการ Java virtual machine ของวิกิพีเดียมีมากกว่า HotSpot และวิธีที่ JVMs จัดการอินไลน์สามารถแตกต่างอย่างสิ้นเชิงกับสิ่งที่ฉันได้อธิบายไว้ข้างต้น Apache Harmony, Dalvik, ART - สิ่งต่าง ๆ อาจทำงานแตกต่างกันตรงนั้น


0

รหัสไม่ได้ถูกคัดลอกมันถูกเข้าถึงโดยการอ้างอิง:

  • คลาสย่อยอ้างอิงถึงวิธีการและซูเปอร์คลาส
  • superclass อ้างอิงถึงวิธีการ

คอมไพเลอร์อาจปรับวิธีการแสดง / ดำเนินการในหน่วยความจำ แต่นี่คือโครงสร้าง

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