Warisan: Apakah kode dari superclass sebenarnya * disalin * ke subkelas, atau apakah * disebut oleh oleh subkelas *?


10

Kelas Subadalah subkelas dari kelas Sup. Apa artinya itu secara praktis? Atau dengan kata lain, apa arti praktis dari "warisan"?

Opsi 1: Kode dari Sup sebenarnya disalin ke Sub. (seperti pada 'copy-paste', tetapi tanpa kode yang disalin secara visual terlihat di subclass).

Contoh: methodA()adalah metode yang awalnya dalam Sup. Sub extends Sup, begitu methodA()juga (secara virtual) disalin ke Sub Sekarang Sub memiliki nama metode methodA(). Ini identik dengan Sup methodA()di setiap baris kode, tetapi sepenuhnya milik Sub - dan tidak bergantung pada Sup atau terkait dengan Sup dengan cara apa pun.

Opsi 2: Kode dari Sup sebenarnya tidak disalin ke Sub. Itu masih hanya di superclass. Tetapi kode itu dapat diakses melalui subclass dan dapat digunakan oleh subclass.

Contoh: methodA()adalah metode dalam Sup. Sub meluas Sup, jadi sekarang methodA()bisa diakses melalui sub seperti: subInstance.methodA(). Tapi itu benar-benar akan memanggil methodA()superclass. Yang berarti methodA () akan beroperasi dalam konteks superclass, bahkan jika itu dipanggil oleh subclass.

Pertanyaan: Manakah dari dua opsi yang benar-benar berfungsi? Jika tidak ada satu pun dari mereka, tolong jelaskan bagaimana hal ini benar-benar bekerja


Ini mudah untuk menguji sendiri - tulis kode, periksa file kelas (bahkan checksum akan melakukannya), modifikasi kelas super, kompilasi lagi, lihat file kelas lagi. Anda juga mungkin menemukan membaca Bab 3. Mengkompilasi untuk Java Virtual Machine dari spesifikasi JVM untuk membantu dalam memahami (terutama bagian 3.7).

@MichaelT " hampir disalin" adalah kata kunci. Juga, bahkan jika kode itu benar-benar disalin, ini hanya dapat terjadi setelah pemuatan kelas.

@delnan akan penasaran jika Hotspot (atau pengoptimal runtime lainnya) akan menyejajarkan kode di beberapa titik, tetapi itu menjadi detail implementasi JVM yang mungkin berbeda dari satu JVM ke yang lain sehingga tidak dapat dijawab dengan benar. Yang terbaik yang bisa dilakukan adalah melihat bytecode dikompilasi (dan invokespecial penggunaan opcode yang menggambarkan apa yang sebenarnya terjadi)

Jawaban:


13

Pilihan 2.

Bytecode direferensikan secara dinamis saat runtime: inilah sebabnya, misalnya, LinkageErrors terjadi.

Misalnya, anggap Anda mengkompilasi dua kelas:

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

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

Sekarang modifikasi dan kompilasi ulang kelas induk tanpa memodifikasi atau mengkompilasi ulang kelas anak :

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

Akhirnya, jalankan program yang menggunakan kelas anak. Anda akan menerima NoSuchMethodError :

Dilemparkan jika suatu aplikasi mencoba memanggil metode tertentu dari suatu kelas (baik statis atau instance), dan kelas itu tidak lagi memiliki definisi metode itu.

Biasanya, kesalahan ini ditangkap oleh kompiler; kesalahan ini hanya dapat terjadi pada saat run time jika definisi kelas telah berubah secara tidak kompatibel.


7

Mari kita mulai dengan dua kelas sederhana:

package com.michaelt.so.supers;

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

lalu

package com.michaelt.so.supers;

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

Mengkompilasi methodA dan melihat kode byte yang didapat:

  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

Dan Anda dapat melihat di sana dengan metode invokes khusus yang melakukan pencarian terhadap metode kelas Sup A ().

The invokespecial opcode memiliki logika berikut:

  • Jika C berisi deklarasi untuk metode instance dengan nama dan deskriptor yang sama dengan metode yang diselesaikan, maka metode ini akan dipanggil. Prosedur pencarian berakhir.
  • Kalau tidak, jika C memiliki superclass, prosedur pencarian yang sama ini dilakukan secara rekursif menggunakan superclass langsung dari C. Metode yang akan dipanggil adalah hasil dari doa rekursif dari prosedur pencarian ini.
  • Kalau tidak, AbstractMethodError dinaikkan.

Dalam hal ini, tidak ada metode instan dengan nama dan deskripsi yang sama di kelasnya sehingga peluru pertama tidak akan ditembakkan. Namun peluru kedua akan - ada superclass dan itu memanggil methodA super.

Kompiler tidak memasukkan ini dan tidak ada salinan sumber Sup di kelas.

Namun ceritanya belum selesai. Ini hanyakode yang dikompilasi . Setelah kode mencapai JVM, HotSpot dapat terlibat.

Sayangnya, saya tidak tahu banyak tentang hal itu, jadi saya akan naik banding ke pihak berwenang mengenai masalah ini dan pergi ke Inlining di Jawa di mana dikatakan bahwa HotSpot dapat menyatukan metode (bahkan metode non-final).

Pergi ke dokumen dicatat bahwa jika pemanggilan metode tertentu menjadi hot spot alih-alih melakukan pencarian setiap kali, informasi ini dapat digarisbawahi - secara efektif menyalin kode dari Sup methodA () ke Sub methodA ().

Ini dilakukan saat runtime, dalam memori, berdasarkan pada bagaimana aplikasi berperilaku dan optimasi apa yang diperlukan untuk mempercepat kinerja.

Seperti yang dinyatakan dalam HotSpot Internals for OpenJDK "Metode sering kali digarisbawahi. Doa statis, pribadi, final, dan / atau" khusus "mudah untuk diselaraskan."

Jika Anda menggali opsi untuk JVM Anda akan menemukan opsi -XX:MaxInlineSize=35(35 sebagai default) yang merupakan jumlah maksimum byte yang dapat digariskan. Saya akan menunjukkan bahwa inilah mengapa Java suka memiliki banyak metode kecil - karena mereka dapat dengan mudah diuraikan. Metode-metode kecil menjadi lebih cepat ketika mereka dipanggil lebih karena mereka dapat digarisbawahi. Dan sementara seseorang dapat bermain dengan angka itu dan membuatnya lebih besar itu dapat menyebabkan optimasi lainnya menjadi kurang efektif. (Pertanyaan SO terkait: Strategi inline HotSpot JIT yang menunjukkan sejumlah opsi lain untuk mengintip internal inlining yang sedang dilakukan HotSpot).

Jadi, tidak - kode tidak diuraikan pada waktu kompilasi. Dan, ya - kode tersebut dapat diuraikan dengan baik pada saat runtime jika optimasi kinerja memerlukannya.

Dan semua yang saya tulis tentang HotSpot inlining hanya berlaku untuk HotSpot JVM yang didistribusikan oleh Oracle. Jika Anda melihat daftar mesin virtual Java wikipedia ada banyak lebih dari sekadar HotSpot dan cara mereka menangani inline JVM bisa sangat berbeda dari apa yang saya jelaskan di atas. Apache Harmony, Dalvik, ART - semuanya dapat bekerja secara berbeda di sana.


0

kode tidak disalin, diakses dengan referensi:

  • subclass mereferensikan metodenya dan superclass
  • superclass mereferensikan metodenya

kompiler dapat mengoptimalkan bagaimana ini diwakili / dieksekusi dalam memori, tapi itu pada dasarnya struktur

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.