Pemuat kelas
Ini digunakan untuk memasok bytecode yang dikompilasi ke JVM, yang biasanya disimpan dalam file dengan ekstensi.class
, tetapi juga dapat diperoleh dari sumber lain, misalnya, diunduh melalui jaringan atau dihasilkan oleh aplikasi itu sendiri. Menurut spesifikasi Java SE, untuk menjalankan kode di JVM, Anda perlu menyelesaikan tiga langkah:
-
memuat bytecode dari sumber daya dan membuat instance kelas
Class
Ini termasuk mencari kelas yang diminta di antara kelas yang dimuat sebelumnya, memperoleh bytecode untuk memuat dan memeriksa kebenarannya, membuat instance kelas
Class
(untuk bekerja dengannya saat runtime), dan memuat kelas induk. Jika kelas induk dan antarmuka belum dimuat, maka kelas yang dimaksud dianggap tidak dimuat. -
mengikat (atau menghubungkan)
Menurut spesifikasinya, tahapan ini dibagi menjadi tiga tahapan lagi:
- Verifikasi , kebenaran bytecode yang diterima diperiksa.
- Persiapan , mengalokasikan RAM untuk bidang statis dan menginisialisasinya dengan nilai default (dalam hal ini, inisialisasi eksplisit, jika ada, sudah terjadi pada tahap inisialisasi).
- Resolusi , resolusi tautan simbolik jenis, bidang, dan metode.
-
menginisialisasi objek yang diterima
di sini, tidak seperti paragraf sebelumnya, segala sesuatunya tampak jelas apa yang harus terjadi. Tentu saja menarik untuk memahami secara pasti bagaimana hal ini terjadi.
- Kelas harus terisi penuh sebelum ditautkan.
- Sebuah kelas harus sepenuhnya diuji dan dipersiapkan sebelum diinisialisasi.
- Kesalahan resolusi tautan terjadi selama eksekusi program, meskipun terdeteksi pada tahap penautan.
Jenis Java Loader
Ada tiga loader standar di Java, yang masing-masing memuat kelas dari lokasi tertentu:-
Bootstrap adalah pemuat dasar, juga disebut Primordial ClassLoader.
memuat kelas JDK standar dari arsip rt.jar
-
Extension ClassLoader – pemuat ekstensi.
memuat kelas ekstensi, yang terletak di direktori jre/lib/ext secara default, tetapi dapat diatur oleh properti sistem java.ext.dirs
-
System ClassLoader – pemuat sistem.
memuat kelas aplikasi yang ditentukan dalam variabel lingkungan CLASSPATH
Kelas abstrak ClassLoader
Setiap pemuat, kecuali pemuat dasar, adalah turunan dari kelas abstrakjava.lang.ClassLoader
. Misalnya, implementasi pemuat ekstensi adalah kelas sun.misc.Launcher$ExtClassLoader
, dan pemuat sistem adalah sun.misc.Launcher$AppClassLoader
. Pemuat dasar adalah asli dan implementasinya disertakan dalam JVM. Kelas mana pun yang diperluas java.lang.ClassLoader
dapat menyediakan caranya sendiri untuk memuat kelas dengan blackjack dan kelas yang sama. Untuk melakukan ini, perlu mendefinisikan kembali metode-metode terkait, yang saat ini saya hanya dapat mempertimbangkannya secara dangkal, karena Saya tidak memahami masalah ini secara detail. Di sini mereka:
package java.lang;
public abstract class ClassLoader {
public Class<?> loadClass(String name);
protected Class<?> loadClass(String name, boolean resolve);
protected final Class<?> findLoadedClass(String name);
public final ClassLoader getParent();
protected Class<?> findClass(String name);
protected final void resolveClass(Class<?> c);
}
loadClass(String name)
salah satu dari sedikit metode publik, yang merupakan titik masuk untuk memuat kelas. Implementasinya adalah memanggil metode lain yang dilindungi loadClass(String name, boolean resolve)
, yang perlu diganti. Jika Anda melihat Javadoc dari metode yang dilindungi ini, Anda dapat memahami sesuatu seperti berikut: dua parameter diberikan sebagai masukan. Salah satunya adalah nama biner kelas (atau nama kelas yang memenuhi syarat) yang perlu dimuat. Nama kelas ditentukan dengan daftar semua paket. Parameter kedua adalah tanda yang menentukan apakah resolusi tautan simbolik diperlukan. Secara default adalah false , yang berarti pemuatan kelas lambat digunakan. Selanjutnya, menurut dokumentasi, dalam implementasi default metode ini terdapat panggilan findLoadedClass(String name)
yang memeriksa apakah kelas telah dimuat sebelumnya dan, jika demikian, mengembalikan referensi ke kelas ini. Jika tidak, metode pemuatan kelas dari pemuat induk akan dipanggil. Jika tidak ada pemuat yang dapat menemukan kelas yang dimuat, masing-masing pemuat, mengikuti dalam urutan terbalik, akan mencoba menemukan dan memuat kelas tersebut, dengan mengesampingkan kelas findClass(String name)
. Hal ini akan dibahas lebih rinci pada bab “Skema Pemuatan Kelas”. Dan terakhir, yang tak kalah pentingnya, setelah kelas dimuat, bergantung pada flag tekadnya , akan diputuskan apakah akan memuat kelas melalui tautan simbolik. Contoh yang jelas adalah tahap Resolusi dapat dipanggil selama tahap pemuatan kelas. Oleh karena itu, dengan memperluas kelas ClassLoader
dan mengganti metodenya, pemuat khusus dapat mengimplementasikan logikanya sendiri untuk mengirimkan bytecode ke mesin virtual. Java juga mendukung konsep pemuat kelas "saat ini". Pemuat saat ini adalah pemuat yang memuat kelas yang sedang dijalankan. Setiap kelas mengetahui loader mana yang memuatnya, dan Anda bisa mendapatkan informasi ini dengan memanggil String.class.getClassLoader()
. Untuk semua kelas aplikasi, pemuat "saat ini" biasanya adalah pemuat sistem.
Tiga Prinsip Pemuatan Kelas
-
Delegasi
Permintaan untuk memuat kelas diteruskan ke pemuat induk, dan upaya untuk memuat kelas itu sendiri dilakukan hanya jika pemuat induk tidak dapat menemukan dan memuat kelas. Pendekatan ini memungkinkan Anda memuat kelas dengan pemuat yang sedekat mungkin dengan pemuat dasar. Ini mencapai visibilitas kelas maksimum. Setiap loader menyimpan catatan kelas-kelas yang dimuat olehnya, menempatkannya dalam cache-nya. Himpunan kelas-kelas ini disebut ruang lingkup.
-
Visibilitas
Pemuat hanya melihat kelas “nya” dan kelas “induk” dan tidak mengetahui kelas yang dimuat oleh “anaknya”.
-
Keunikan
Sebuah kelas hanya dapat dimuat satu kali. Mekanisme delegasi memastikan bahwa loader yang memulai pemuatan kelas tidak membebani kelas yang sebelumnya dimuat ke JVM.
Skema pemuatan kelas
Ketika panggilan untuk memuat suatu kelas terjadi, kelas ini dicari di cache kelas yang sudah dimuat dari pemuat saat ini. Jika kelas yang diinginkan belum pernah dimuat sebelumnya, prinsip delegasi mentransfer kendali ke pemuat induk, yang terletak satu tingkat lebih tinggi dalam hierarki. Pemuat induk juga mencoba menemukan kelas yang diinginkan dalam cache-nya. Jika kelas telah dimuat dan pemuat mengetahui lokasinya, maka objekClass
kelas tersebut akan dikembalikan. Jika tidak, pencarian akan dilanjutkan hingga mencapai base bootloader. Jika pemuat dasar tidak memiliki informasi tentang kelas yang diperlukan (yaitu, belum dimuat), bytecode kelas ini akan dicari di lokasi kelas yang diketahui oleh pemuat tersebut, dan jika kelas tidak dapat dimuat, kontrol akan kembali ke pemuat anak, yang akan mencoba memuat dari sumber yang dikenalnya. Seperti disebutkan di atas, lokasi kelas untuk pemuat dasar adalah perpustakaan rt.jar, untuk pemuat ekstensi - direktori dengan ekstensi jre/lib/ext, untuk sistem - CLASSPATH, untuk pengguna dapat berupa sesuatu yang berbeda . Dengan demikian, kemajuan kelas pemuatan berjalan ke arah yang berlawanan - dari root loader ke yang sekarang. Ketika bytecode kelas ditemukan, kelas tersebut dimuat ke dalam JVM dan sebuah instance dari tipe tersebut diperoleh Class
. Seperti yang dapat Anda lihat dengan mudah, skema pemuatan yang dijelaskan mirip dengan penerapan metode di atas loadClass(String name)
. Di bawah ini Anda dapat melihat diagram ini dalam diagram.
Sebagai sebuah kesimpulan
Pada langkah pertama mempelajari suatu bahasa, tidak ada kebutuhan khusus untuk memahami bagaimana kelas dimuat di Java, namun mengetahui prinsip-prinsip dasar ini akan membantu Anda menghindari keputusasaan ketika menghadapi kesalahan sepertiClassNotFoundException
atau NoClassDefFoundError
. Ya, atau setidaknya secara kasar memahami apa akar masalahnya. Jadi, pengecualian ClassNotFoundException
terjadi ketika suatu kelas dimuat secara dinamis selama eksekusi program, ketika pemuat tidak dapat menemukan kelas yang diperlukan baik di cache atau di sepanjang jalur kelas. Namun kesalahan ini NoClassDefFoundError
lebih kritis dan terjadi ketika kelas yang diperlukan tersedia selama kompilasi, namun tidak terlihat selama eksekusi program. Hal ini bisa terjadi jika program lupa menyertakan perpustakaan yang digunakannya. Fakta memahami prinsip-prinsip struktur alat yang Anda gunakan dalam pekerjaan Anda (belum tentu mendalami secara jelas dan mendetail) menambah kejelasan pada pemahaman tentang proses yang terjadi di dalam mekanisme ini, yang, di gilirannya, mengarah pada penggunaan alat ini dengan percaya diri.
GO TO FULL VERSION