Java學習筆記54(反射詳解)
阿新 • • 發佈:2018-01-27
pos code 重名 java學習筆記 spl catch 兩種 new fig
反射概念:
java反射機制是在運行狀態中,對於任意一個類,都能知道所有屬性和方法
對於任意一個對象都能調用它的任意一個方法和屬性,這種動態獲取和調用的功能稱為java的反射機制
實際作用:
已經完成一個java程序,但是想再添加新功能,又不能修改源碼,這時候就用到反射機制了
獲取class文件的三種方式:
簡單地自定義一個Person類:
package demo; public class Person { public String name; private int age; public Person() { } publicView CodePerson(String name, int age) { this.name = name; this.age = age; } private Person(int age, String name) { this.name = name; this.age = age; } public void eat() { System.out.println("人吃飯"); } public void sleep(String s, int a, doubled) { System.out.println("人在睡覺" + s + "....." + a + "....." + d); } private void playGame() { System.out.println("人在打遊戲"); } public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } public String getName() { return name; }public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
獲取person類class文件(三種方法本質上獲取的是同一個對象):
package demo; public class ReflectDemo { public static void main(String[] args) { //1.對象獲取 Person p = new Person(); Class c = p.getClass(); System.out.println(c); //2.類名獲取 Class c1 = Person.class; System.out.println(c1); System.out.println(c==c1);//true System.out.println(c.equals(c1));//true //只存在一個class文件,兩種方式都是獲得同一個對象 //3.Class類的靜態方法,參數註意帶著包名防止重名 try { Class c2 = Class.forName("demo.Person"); System.out.println(c2); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
反射獲取空參構造方法:
package demo; import java.lang.reflect.Constructor; public class ReflectDemo { public static void main(String[] args) throws ClassNotFoundException { Class c = Class.forName("demo.Person"); function1(c); function2(c); } private static void function1(Class c) { // 獲得所有公共權限(public)的構造器 Constructor[] cons = c.getConstructors(); for (Constructor con : cons) { System.out.println(con); // 輸出: // public demo.Person(java.lang.String,int) // public demo.Person() } } public static void function2(Class c) { // 獲取空參構造器並執行(toString方法) try { Constructor con = c.getConstructor(); Object obj = con.newInstance(); System.out.println(obj); // 輸出:Person [name=null, age=0] } catch (Exception ex) { ex.printStackTrace(); } } }
反射獲取有參構造方法:
package demo; import java.lang.reflect.Constructor; public class ReflectDemo { public static void main(String[] args) throws Exception { Class c = Class.forName("demo.Person"); Constructor con = c.getConstructor(String.class, int.class); System.out.println(con); Object object = con.newInstance("張三", 20); System.out.println(object); } } /*輸出: public demo.Person(java.lang.String,int) Person [name=張三, age=20] */
發現上邊的方式代碼量偏大,快捷一些的方式:
前提:被反射的類,必須具有空參構造方法,且構造方法權限必須是public
package demo; public class ReflectDemo { public static void main(String[] args) throws Exception { Class c = Class.forName("demo.Person"); // Class類中定義方法, T newInstance() 直接創建被反射類的對象實例 Object obj = c.newInstance(); System.out.println(obj); } }
反射獲取私有構造方法(日常開發不建議使用,這種方法了解即可):
package demo; import java.lang.reflect.Constructor; public class ReflectDemo { public static void main(String[] args) throws Exception { Class c = Class.forName("demo.Person"); // 獲取所有構造方法(包括私有的) // Constructor[] cons = c.getDeclaredConstructors(); Constructor con = c.getDeclaredConstructor(int.class, String.class); con.setAccessible(true);// 取消權限,破壞了封裝性 Object object = con.newInstance(18, "張三"); System.out.println(object); // Person [name=張三, age=18] } }
反射獲取類的成員變量並修改:
package demo; import java.lang.reflect.Constructor; import java.lang.reflect.Field; public class ReflectDemo { public static void main(String[] args) throws Exception { Class c = Class.forName("demo.Person"); // 獲得所有public成員 Field[] fields = c.getFields(); // 獲得所有成員變量 Field[] fields2 = c.getDeclaredFields(); for (Field field : fields2) { System.out.println(field); } // 獲取指定成員變量並修改 Field field = c.getField("name"); Object object = c.newInstance(); field.set(object, "張三"); System.out.println(object); // 輸出:Person [name=張三, age=0] } }
反射獲取成員方法並執行:
package demo; import java.lang.reflect.Method; public class ReflectDemo { public static void main(String[] args) throws Exception { Class c1 = Class.forName("demo.Person"); // 獲得所有public方法 /* * Method[] methods = c1.getMethods(); * for(Method method:methods){ * System.out.println(method); } */ // 獲取指定方法運行(空參) Method method = c1.getMethod("eat"); Object obj = c1.newInstance(); method.invoke(obj); // 輸出:人吃飯 // 有參 Method method1 = c1.getMethod("sleep", String.class, int.class, double.class); Object obj1 = c1.newInstance(); method1.invoke(obj1, "休息", 10, 10.11); // 輸出:人在睡覺休息.....10.....10.11 // 可以利用前面提到的方法暴力運行私有方法 } }
反射的泛型擦除:
package demo; import java.lang.reflect.Method; import java.util.ArrayList; public class ReflectTest { public static void main(String[] args) throws Exception { ArrayList<String> array = new ArrayList<String>(); // 通常添加方式 array.add("a"); array.add("1"); Class class1 = array.getClass(); Method method = class1.getMethod("add", Object.class); method.invoke(array, 100); method.invoke(array, 666.666); method.invoke(array, 0.1); System.out.println(array); // 輸出:[a, 1, 100, 666.666, 0.1] } }
反射實現通過配置文件運行:
有時候想改源碼,但是不能改源碼,可以這樣做:
自定義三個類:
package demo; public class Person { public void eat(){ System.out.println("人在吃飯"); } }View Code
package demo; public class Student { public void study(){ System.out.println("學生在學習"); } }View Code
package demo; public class Worker { public void job(){ System.out.println("上班族在工作"); } }View Code
配置文件:config.properties
#className=demo.Student
#methodName=study
className=demo.Person
methodName=eat
#className=demo.Worker
#methodName=job
測試類:
package demo; import java.io.FileReader; import java.lang.reflect.Method; import java.util.Properties; /* * 調用Person方法,調用Student方法,調用Worker方法 * 類不清楚,方法也不清楚 * 通過配置文件實現此功能 * 運行的類名和方法名字,以鍵值對的形式,寫在文本中 * 運行哪個類,讀取配置文件即可 * 實現步驟: * 1. 準備配置文件,鍵值對 * 2. IO流讀取配置文件 Reader * 3. 文件中的鍵值對存儲到集合中 Properties * 集合保存的鍵值對,就是類名和方法名 * 4. 反射獲取指定類的class文件對象 * 5. class文件對象,獲取指定的方法 * 6. 運行方法 */ public class Test { public static void main(String[] args) throws Exception{ //IO流讀取配置文件 FileReader r = new FileReader("config.properties"); //創建集合對象 Properties pro = new Properties(); //調用集合方法load,傳遞流對象 pro.load(r); r.close(); //通過鍵獲取值 String className = pro.getProperty("className"); String methodName = pro.getProperty("methodName"); //反射獲取指定類的class文件對象 Class c = Class.forName(className); Object obj = c.newInstance(); //獲取指定的方法名 Method method = c.getMethod(methodName); method.invoke(obj); } }
Java學習筆記54(反射詳解)