1. Rtti:
Informasi Jenis Runtime memungkinkan Anda untuk menemukan dan menggunakan informasi tipe saat program sedang berjalan.
Ada dua cara untuk mengidentifikasi informasi tentang objek dan kelas saat berjalan di Java: RTTI tradisional, dan refleksi. Mari kita bicara tentang RTTI.
RTTI: Saat runtime, identifikasi jenis objek. Tetapi jenis ini harus diketahui pada waktu kompilasi.
Mari kita ambil contoh untuk melihat penggunaan RTTI. Ini melibatkan konsep polimorfisme: membiarkan kode hanya beroperasi pada referensi ke kelas dasar, dan benar -benar memanggil metode subkelas tertentu biasanya akan membuat objek konkret (lingkaran, persegi, atau segitiga, lihat contoh di bawah), mengubahnya ke atas menjadi bentuk (mengabaikan jenis spesifik dari objek), dan menggunakan anonim (itu, tidak mengetahui jenis spesifik) dalam bentuk referensi spesifik dalam bentuk tertentu dalam bentuk punggung dalam bentuk tertentu) dalam bentuk punggung), dan menggunakan rujukan wadah (itu, tidak mengetahui jenis) dalam bentuk spesifik dalam rujukan spesifik dalam) dalam rujukan spesifik) dalam rujukan spesifik dalam bentuk dalam) dalam bentuk punggung dalam spesifik) dalam bentuk punggungnya), dan menggunakan anonim (itu, tidak mengetahui jenis spesifik dalam) dalam bentuk referensi dalam spesifik) dalam rujukan spesifik dalam bentuk punggung), dan menggunakan wadah dan menggunakan spesifik dalam bentuk dalam bentuk),
Bentuk kelas abstrak {// ini memanggil metode toString () dari kelas saat ini, mengembalikan konten void aktual () {System.out.println (this + "draw ()"); } // mendeklarasikan toString () sebagai tipe abstrak, integrasi paksa untuk mengganti metode abstrak string publik toString ();} lingkaran kelas memperluas bentuk {public string toString () {return "circle"; }} class square memperluas bentuk {public string toString () {return "square"; }} kelas segitiga memperluas bentuk {public string toString () {return "triangle"; }} public static void main (string [] args) {// Saat memasukkan objek bentuk ke dalam array daftar <dape>, itu akan berubah menjadi bentuk ke atas, sehingga kehilangan daftar informasi tipe spesifik <dape> Shapelist = arrays.aslist (new circle (), new baru (), segitiga baru ()); // Ketika dikeluarkan dari array, pada kenyataannya, semua elemen wadah ini diadakan sebagai objek dan secara otomatis akan mengubah hasil menjadi bentuk. Ini adalah penggunaan dasar RTTI. untuk (bentuk bentuk: shapelist) {shape.draw (); }}Hasil outputnya adalah:
Circledraw () squaredraw () triangledraw ()
Saat menyimpannya ke dalam array, itu akan secara otomatis berubah menjadi bentuk, dan jenis spesifiknya hilang. Ketika dikeluarkan dari array, (wadah daftar memegang semuanya sebagai objek), itu akan secara otomatis mengubah hasil kembali ke bentuk. Ini adalah penggunaan dasar RTTI. Semua konversi jenis di Java diperiksa korektif saat runtime, yaitu, RTTI: at runtime, identifikasi jenis objek.
Transformasi di atas tidak menyeluruh. Ketika elemen -elemen array dikeluarkan, objek diubah menjadi bentuk, bukan jenis spesifik. Ini dilakukan oleh wadah dan sistem generik Java selama kompilasi, dan ada operasi konversi jenis untuk memastikan ini saat runtime.
Kode spesifik yang dapat dieksekusi ke dalam subkelas melalui objek bentuk ditentukan oleh polimorfisme. Untuk detailnya, itu tergantung pada objek spesifik yang ditunjukkan oleh referensi bentuk.
Selain itu, menggunakan RTTI, Anda dapat meminta jenis yang tepat dari objek yang ditunjuk oleh referensi bentuk, dan kemudian secara selektif menjalankan metode subclass.
2. Objek Kelas:
Untuk memahami bagaimana RTTI bekerja di Java, Anda harus tahu bagaimana informasi jenis diwakili saat runtime, yang dilakukan oleh kelas objek khusus.
Objek kelas digunakan untuk membuat semua objek "biasa" dari suatu kelas. Java menggunakan objek kelas untuk menjalankan RTTI -nya.
Setiap kali kelas baru dikompilasi, objek kelas (file .class) dihasilkan. JVM yang menjalankan program ini akan menggunakan subsistem "Class Loader".
Subsistem Loader Class: Berisi rantai loader kelas, tetapi hanya satu loader kelas asli, yang merupakan bagian dari implementasi JVM. Loader kelas asli memuat kelas tepercaya, termasuk kelas API Java, biasanya dari disk lokal. Ketika kelas perlu dimuat dengan cara tertentu untuk mendukung aplikasi server web, loader kelas tambahan dapat dilampirkan.
2.1. Waktu memuat kelas:
Kelas ini dimuat ketika program membuat referensi pertama ke anggota statis kelas. Ini membuktikan bahwa konstruktor sebenarnya merupakan metode statis kelas. Saat membuat objek baru kelas menggunakan operator baru, itu juga akan digunakan sebagai referensi ke anggota statis kelas.
Dapat dilihat bahwa program Java dimuat secara dinamis dan dimuat sesuai permintaan. Ketika kelas diperlukan, class loader akan terlebih dahulu memeriksa apakah objek kelas kelas ini telah dimuat. Jika belum dimuat, loader kelas default akan menemukan file .class berdasarkan nama kelas. Berikutnya adalah fase verifikasi: Saat dimuat, mereka menerima verifikasi untuk memastikan mereka tidak rusak dan tidak mengandung kode java yang buruk.
2.2. Metode terkait kelas, newinstance ()
Berikut ini adalah contoh untuk menunjukkan pemuatan objek kelas:
Kelas A {// basis kode statis, dieksekusi ketika dimuat untuk pertama kalinya, dan diketahui ketika kelas dimuat dengan mencetak informasi statis {System.out.println ("Memuat A"); }} class b {static {System.out.println ("memuat b"); }} class c {static {System.out.println ("memuat c"); }} class public memuat {public static void main (string [] args) {System.out.println ("Execute Main ..."); a a () baru; System.out.println ("Setelah A New A"); coba {class.forname ("com.itzhai.test.type.b"); } catch (ClassNotFoundException e) {System.out.println ("Cloud Not Find Class B"); } System.out.println ("After Class.Forname B"); baru c (); System.out.println ("Setelah C baru"); }}Hasil outputnya adalah:
Eksekusi utama ... Memuat lebih banyak kelas aloading baru.
Dapat dilihat bahwa objek kelas dimuat hanya saat dibutuhkan. Perhatikan metode class.forname () di sini:
Metode forname () adalah metode untuk mendapatkan referensi ke objek kelas. Dengan mendapatkan referensi yang sesuai untuk objek kelas, Anda dapat menggunakan informasi tipe saat runtime.
Jika Anda sudah memiliki objek yang menarik, Anda bisa mendapatkan referensi kelas dengan mengikuti metode getClass () yang disediakan oleh objek kelas.
Berikut adalah kode yang digunakan oleh kelas:
antarmuka x {} antarmuka y {} antarmuka z {} class letter {letter () {}; Huruf (int i) {};} class newletter memperluas implemen huruf x, y, z {newletter () {super (1); };} kelas publik classtest { / *** Cetak informasi jenis* @param c* / static void printinfo (kelas c) {// getName () Mendapat sistem nama kelas yang sepenuhnya memenuhi syarat. // Dapatkan nama kelas System.out.println ("Nama Sederhana:" + C.GetSimpleName ()); // Dapatkan sistem nama kelas yang sepenuhnya memenuhi syarat.out.println ("Nama Canonical:" + C.GetCanonicalName ()); } public static void main (string [] args) {class c = null; coba {// dapatkan referensi kelas c = class.forname ("com.itzhai.test.type.newletter"); } catch (classNotFoundException e) {System.out.println ("Tidak dapat menemukan com.itzhai.test.type.newletter"); System.exit (1); } // Cetak Informasi Jenis Antarmuka untuk (kelas wajah: c.getInterfaces ()) {printinfo (wajah); } // Dapatkan kelas referensi kelas superclass up = c.getSuperclass (); Objek obj = null; coba {// Buat instance kelas melalui metode newInstance () obj = up.newinstance (); } catch (InstantiationException e) {System.out.println ("tidak dapat membuat instantiate"); } catch (IllegalAccessException e) {System.out.println ("tidak dapat mengakses"); } // cetak informasi jenis superclass printinfo (obj.getClass ()); }}Outputnya adalah:
Nama kelas: com.itzhai.test.type.x adalah antarmuka? Nama Truesimple: XCanonical Name: com.itzhai.test.type.xclass Nama: com.itzhai.test.type.y adalah antarmuka? Nama TruesImple: Nama Ycanonical: com.itzhai.test.type.yclass Nama: com.itzhai.test.type.z adalah antarmuka? Nama Truesimple: Nama Zanonik: com.itzhai.test.type.zClass Nama: com.itzhai.test.type.Letter adalah antarmuka? Falsesimple Name: Lettercanonical Name: com.itzhai.test.type.Letter
Perhatikan bahwa string yang diteruskan ke forname () harus menggunakan nama yang sepenuhnya memenuhi syarat (termasuk nama paket).
Melalui metode yang digunakan dalam printinfo, Anda dapat menemukan struktur warisan kelas lengkap objek saat runtime.
Dengan menggunakan metode newInstance () kelas, ini adalah cara untuk mengimplementasikan "konstruktor virtual" untuk membuat instance kelas. Referensi objek diperoleh, tetapi menunjuk ke objek huruf saat direferensikan. Kelas yang dibuat menggunakan newInstance () harus memiliki konstruktor default. (Melalui API refleksi, Anda dapat menggunakan konstruktor apa pun untuk secara dinamis membuat objek kelas).
2.3. Konstanta literal kelas:
Selain menggunakan metode getName (), Java juga menyediakan cara lain untuk menghasilkan referensi ke objek kelas, yaitu, menggunakan konstanta literal kelas:
Newletter.class;
Metode ini sederhana dan aman, dan diperiksa selama kompilasi, membuatnya lebih efisien. Ini dapat digunakan tidak hanya untuk kelas biasa, tetapi juga untuk antarmuka, array, dan tipe data dasar. Selain itu, untuk kelas pembungkus tipe data dasar, ada juga tipe bidang standar. Bidang tipe adalah referensi untuk menjalankan objek kelas tipe data dasar yang sesuai. Demi penyatuan, disarankan untuk menggunakan formulir .class.
2.4. Perbedaan antara menggunakan .class dan menggunakan metode getName () untuk membuat referensi objek:
Saat dibuat dengan .class, objek kelas tidak diinisialisasi secara otomatis. Langkah -langkah penciptaan adalah sebagai berikut:
(1) Pemuatan dilakukan oleh class loader: lihat bytecode (biasanya di jalur yang ditentukan oleh classpath, tetapi tidak perlu), dan kemudian buat objek kelas dari bytecode ini.
(2) Tautan akan memverifikasi bytecode di kelas dan mengalokasikan ruang penyimpanan untuk domain statis. Jika perlu, semua referensi ke kelas lain yang dibuat oleh kelas ini akan diuraikan.
(3) Inisialisasi Jika kelas memiliki superclass, inisialisasi, dan jalankan inisialisasi statis dan blok inisialisasi statis.
Inisialisasi ditunda sampai referensi pertama ke metode statis (konstruktor secara implisit statis) atau domain statis non-nomor:
kelas data1 {static final int a = 1; static final double b = math.random (); static {system.out.println ("init data1 ..."); }} kelas data2 {static int a = 12; static {system.out.println ("init data2 ..."); }} kelas data3 {static int a = 23; static {system.out.println ("init data3 ..."); }} kelas publik classtest2 {public static void main (string [] args) {system.out.println ("data1.class:"); Data kelas1 = data1.class; System.out.println (data1.a); // data1 system.out.println (data1.b); // data1 inisialisasi system.out.println (data2.a); // data2 diinisialisasi coba {class data3 = class.forname ("com.itzhai.test.type.data3"); // data3 inisialisasi} catch (classnotfoundException e) {System.out.println ("Tidak dapat menemukan com.itzhai.test.type.data3 ..."); } System.out.println (data3.a); }}Hasil outputnya adalah:
Data1.class: 1Init Data1 ... 0.26771085109184534Init data2 ... 12init data3 ... 23
Inisialisasi secara efektif dicapai sebagai "malas" mungkin.
2.5. Berikut ini adalah beberapa situasi untuk menentukan apakah akan melakukan inisialisasi:
(1) Sintaks kelas memperoleh referensi ke kelas dan tidak akan menyebabkan inisialisasi;
(2) class.forname () menghasilkan referensi kelas dan segera diinisialisasi;
(3) Jika nilai akhir statis adalah "konstanta kompiler", maka nilai ini dapat dibaca tanpa menginisialisasi kelas;
(4) Tidak cukup untuk memastikan perilaku ini jika hanya mengatur domain ke final statis, misalnya:
static final double b = math.random ();
(5) Jika domain statis adalah bushifinal, maka saat mengaksesnya, Anda selalu perlu dihubungkan dan diinisialisasi;
2.6. Kutipan kelas umum:
Referensi kelas mewakili jenis yang tepat dari objek yang ditunjuk, dan objek adalah objek kelas kelas. Di Javase5, objek kelas yang ditunjuk oleh referensi kelas dapat memenuhi syarat dengan obat generik, dan kompiler dapat menegakkan pemeriksaan jenis tambahan:
Class intcls = int.class; // Gunakan generik untuk menentukan referensi yang ditunjukkan oleh kelas kelas <integer> genIntcls = int.class; // kelas tanpa obat generik dapat dipindahkan untuk menunjuk ke objek kelas lain intcls = double.class; // kompilasi berikut akan kesalahan // genIntcls = double.class;
2.6.1. Gunakan wildcard? Santai keterbatasan obat generik:
Kelas <?> Intcls = int.class; intcls = string.class;
Di javase5, kelas <?> Lebih baik daripada kelas biasa, dan disarankan untuk menggunakan kelas <?> Bahkan jika mereka setara, karena keuntungan kelas <?> Apakah itu berarti Anda tidak terjadi atau lalai, tetapi menggunakan referensi kelas yang tidak spesifik.
Untuk mendefinisikan referensi ke kelas ke jenis tertentu, atau subtipe jenis itu dapat menggunakan wildcard dengan ekstensi, buat ruang lingkup:
Kelas <? Extends number> num = int.class; // Rentang referensi num adalah angka dan subkelasnya, sehingga Anda dapat menetapkan nilai num = double.class; num = number.class;
2.6.2. Metode newinstance () di bawah obat generik:
Using the Class after generics, the object returned by calling newInstance() is of the exact type, but when you use getSuperclass() to get the superclass corresponding to the generic, there are some limitations to the real type: the compiler knows the type of the superclass during the compilation period, but the newInstance() method referenced by this obtained superclass does not return the exact type, but the Object:
Dog dog = dogcls.newinstance (); abstrak kelas hewan {} class dog memperluas hewan {} // metode penulisan berikut salah, dan hanya dapat mengembalikan kelas <? Super Dog> type // class <animal> animalcls = dogcls.getsuperclass (); Kelas <? Super Dog> AnimalCls = dogcls.getsuperclass (); // Melalui referensi superclass yang diperoleh, Anda hanya dapat membuat objek yang mengembalikan objek tipe objek obj = animalcls.newinstance (); 2.6.3. Sintaks transformasi baru: Cast () Metode
Lihat langsung pada kode:
Hewan hewan = anjing baru (); kelas <dog> dogcls = dog.class; dog dog = dogcls.cast (hewan); // atau langsung menggunakan metode transformasi berikut anjing = (anjing) hewani;
Dapat ditemukan bahwa menggunakan metode cast () telah melakukan pekerjaan tambahan. Metode konversi ini dapat digunakan dalam situasi berikut: Saat menulis pita generik, jika referensi kelas disimpan dan Anda berharap untuk melakukan transformasi melalui referensi kelas ini, Anda dapat menggunakan metode cast ().
3. Ketik Periksa Instanceof
3.1. Periksa sebelum jenis konversi
Kompiler memungkinkan Anda untuk secara bebas melakukan operasi penugasan transformasi ke atas tanpa operasi transformasi yang ditampilkan, seperti menetapkan nilai untuk referensi ke superclass.
Namun, jika konversi tipe yang ditampilkan tidak digunakan, kompiler tidak akan memungkinkan Anda untuk melakukan penugasan downconversion. Pada saat ini, kami mungkin juga memeriksa apakah objek tersebut merupakan instance dari jenis tertentu, dan contoh kata kunci dari kata kunci:
if (x instance dari anjing) ((anjing) x) .bark ();
3.2. Bentuk RTTI:
Jadi, sejauh ini, kita tahu bahwa bentuk RTTI meliputi:
(1) Konversi tipe tradisional (bentuk)
(2) Objek kelas mewakili jenis objek
(3) Contoh Kata Kunci
3.3. Metode Dinamis dari Metode:
Metode Class.isInstance menyediakan cara untuk menguji objek secara dinamis.
Berikut ini menunjukkan penggunaan instanceof dan class.isinstance:
Atribut:
Atribut Antarmuka Publik {}Membentuk:
/** * Buat kelas abstrak */bentuk kelas abstrak publik {// ini memanggil metode toString dari metode ToString kelas saat ini untuk mendapatkan informasi public void draw () {System.out.println (this + ".draw ()"); } // mendeklarasikan metode tostring () untuk abstrak, sehingga memaksa pewaris untuk menulis ulang metode. abstrak string publik toString ();}Lingkaran:
Lingkaran Kelas Publik memperluas Atribut Implement {Public String ToString () {return "Circle"; }}Persegi:
Public Class Square memperluas bentuk {public string toString () {return "square"; }}Segi tiga:
Triangle kelas publik memperluas bentuk {public string toString () {return "Triangle"; }}Ketik Pemeriksaan:
// instanceofcircle c = new circle (); // Tentukan apakah instance superclass system.out.format ("menggunakan instanceof: %s adalah bentuk? %b/n", c.tostring (), cemain Contoh C); // Tentukan apakah instance dari System Superclass.out.Format ("Menggunakan contoh: %s adalah lingkaran? %B/N/N", CIREPTOF ("Menggunakan instance: %s adalah lingkaran? %B/N/N", CIREPTOF ("Menggunakan instancef: %s adalah lingkaran? %B/n Superclass System.out.format ("Menggunakan class.isinstance: %s adalah bentuk? %b/n", c.tostring (), c instance dari lingkaran); // Tentukan apakah instance dari superclass system.out.format ("menggunakan class.isinstance: %s adalah bentuk? %b/n", c.tostring (), bentuk. System.out.format ("Menggunakan class.isInstance: %s adalah atribut? %B/n", c.tostring (), attribute.class.isinstance (c));Dapat ditemukan bahwa instance dari atau metode kelas.
Berikut ini menunjukkan cara menggunakan kelas dinamis.
Pertama -tama buat kelas generator bentuk abstrak:
kelas abstrak publik shapecreator {private acak rand = new acak (10); // Kembalikan serangkaian jenis objek yang disediakan oleh kelas implementasi. Anda akan melihat dua formulir implementasi nanti, berdasarkan forname dan berdasarkan konstanta literal kelas. Daftar abstrak publik kelas <kelas <? memperluas bentuk >> type (); // menghasilkan instance objek tipe secara acak dalam array tipe objek bentuk publik randomshape () {int n = rand.nextInt (type (). Size ()); coba {return type (). get (n) .newInstance (); } catch (InstantiationException e) {E.PrintStackTrace (); kembali nol; } catch (ilegalAccessException e) {e.printstacktrace (); kembali nol; }} // menghasilkan bentuk publik array acak [] createArray (ukuran int) {bentuk [] hasil = bentuk baru [ukuran]; untuk (int i = 0; i <size; i ++) {hasil [i] = randomshape (); } hasil pengembalian; } // Hasilkan array acak, arraylist arraylist generik <sitem> arraylist (ukuran int) {arraylist <dape> result = new arraylist <daping> (); Collections.addall (hasil, createarray (ukuran)); hasil pengembalian; }}Selanjutnya, tulis implementasi kelas abstrak ini:
/** * Implementasi Generator Gabungan * @Author Artinking * */Kelas Publik ForNamecreator meluas Shapecreator {Private Static List <class <? Extends Shape >> type = new ArrayList <class <? memperluas bentuk >> (); Private Static String [] typenames = {"com.itzhai.javanote.entity.circle", "com.itzhai.javanote.entity.square", "com.itzhai.javanote.entity.triatangle"}; @SuppressWarnings ("tidak digunakan") private static void loader () {for (name string: typenames) {coba {type.add ((class <? Extends shape>) class.forname (name)); } catch (ClassNotFoundException e) {E.PrintStackTrace (); }}} // Inisialisasi array jenis yang diperlukan untuk memuat statis {loader (); } Daftar Publik <class <? Extends Shape >> type () {return tipe; }}Akhirnya, tulis kelas yang menghitung jumlah bentuk, menggunakan instanceof:
kelas publik shapecount {kelas statis shapecounter memperluas hashmap <string, integer> {public void count (string type) {integer quantity = get (type); if (kuantitas == null) {put (type, 1); } else {put (type, quantity + 1); }}} // Demonstrasi jenis objek yang menyatakan melalui instance dari kata kunci kata public static void countshapes (shapecreator creator) {shapecounter counter = new shapecounter (); untuk (bentuk bentuk: create.createArray (20)) {if (bentuk instance dari lingkaran) counter.count ("circle"); if (bentuk instance dari square) counter.count ("square"); if (bentuk contoh segitiga) {counter.count ("segitiga"); }} System.out.println (counter); } public static void main (string [] args) {countshapes (new fornamecreator ()); }}Tulis ulang implementasi kelas abstrak dan ulangi kembali dengan konstanta literal kelas:
/*** Implementasi generator literal*/kelas publik LiteralCreator meluas Shapecreator {Daftar Akhir Statis Publik <class <? Extends Shape >> AllType = collections.unmodifiblelist (arrays.aslist (Circle.class, Triangle.class, Square.class)); Daftar Publik <Kelas <? Extends Shape >> type () {return allType; } public static void main (string [] args) {System.out.println (allType); }}Sekarang gunakan class.instance untuk menghitung jumlah bentuk sebagai berikut:
/*** Hapus pernyataan instance monotonik di Shapecount asli dengan menggunakan class.instanceof objek tes dinamis**/kelas publik shapecount2 {private static final list <class <? Extends Shape >> shapetypes = literalcreator.alltype; Kelas statis Shapecounter memperluas hashmap <string, integer> {public void count (string type) {integer quantity = get (type); if (kuantitas == null) {put (type, 1); } else {put (type, quantity + 1); }}} // Demonstrasi tipe objek statistik melalui class.isInstance () public static void Countshapes (Shapecreator Creator) {shapecounter counter = Shapecounter baru (); untuk (bentuk bentuk: create.createArray (20)) {for (class <? Extends Shape> cls: shapetypes) {if (cls.isInstance (bentuk)) {counter.count (cls.getsimplename ()); }} System.out.println (counter); } public static void main (string [] args) {countshapes (new fornamecreator ()); }}Sekarang ada dua implementasi generator. Kami dapat menambahkan lapisan penampilan di sini dan mengatur metode implementasi default:
/*** Sekarang ada dua implementasi generator. Mari kita tambahkan lapisan penampilan di sini dan atur metode implementasi default */bentuk kelas publik {public static final shapecreator creator = new literalcreator (); bentuk public static randeshape () {return creator.randomshape (); } bentuk public static [] createArray (ukuran int) {return creator.createArray (size); } Public Static ArrayList <Torm> ArrayList (int ixe) {return creator.arraylist (size); }} 3.4. Kesetaraan Contoh dan Kelas:
Hasil yang dihasilkan oleh instanceof dan isInstance () persis sama, mempertahankan konsep tipe dan menentukan apakah kelas atau kelas turunan dari kelas ini.
Equals () sama dengan ==, dan menggunakan objek kelas yang lebih praktis ini, warisan tidak dipertimbangkan.
System.out.println (Circle baru () Instanceof Circle); // truesystem.out.println (shape.class.isinstance (new circle ())); // truesystem.out.println ((new circle ()). getClass () == Circle.class); // truesystem.out.println ((Circle baru (). getClass ()). Equals (Shape.class)); // PALSU