反射是java的一個非常重要但又很容易被忽略的知識點,下面先讓我們來看看反射的基本使用:
a/新建Person類
public class Person { public String name; PRivate int age; public void setAge(int age) { this.age = age; } // 無參數 public void show() { System.out.println("這是一個人"); } // 一個參數 public void showNation(String nation) { System.out.println("國籍是------->" + nation); } // 多個參數 public void showNation(String name, String nation, int age) { this.name = name; this.age = age; System.out.println(name + "的國籍是------->" + nation + ",年齡是------>" + age); } @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]"; }}b/執行測試代碼import java.lang.reflect.Field;import java.lang.reflect.Method;import org.junit.Test;public class TestReflection { @Test public void test1() throws Exception { // 1、獲取運行時類 Class clazz = Class.forName("com.test.Person"); // 2、創建運行時類 // Person必須要有空參的構造函數,否則用newInstance實例化會報錯,構造器修飾符權限要足夠,最好要給類留個空參構造器、 Person person1 = (Person) clazz.newInstance(); // 3、操作屬性 // 3.1、公共屬性 Field field1 = clazz.getField("name"); field1.set(person1, "劉德華"); System.out.println(person1); // 3.2、操作私有屬性 Field field2 = clazz.getDeclaredField("age"); field2.setaccessible(true); field2.set(person1, 30); // 4、操作方法。 // 4.1、方法無參數 Method method1 = clazz.getMethod("show"); method1.invoke(person1); // 4.2、方法一個參數 Method method2 = clazz.getMethod("showNation", String.class); // (方法名,參數類型) method2.invoke(person1, "中國"); // (對象,實參) // 4.3、方法多個參數 Method method3 = clazz.getMethod("showNation", String.class, String.class, int.class); method3.invoke(person1, "張學友", "中國香港", 40); }}結果輸出:Person [name=劉德華, age=0]
這是一個人
國籍是------->中國
張學友的國籍是------->中國香港,年齡是------>40
1.注意Class類的特殊性 Java語言是先把Java源文件編譯成后綴為class的字節碼文件,然后通過ClassLoader機制把類文件加載到內存當中,最后生成實例執行的,在描述一個類是,Java使用了一個元類來對類進行描述,這就是Class類,他是一個描述類的類,所以注定Class類是特殊的 1.Class類無構造函數,Class對象加載時由JVM通過調用類加載器的 defineClass方法來構造Class對象 2.Class類還可以描述基本數據類型,由于基本類型并不是Java中的對象,它們 一般存在于棧,但Class仍然可以對它們進行描述,例如使用int.class 3.其對象都是單例模式,一個Class的實現對象描述一個類,并且只描述一個類 所以只要是該被描述的類所有對象都只有一個Class實例 4.Class類是Java反射的入口 2.適時選擇getDeclaredXXX和getXXX Class類中提供了很多getDeclaredXXX和getXXX的方法,請看以下例子
public static void main(String[] args) { Class cls = User.class; // 獲取類方法 cls.getDeclaredMethods(); cls.getMethods(); // 獲取類構造函數 cls.getDeclaredConstructors(); cls.getConstructors(); // 獲取類屬性 cls.getDeclaredFields(); cls.getFields(); } getXXX的方式是獲取所有公共的(public)級別的,包括從父類繼承的方法,而getDeclaredXXX的方式是獲取所有的,包括公共的(public),私有的(private),不受限與訪問權限,3.反射訪問屬性或方法時將Accessible設置為true 在調用構造函數或方法的invoke前檢查accessible已經是公認的寫法,例如以下代碼
public static void main(String[] args) throws Exception { Class cls = User.class; // 創建對象 User user = cls.newInstance(); // 獲取test方法 Method method = cls.getDeclaredMethod("test"); // 檢查Accessible屬性 if (!method.isAccessible()) { method.setAccessible(true); } method.invoke(user); }讀者可以嘗試獲取Class的getMethod,也就是公開的方法,再輸出isAccessible,可以看到輸出的其實也是false,其實因為accessible屬性的語義并不是我們理解的訪問權限,而是指是否進行安全檢查,而安全監察是非常消耗資源的,所以反射提供了Accessible可選項,讓開發者逃避安全檢查,有興趣的讀者可以查看AccessibleObject類觀察其源碼了解安全檢查. 4.使用forName動態加載類 forName相信各位讀者不會陌生,在使用JDBC時要動態加載數據庫驅動就是使用forName的方式進行加載,同時亦可以從外部配置文件中讀取類的全路徑字符串進行加載,在使用forName時,被加載的類就會被加載到內存當中,只會加載類,并不會執行任何代碼,而我們的數據庫驅動就是利用static代碼塊來執行操作的,因為當類被加載到內存中時,會執行static代碼塊 5.使用反射讓模板方法更強大 模板方法的定義是,定義一個操作的算法骨架,將某些步驟延遲到子類當中實現,而實現細節由子類決定,父類只決定骨架,以下是一個傳統模板方法的事例
public abstract class Test { public final void doSomething() { System.out.println("start..."); doInit(); System.out.println("end....."); } protectedabstractvoid doInit(); }此時子類只需要繼承Test類實現doInit()方法即可嵌入到doSomething中,現在我們有一個需求,若我在doSomething中需要調用一系列的方法才能完成doSomething呢?而且我調用方法的數量并不確定,只需遵從某些規則則可將方法添加到doSomething方法當中.請看以下代碼
public abstract class Test { public final void doSomething() throws Exception { Method[] methods = this.getClass().getDeclaredMethods(); System.out.println("start..."); for (Method method : methods) { if (this.checkInitMethod(method)) { method.invoke(this); } } System.out.println("end....."); } private boolean checkInitMethod(Method method) { // 方法名初始是否為init return method.getName().startsWith("init") // 是否為public修飾 && Modifier.isPublic(method.getModifiers()) // 返回值是否為void && method.getReturnType().equals(Void.TYPE) // 是否沒有參數 && !method.isVarArgs() // 是否抽象類型 && !Modifier.isAbstract(method.getModifiers()); } }看到上面的代碼,讀者是否有似曾相識的感覺?在使用Junit3時是不是只要遵守方法的簽名約定,就能成為測試類?使用這種反射可以讓模板方法更強大,下次需要使用多個方法在模板方法中時,不要創建多個抽象方法,嘗試使用以上方式 6.不要過分關注反射的效率 反射的效率是一個老生常談的問題,普通的調用方法,創建類,在反射的情況下需要調用諸多API才能實現,效率當然要比普通情況下低下,但在項目當中真正引起性能問題的地方,絕大數不會是由反射引起的,而反射帶給我們的卻是如此的美妙,當今的各系列開源框架,幾乎都存在反射的身影,而且大量存在更比比皆是,讓Java這個沉睡的美女活起來的,非反射莫屬
部分資料源自互聯網
新聞熱點
疑難解答