前幾天上REST課,因為涉及到Java的反射機制,之前看過一直沒有用過,有些遺忘了,周末找了些資料來重新學習,現在總結一下,加深印象。
什么是反射機制?
參考百度百科對java反射機制的定義:
“JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意方法和屬性;這種動態獲取信息以及動態調用對象方法的功能稱為java語言的反射機制。”
我們通過一些例子,更好理解反射機制。
Class類
我們知道Java是一門面向對象語言,在Java的世界里,萬物皆為對象,比如我們有一個Person類:
public class Person { }
我們創建一個Person類的實例,Person person = new Person(); 那么這個person,就是Person類的實例對象。
那么既然萬物都為對象,所以類也是對象。
類是什么的對象呢?類是Class類的對象,表示方式有三種:
//第一種,任何一個類都有一個隱含的靜態成員變量class Class c1 = Person.class; //第二種,已經知道該類的對象,通過getClass()獲得 Class c2 = person.getClass(); //第三種,Class類的forName()方法 Class c3 = Class.forName("Person"); //這里,c1,c2,c3都是Class類的實例,我們稱c1, c2 ,c3為Person類的類類型 //不難看出,c1 == c2結果是true, c2 == c3結果也是true
通過類的類類型,我們經常會用到的方法就是newInstance()方法,通過該方法可以創建該類的實例:
Person personA = new Person(); //直接new一個實例 Person personB = Person.class.newInstance(); //通過newInstance()方法獲得Person的實例 //在學習JAVAEE時候,newInstance()方法我們最常見於獲取數據庫驅動 Class.forName("com.mysql.jdbc.Driver").newInstance(); //需要注意的是,在使用newInstance()方法的前提是該類必須要有無參構造方法
動態加載類:
編譯時刻加載類稱為靜態加載,運行時刻加載類稱為動態加載,使用new方法新建實例即為靜態加載類,在編譯時候就要加載全部類。這里我們舉一個例子:
為了更好的區分編譯和運行,我們不適用IDE工具,而使用記事本來實現這個例子:
//我們舉女媧造人的例子,創建一個CreatePerson類 public class CreatePerson { public static void main(String[] args) { if(args[0].equalsIgnoreCase("man")) { //如果從命令行傳入的參數為man 則創建Man的實例並調用say()方法 new Man().say(); } if(args[0].equalsIgnoreCase("woman")) { //如果從命令行傳入的參數為woman 則創建Woman的實例並調用say()方法 new Woman().say(); } } } //CreatePerson類用到了2個類,分別是Man和Woman。 //但是我們現在只創建Man類 public class Man { public void say() { System.out.println("I am a man !"); } }
我們在CMD中編譯CreatePerson,看看會發生什么:
提示我們找不到Woman這個類。這就是靜態加載,在編譯時刻需要加載全部類。那么問題來了,如果我要寫一個程序,里面有100個功能,這100個功能分別由100個類實現,那么一旦缺少一個類,這整個程序是不是就不能用了。
為了解決這個問題,我們可以使用動態加載。我們把上面這個例子改一下:
//創建一個Person接口 public interface Person { void say(); } //修改Man類,繼承Person接口 public class Man implements Person{ public void say() { System.out.println("I am a man !"); } } //修改CreatePerson類,實現動態加載類 public class CreatePerson{ public static void main(String[] args) { try{ //動態加載類 Class c = Class.forName(args[0]); //通過類的類類型,創建該類實例對象 Person person = (Person)c.newInstance(); person.say(); } catch(Exception e) { e.printStackTrace(); } } }
這個時候,我們再進行編譯:
沒有報錯了,那么我們繼續把Man編譯,再運行一下:
如果我們在命令行中輸入的參數是Woman呢?
拋出ClassNotFoundException異常。因為我們並沒有創建Woman類,所以如果以后需要創建Woman類實例,我們只需要新建一個Woman類,並實現Person接口就行了。
通過反射機制,獲得類的信息
在Java反射機制的定義中有:JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法。下面是一個實例:
import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; public class ClassUtil { /** * 獲取成員函數的信息 */ public static void getClassMethodMessage(Object obj) { Class c = obj.getClass(); //獲取類的名稱 //System.out.println(c.getName()); /** * * Method類,方法對象 *getMethods方法獲得所有public的方法,包括繼承而來 *getDeclaredMethods是獲取該類自己的聲明的方法 */ Method[] ms = c.getMethods(); for(int i = 0; i < ms.length; i++) { //得到方法的返回值類型的類類型 Class returnType = ms[i].getReturnType(); System.out.print(returnType.getName() + " "); //得到方法的名稱 System.out.print(ms[i].getName() + "("); //獲取參數類型 Class[] paramTypes = ms[i].getParameterTypes(); for(Class class1:paramTypes) { System.out.print(class1.getName() + ","); } System.out.println(")"); } } public static void getFieldMessage(Object obj) { Class c = obj.getClass(); /** * 成員變量也是對象 * java.lang.reflect.Field * */ Field[] fs = c.getDeclaredFields(); for(Field field:fs) { //得到成員變量類型的類類型 Class fieldType = field.getType(); String typeName = fieldType.getName(); //得到成員變量的名稱 String fieldName = field.getName(); System.out.println(typeName + " " + fieldName); } } public static void printConMessage(Object obj) { Class c = obj.getClass(); /** * 構造函數也是對象 * */ Constructor[] cs = c.getConstructors(); for (Constructor constructor : cs) { System.out.print(constructor.getName() + "("); //獲取構造函數參數列表------>參數列表的參數類型 Class[] paramType = constructor.getParameterTypes(); for (Class class1 : paramType) { System.out.print(class1.getName() + ","); } System.out.println(")"); } } public static void main(String[] args) { String s = "hello"; getFieldMessage(s); ClassUtil.printConMessage(s); getClassMethodMessage(s); } }
方法的反射操作
可以通過方法的反射操作實現方法的調用:
import java.lang.reflect.Method; public class MethodDemo1 { public static void main(String[] args) { //要獲取print(int, int) //要獲取類的方法就要獲取類的信息,獲取類的信息就要獲取類的類類型 A a1 = new A(); Class c = a1.getClass(); //2,獲取方法 名稱和參數列表 //getMethod獲取的是public的方法 try { Method m = c.getDeclaredMethod("print", int.class,int.class); //方法的反射操作 //a1.print(10, 20);方法的反射操作,用m來進行方法調用和前者效果一致 Object obj = m.invoke(a1, 10,20);//如果方法有返回值返回值,沒有就null } catch (Exception e) { e.printStackTrace(); } } } class A { public void print(int a , int b) { System.out.println(a + b); } public void print(String a , String b) { System.out.println(a.toUpperCase() + "," + b.toUpperCase()); } }
以上就是我對Java反射機制的學習和理解,如果有錯誤,望指出,以便及時更正!謝謝!