Apa itu refleksi
"Refleksi memungkinkan program yang berjalan di JVM untuk mendeteksi dan memodifikasi perilaku runtime." Konsep ini sering dikacaukan dengan introspeksi. Berikut adalah penjelasan dari dua istilah ini di Wikipedia:
Contoh introspeksi: Contoh operator digunakan untuk mendeteksi apakah suatu objek termasuk dalam kelas tertentu.
if (obj instance dari anjing) {dog d = (dog) obj; d.bark ();}Contoh refleksi: metode class.forname () dapat memperoleh objek kelas yang sesuai melalui nama kelas atau antarmuka (string atau nama yang sepenuhnya memenuhi syarat). Metode gabungan memicu inisialisasi kelas.
// Gunakan kelas refleksi <?> C = class.forname ("classpath.and.classname"); objek anjing = c.newinstance (); metode m = c.getDeclaredMethod ("kulit kayu", kelas baru <?> [0]); m.invoke (anjing);Di Java, refleksi lebih dekat dengan introspeksi karena Anda tidak dapat mengubah struktur suatu objek. Meskipun beberapa API dapat digunakan untuk memodifikasi visibilitas metode dan sifat, mereka tidak dapat memodifikasi struktur.
Refleksi array
Apa gunanya refleksi array? Kapan Anda perlu menggunakan refleksi array? Mari kita lihat kode berikut:
Integer [] nums = {1, 2, 3, 4}; Objek [] objs = nums; // Dapat ada secara otomatis dikonversi menjadi integer [] ke objek [] objek obj = nums; // integer [] tentu saja objek int [] ids = {1, 2, 3, 4}; // objek [] objs2 = id; // int [] tidak dapat dikonversi ke objek [] objek obj2 = id; // int [] adalah objekContoh di atas menunjukkan bahwa tipe dasar array satu dimensi hanya dapat dianggap sebagai objek, bukan sebagai objek [].
int [] [] intarray = {{1, 2}, {3, 4}}; Objek [] oa = intarray; Objek obj = intarray; // integer [] [] integerarray = intarray; int [] [] bukan integer [] [] integer [] [] integerarray2 = integer baru [] [] {{1, 2}, {3, 4}}; Objek [] [] oa2 = integerarray2; Objek [] oa3 = integerarray2; Objek obj2 = integerarray2;Dari contoh di atas, kita dapat melihat bahwa array Java dua digit adalah array array. Mari kita lihat contoh mencerminkan array:
paket cn.zq.array.reflect; impor java.lang.reflect.array; impor java.util.arrays; impor java.util.random; Public Class ArrayReflect {public static void main (string [] args) {acak rand = new acak (47); int [] adalah = int baru [10]; untuk (int i = 0; i <is.length; i ++) {is [i] = rand.nextint (100); } System.out.println (IS); System.out.println (arrays.aslist (IS)); /*Dua output di atas adalah string mirip dengan "[[i@14318bb]", yang tidak dapat menampilkan konten yang disimpan dalam array. Tentu saja, kami menggunakan traversal untuk mengeluarkan konten dalam array*/ system.out.println ("-1. Traversal ke array dengan mencetak-"); untuk (int i = 0; i <is.length; i ++) {System.out.print (IS [i]+""); } System.out.println (); System.out.println ("-2. Traversal ke array dengan mencetak-"); Objek obj = is; // Konversi array int satu dimensi ke objek System.out.println ("obj isArray:" + obj.getClass (). IsArray ()); untuk (int i = 0; i <array.getLength (obj); i ++) {int num = array.getint (obj, i); // Anda juga dapat menggunakan metode yang biasa digunakan ini untuk mendapatkan nilai dari posisi indeks yang sesuai // nilai objek = array.get (obj, i); // Jika array menyimpan tipe dasar, maka tipe pembungkus yang sesuai dengan tipe dasar System.out.print (num + ""); }}}Keluaran:
[I@14318bb [[i@14318bb] --1. Cetak array dengan melintasi array dengan cara konvensional- 58 55 93 61 61 29 68 0 22 7 --2. Cetak array dengan melintasi array dengan melintasi array dengan melintasi array- obj isarray: true 58 55 93 61 61 29 68 0 22 7 7
Contoh di atas pertama kali menciptakan array satu dimensi int, dan kemudian secara acak mengisi bilangan bulat 0 ~ 100. Kemudian, secara langsung output array melalui metode System.out.println () atau gunakan metode array. Array dikonversi ke daftar dan kemudian output, dan passing bukan hasil output yang kami harapkan. Selanjutnya, konten dalam array adalah output dalam metode traversal array reguler, dan kemudian memperlakukan int [] sebagai objek, dan menggunakan refleksi untuk melintasi kontennya. Class.isArray () dapat digunakan untuk menentukan apakah suatu objek adalah array. Jika itu adalah array, maka informasi yang relevan dari array diperoleh melalui java.lang.reflect.array, kelas alat yang mencerminkan array. Kelas ini menggunakan beberapa metode GET untuk mendapatkan panjang array, masing-masing versi yang digunakan untuk mendapatkan indeks yang sesuai dari array satu dimensi tipe dasar, metode umum untuk mendapatkan nilai (array objek, indeks int), metode untuk mengatur nilai, dan dua metode untuk membuat instance array. Melalui kelas Alat Refleksi Array, mudah untuk menggunakan refleksi array untuk menulis kode umum tanpa harus menilai jenis array dasar mana yang diberikan array yang diberikan.
paket cn.zq.array.reflect; impor java.lang.reflect.array; kelas publik newArrayInstance {public static void main (string [] args) {objek o = array.newinstance (int.class, 20); int [] is = (int []) o; System.out.println ("is.length =" + is.length); Objek o2 = array.newinstance (int.class, 10, 8); int [] [] iss = (int [] []) o2; System.out.println ("iss.length =" + iss.length + ", ISS [0] .lenght =" + ISS [0] .length); }} is.length = 20 iss.length = 10, iss [0] .lenght = 8 Array telah melewati 2 metode untuk membuat array
Object newInstance (class <?> ComponentType, int length), Buat array dengan panjang yang ditentukan berdasarkan kelas yang disediakan. Jika int.class disediakan seperti di atas, panjangnya 10, yang setara dengan int baru [10];
Object newInstance (class <?> ComponentType, int ... dimensi), membuat array berdasarkan kelas dan dimensi yang disediakan. Dimensi parameter variabel digunakan untuk menentukan panjang setiap dimensi array. Seperti pada contoh di atas, itu setara dengan menciptakan array dua dimensi int [10] baru [8], tetapi tidak dapat membuat array multi-dimensi dengan panjang yang berbeda dari masing-masing dimensi. Melalui metode pertama membuat array, Anda dapat membuat array seperti ini. Objek o = array.newinstance (int []. Class, 20) dapat digunakan untuk membuat array dua dimensi, yang setara dengan objek o = int baru [20] [];
Tentu saja, jarang menggunakan contoh di atas untuk membuat array, tetapi sebenarnya berlebihan. Mengapa tidak membuat array secara langsung melalui yang baru? Refleksi menciptakan array tidak hanya lebih cepat dari yang baru, tetapi juga program yang ditulis tidak mudah dibaca, jadi itu tidak sekecil baru. Bahkan, sangat jarang membuat array melalui refleksi. Jenis kebutuhan abnormal apa yang ada untuk menggunakan refleksi untuk membuat array!
Karena beberapa hambatan ditemui ketika mengeluarkan serangkaian tipe dasar, berikut ini akan menggunakan refleksi array untuk mengimplementasikan kelas alat untuk mencapai output yang diinginkan:
paket cn.zq.util; impor java.io.bytearrayoutputStream; impor java.io.printstream; impor java.lang.reflect.array; cetak kelas publik {public static void print (objek obj) {print (obj, system.out); } public static void print (objek obj, printStream out) {out.println (getPrintString (obj)); } public static void println () {print (System.out); } public static void println (printStream out) {out.println (); } public static void printnb (objek obj) {printnb (obj, system.out); } public static void printnb (objek obj, printStream out) {out.print (getPrintString (obj)); } public static printStream format (format string, objek ... objek) {return format (System.out, format, objek); } public static printStream format (printStream out, format string, objek ... objek) {objek [] handleObjects = objek baru [objeks.length]; untuk (int i = 0; i <objects.length; i ++) {objek objek = objek [i]; if (object == null || isPrimitiveWrapper (objek)) {handleObjects [i] = objek; } else {bytearrayoutputStream bos = bytearrayoutputStream () baru; PrintStream PS = printStream baru (BOS); printnb (objek, ps); ps.close (); handleObjects [i] = string baru (bos.tobyteArray ()); }} out.format (format, handleObjects); kembali; } /*** Tentukan apakah objek yang diberikan adalah kelas pembungkus dari tipe dasar. * @param o Objek objek yang diberikan * @return jika itu adalah kelas pembungkus tipe primitif, kembalikan ya, jika tidak, kembalikan No. */ private static boolean isPrimitiveWrapper (objek o) {return o instance dari void || o Contoh Boolean || o contoh karakter || o Contoh byte || o contoh pendek || o Contoh Integer || o Contoh panjang || o contoh dari float || o Contoh ganda; } public static String getPrintString (objek obj) {stringBuilder result = new stringBuilder (); if (obj! = null && obj.getClass (). isArray ()) {result.append ("["); int len = array.getLength (obj); untuk (int i = 0; i <len; i ++) {Nilai objek = array.get (obj, i); result.append (getPrintString (nilai)); if (i! = len - 1) {result.append (","); }} result.append ("]"); } else {result.append (string.valueof (obj)); } return result.toString (); }}Kelas alat cetak di atas menyediakan beberapa metode statis praktis untuk output, dan menyediakan beberapa versi kelebihan beban. Anda dapat menulis beberapa versi kelebihan beban berdasarkan preferensi pribadi Anda, mendukung pencetakan jenis dasar array satu dimensi dan array multi-dimensi. Lihat contoh pengujian alat cetak berikut:
paket cn.zq.array.reflect; impor statis cn.zq.util.print.print; impor java.io.printstream; Impor statis cn.zq.util.print.*; kelas publik printTest {static class person {private static int counter; private final int id = counter ++; Public String ToString () {return getClass (). getsImplename () + id; }} public static void main (string [] args) melempar Exception {print ("-print non-array--"); cetak (objek baru ()); print ("-cetak array satu dimensi tipe dasar--"); int [] adalah = int baru [] {1, 22, 31, 44, 21, 33, 65}; cetak; print ("-cetak array dua dimensi tipe dasar--"); int [] [] iss = new int [] [] {{11, 12, 13, 14}, {21, 22,}, {31, 32, 33}}; cetak (ISS); print ("-cetak array satu dimensi tipe non-base--"); Orang [] orang = orang baru [10]; untuk (int i = 0; i <orang. } cetak (orang); cetak (orang); print ("-cetak array dua dimensi tipe non-primitif--"); Orang [] [] orang2 = orang baru [] [] {{orang baru ()}, {orang baru (), orang baru ()}, {orang baru (), orang baru (), orang baru (), orang baru (),},}; cetak (orang2); print ("-cetak array kosong--"); cetak (int baru [] {}); print ("-cetak array dengan nilai nol--"); Objek [] objek = objek baru [] {orang baru (), null, objek baru (), integer baru (100)}; cetak (objek); print ("-cetak array dua dimensi untuk kasus khusus--"); Objek [] [] objek2 = objek baru [3] []; objek2 [0] = objek baru [] {}; objek2 [2] = objek; cetak (objek2); print ("-output hasil array satu dimensi ke file--"); PrintStream out = printStream baru ("out.c"); coba {print (iss, out); } akhirnya {out.close (); } print ("-Format output--"); format (" %-6d %s %b %s", 10086, "adalah", true, iss); /** * Beberapa metode yang umum digunakan dari kelas alat cetak tercantum di atas, * Ada juga beberapa metode yang tidak terdaftar, silakan periksa sendiri. */}} Keluaran:
--Print non-array -- java.lang.Object@61de33 --Print one-dimensional array of basic types -- [1, 22, 31, 44, 21, 33, 65] --Print two-dimensional array of basic types -- [[11, 12, 13, 14], [21, 22], [31, 32, 33]] --Print one-dimensional array of non-base type -- [Person0, Person1, Person2, Person3, Person4, Person5, Person6, Person7, Person8, Person9]-mencetak array dua dimensi dari tipe non-base-[[person10], [person11, person12], [person13, person14, person15]]-jejak array kosong-[] --print array dengan nilai-nilai nol-[orang15] [nol, [langgar nol. --Print Array Dua Dimensi dalam Kasus Khusus-[[], Null, [Person16, Null, Java.lang.object@ca0b6, 100]]-Cetak hasil dari array satu dimensi ke file--output format-10086 benar [[11, 12, 13, 14], [21, 22], 32], [11, [11, 12, 13, [21, [21, 22], [[11, 12, 12, 13, [21, [21, 22], [11, 32], [11, 12, 12, 32], [11, 12, 32], [11, 12, 32], [11, 12, 32], [11, 12.
File output:
Dapat dilihat bahwa kelas alat cetak sudah memiliki kemampuan untuk mencetak tipe dasar array satu dimensi dan array multi-dimensi. Secara umum, kelas alat di atas cukup praktis, agar tidak secara manual menulis kode setiap kali Anda ingin melihat konten dalam array. Itu akan terlalu merepotkan. Cukup gunakan kelas alat cetak di masa depan. Betapa nyamannya.
Kelas alat di atas memang bekerja dengan sangat baik, tetapi jika ada persyaratan: memberi Anda array (dan mungkin wadah lainnya), Anda dapat membuat daftar untuk saya. Jadi apa yang harus kita lakukan? Faktanya, array. Daftar tidak selalu mendapatkan hasil yang kami harapkan. Meskipun Java5 menambahkan obat generik, ia memiliki keterbatasan dan tidak dapat sama umumnya dengan template C ++. Justru karena ada tipe dasar di Java. Bahkan jika ada mekanisme pembungkus otomatis, itu tidak dapat digunakan dengan obat generik. Jenis parameter harus dari tipe tertentu, bukan tipe dasar. Ini solusi untuk Anda sendiri:
paket cn.zq.util; impor java.lang.reflect.array; impor java.util.arraylist; impor java.util.arrays; impor java.util.enumeration; impor java.util.iterator; impor java.util.list; impor java.util.map; Collection Collection PublicUtils {Public Static List <?> Aslist (Object Obj) {return convertTolist (makeIterator (obj)); } Daftar public static <T> <T> ConvertTolist (iterator <T> iterator) {if (iterator == null) {return null; } Daftar <T> Daftar = ArrayList baru <T> (); while (iterator.hasnext ()) {list.add (iterator.next ()); } daftar pengembalian; } @SuppressWarnings ({"rawTypes", "Uncecked"}) public static iterator <?> MakeIterator (objek obj) {if (instance Iterator) {return (iterator <?> Obj; } if (obj == null) {return null; } if (obj instance dari peta) {obj = ((peta <?,?>) obj) .Entryset (); } Iterator <?> Iterator = null; if (obj instance dari iterable) {iterator = ((iterable <?>) obj) .iterator (); } lain jika (obj.getClass (). isArray ()) {// objek [] objs = (objek []) obj; // atau array tipe primitif tidak dapat dikonversi seperti daftar arraylist ini = ArrayList baru (array.getLength (OBJ)); untuk (int i = 0; i <array.getLength (obj); i ++) {list.add (array.get (obj, i)); } iterator = list.iterator (); } lain jika (obj instance dari enumeration) {iterator = new enumerationIterator ((enumeration) obj); } else {iterator = arrays.aslist (obj) .iterator (); } return iterator; } Public Static Class EnumerationIterator <T> mengimplementasikan Iterator <T> {enumeration private <T> Pencacahan; enumerationiterator publik (enumerasi <T> enumeration) {this.enumeration = enumeration; } public boolean hasnext () {return enumeration.hasmoreElements (); } public t next () {return enumeration.NextElement (); } public void remove () {throw new UnsupportedOperationException (); }}}Kode Uji:
paket cn.zq.array.reflect; impor java.util.iterator; impor java.util.list; impor cn.zq.array.reflect.printtest.person; impor cn.zq.util.collectionutils; Public Class Collectionutilstest {public static void main (string [] args) {system.out.println ("--- array tipe satu dimensi dasar--"); int [] nums = {1, 3, 5, 7, 9}; Daftar <?> Daftar = collectionutils.aslist (nums); System.out.println (daftar); System.out.println ("-bukan array tipe satu dimensi dasar--"); Orang [] orang = orang baru [] {orang baru (), orang baru (), orang baru (),}; Daftar <Son> personList = (Daftar <Fon>) collectionutils.aslist (orang); System.out.println (Personlist); System.out.println (Personlist); System.out.println ("-iterator--"); Iterator <fone> iterator = personlist.iterator (); Daftar <Fon> personList2 = (Daftar <Fon>) collectionS.aslist (iterator); System.out.println (personList2); }}Keluaran:
--Menik tipe satu dimensi tipe-[1, 3, 5, 7, 9]-Array tipe satu dimensi tipe-basic-- [Person0, Person1, Person2] --Iterator-- [Person0, Person1, Person2]
Di Perpustakaan Kelas Kontainer Java, dapat dibagi menjadi koleksi, peta, dan array. Karena iterator (dan enumerasi antarmuka warisan awal) adalah antarmuka umum dari semua kontainer dan antarmuka pengumpulan berasal dari iterable (iterator antarmuka ini akan mengembalikan iterator), situasi ini diproses satu per satu dalam metode MakeIterator. Untuk jenis peta, hanya metode entriSet () yang perlu dipanggil. Untuk kelas yang mengimplementasikan antarmuka Iterable (termasuk koleksi), hubungi iterator () untuk secara langsung mendapatkan objek Iterator. Untuk jenis enumerasi, gunakan EnumerationIterator Adapter untuk adaptasi. Untuk array, gunakan refleksi array untuk melintasi array menjadi arraylist, dan hubungi metode arrays.aslist () untuk membuat daftar untuk jenis lain. Collectionutils juga menyediakan beberapa metode lain untuk dikonversi, dan Anda dapat menambahkan metode yang Anda butuhkan sesuai kebutuhan.
Ringkasan: Refleksi array memberikan metode yang lebih nyaman dan fleksibel untuk desain di mana array dapat muncul, agar tidak menulis pernyataan penilaian yang lebih merepotkan. Fleksibilitas ini membayar harga kinerja, dan benar -benar tidak perlu menggunakan refleksi array ketika refleksi array tidak diperlukan sama sekali. Apakah akan menggunakan refleksi array berbeda dalam pengembangan aktual. Pilih apakah akan menggunakan refleksi array sesuai dengan kebutuhan. Cara terbaik adalah mengeksplorasi jalan melalui latihan, menulis dalam cara Anda berpikir, dan terus meningkat dalam praktik.