在Java編程思想(十五) —— 類型資訊之反射和Java編程思想(十六) —— 聯絡JVM再談Class,書上只用了3頁就講完了,還有講了那麼多Class的東西,接下來要從反射中怎麼用,自己結合API和其他資料再寫多一些。
樣本:Test.java
public class Test { public Test() { } public Test(int i) { System.out.println(i); } private void pri() {System.out.println("private");}public void pub() {System.out.println("public");}protected void pro() {System.out.println("protected");}private String pristr;public String pubstr;protected String pro;}
有不同的方法和域。
測試:
import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;public class Reflect {public static void main(String[] args) {try {//測試參數類型後面跟著三點的作用Test t = new Test();t.three();t.three("a","b","c");//輸出[] [a,b,c] 證明...表示的參數可以為空白或者是傳字元數組 即可傳可不傳 英文成為varargs//將Test裝載進命名空間 得到代表Test的Class對象引用Class c = Class.forName("Test");//Method類 拿到名為pub的方法try {Method one = c.getMethod("pub");try {//方法調用 invoke(Object obj, Object... args) obj為所要調用方法所屬的類 args為方法所傳的參數//所傳obj要為執行個體 ,不然會有object is not an instance of declaring classone.invoke(c.newInstance());} catch (IllegalAccessException e) {e.printStackTrace();} catch (IllegalArgumentException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();}} catch (NoSuchMethodException e1) {e1.printStackTrace();} catch (SecurityException e1) {e1.printStackTrace();}//這種返回的方法的數組 為公用方法 包括那些父類和父介面中繼承的方法Method[] method = c.getMethods();//返回的是該Class對象代表的類聲明的所有方法 這樣就包括public private protected方法 Method[] declaredmethod = c.getDeclaredMethods();Class[] array = c.getClasses();Field[] field = c.getFields();Field[] declaredfield = c.getDeclaredFields();for (int i = 0; i < array.length; i++) {System.out.println("Classes array is "+array[i].getName());}for (int i = 0; i < method.length; i++) {System.out.println("Methods array is "+method[i].getName());}//getReturnType拿到方法返回的屬性。for (int i = 0; i < declaredmethod.length; i++) {System.out.println("DeclaredMethods array is "+declaredmethod[i].getReturnType().getName()+" "+declaredmethod[i].getName());try {try {String s[] ={"1","2"}; //如果沒有設定 就會有Class Reflect can not access a member of class Test with //modifiers "private"異常//Constructor, Field, Method 為AccessibleObject的子類//有著存取控制的許可權 預設是false 會強制java進行 訪問檢查 所以private是禁止訪問的 Filed同理declaredmethod[i].setAccessible(true);System.out.println(declaredmethod[i].getName());if(declaredmethod[i].getName().equals("three")){System.out.println("true");//下面這條會報 wrong number of arguments異常 //這個問題是搞了我最久的東西 中文都是前篇一律的胡說八道//invoke(Object obj, Object... args) 數組是協變的 String[]被當成Object[]//String[] 被當成了整個參數 而string數組裡面的元素又不是object //而不是args的第一個參數,其實,我們String數組是Objec數組的第一個元素/*這是因為編譯器會把字串數組當作一個可變長度參數傳給對象o,而我們取得方法只有一個參數,所以就會出現wrong number ofarguments的異常,我們只要把字串數組強制轉換為一個Object對象就可以解決這個異常了 這個簡直就是放屁*///declaredmethod[i].invoke(c.newInstance(),s);declaredmethod[i].invoke(c.newInstance(),new Object[]{s});declaredmethod[i].invoke(c.newInstance(),(Object)new String[]{"1","2","3"});declaredmethod[i].invoke(c.newInstance(),(Object)null);declaredmethod[i].invoke(c.newInstance(),(Object)new String[]{});}else{declaredmethod[i].invoke(c.newInstance());}} catch (InstantiationException e) {e.printStackTrace();}} catch (IllegalAccessException e) {e.printStackTrace();} catch (IllegalArgumentException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}}//與Method同理,只不過Filed拿到的是域。for (int i = 0; i < field.length; i++) {System.out.println("Fields array is "+field[i].getName());}for (int i = 0; i < declaredfield.length; i++) {System.out.println("Declaredfieds array is "+declaredfield[i].getName());try {declaredfield[i].setAccessible(true);declaredfield[i].set(t, "a");} catch (IllegalArgumentException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}System.out.println(t.pro);System.out.println(t.pub);} catch (ClassNotFoundException e) {e.printStackTrace();}}}
基本所有的問題和屬性方法都在裡面,不過wrong number of arguments異常,是我查了最久的異常,靜下心來後看懂了。
這是因為編譯器會把字串數組當作一個可變長度參數傳給對象o,而我們取得方法只有一個參數,
所以就會出現wrong number of arguments的異常,我們只要把字串數組強制轉換為一個Object對象就可以解決這個異常了。
全部都是這個錯誤的答案,invoke(Object obj, Object... args) ,數組是協變的 String[]被當成Object[]。
String[] 被當成了整個參數 ,而string數組裡面的元素又不是object 。
而不是args的第一個參數,其實,我們String數組是Objec數組的第一個元素。
慢慢看就理解了。
後面補充:
Constructor<?>[] ct = Class.forName("Test").getConstructors();for (int i = 0; i < ct.length; i++) {System.out.println("Constructor:" + ct[i].getName());}System.out.println("new instance");try {ct[0].newInstance();ct[1].newInstance(123);} catch (InstantiationException | IllegalAccessException| IllegalArgumentException | InvocationTargetException e2) {e2.printStackTrace();}
Constructor就是構造器,不過不同於Class的newInstace方法,Constructor的newInstance(Object... initargs) 為varargs,即上面提到的三點。可以進行參數的傳入。
而有趣的一點是,Constructor數組是有序的,前提是你的構造方法寫在類裡面最前面,如果前面有其他方法,那麼數組的順序就不是按照排序而排了。
反射的作用,就是運行時檢查對象的類型,任意調用對象的方法(Spring和servlet中都用到了,注意到沒有,我們在xml配置,然後屬性會根據xml注入),同時可以知道方法參數和屬性。
雖然這算是打破了所謂的封裝性,private屬性,不過想想,沒有後門,語言豈不是死了。
對於反射,想瞭解跟多的可以看看國外的反射教程,http://www.programcreek.com/2013/09/java-reflection-tutorial/