Java的反射機制和使用
文章目錄
反射相信大家平時學習時用的不多但見的很多,特別是各種開源框架中,到此都是反射。編譯時載入類是靜態載入、執行時載入類是動態載入,動態載入可通過反射實現。
一、定義
- 反射機制是在執行時,對於任意一個類,都能夠知道這個類的所有屬性和方法;
- 對於任意一個物件,都能夠呼叫它的任意一個方法
- 在java 中,只要給定類的名字,那麼就可以通過反射機制來獲得類的所有資訊。
二、功能
- 在執行時判定任意一個物件所屬的類;
- 在執行時建立物件;
- 在執行時判定任意一個類所具有的成員變數和方法;
- 在執行時呼叫任意一個物件的方法;
- 生成動態代理。
如:Class.forName(‘com.mysql.jdbc.Driver.class’);//載入MySql 的驅動類。這就
是反射,現在很多框架都用到反射機制,hibernate,struts ,spring等框架都是用反射機制實
現的。
三、反射的實現方式
1、Class.forName(“類的路徑”)
2、類名.class
3、物件名.getClass()
4、如果是基本型別的包裝類,則可以通過呼叫包裝類的Type 屬性來獲得該包裝類的Class 物件。
例如:Class<?> clazz = Integer.TYPE;
這個Class也稱類型別,也就是說每一個 Class它也是個物件(萬物皆物件),所以clazz是一個類物件
四、實現反射的類
1、Class:它表示正在執行的Java 應用程式中的類和介面。
2、Field:提供有關類或介面的屬性資訊,以及對它的動態訪問許可權,如private、public等許可權。
3、Constructor:提供關於類的單個構造方法的資訊以及對它的訪問許可權
4、Method:提供關於類或介面中某個方法資訊。
注意:Class類是Java反射中最重要的一個功能類,所有獲取物件的資訊(包括:方法/屬性/構造方法/訪問許可權)都需要它來實現。
五、Java動態載入類使用場景
例如,你可以傳一個引數,執行時判斷引數為1載入A類(通過反射),引數為2載入B類,然後A、B類都實現一個介面C,這樣就可以面向介面程式設計啦(主程式面向C程式設計,後面還可以繼續新增C介面的實現類來擴充套件,符合開閉原則)
六、反射機制的優缺點?
優點:
(1)能夠執行時動態獲取類的例項,大大提高程式的靈活性(由各框架中到此是反射可見)。
(2)與Java 動態編譯相結合,可以實現無比強大的功能。
缺點:
(1)使用反射的效能較低。java 反射是要解析位元組碼,將記憶體中的物件進行解析。
七、下面不是概念,乾貨來啦!
這是我寫的一個反射工具測試類
package com.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
/**
* @author Guoming Zhang
* @Description Class類的使用
* @Date 2018/12/20
*/
class Foo {
void print(int a, int b) {
System.out.println(a + b);
}
void print(String a, String b) {
System.out.println(a + " , " + b);
}
}
public class ReflectUtil {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
/***********一、反射Class類的使用*****************/
//useOfClass();
/***********二、反射獲取方法資訊******************/
//printMethodMessage(new String());
/***********三、反射獲取成員變數********************************/
//printFiledMessage(new String());
/***********四、反射獲取成員建構函式********************************/
//printConstructorMessage(new String());
/***********五、方法的反射呼叫********************************/
//printMethodInvoke();
/***********六、通過反射驗證泛型擦除**********************************************/
printFanXingClear();
}
//反射Class類的使用
public static void useOfClass() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Foo foo = new Foo();
Class c1 = Foo.class;
Class c2 = foo.getClass();
Class c3 = Class.forName("com.reflect.Foo");
System.out.println(c1 == c2);//true
System.out.println(c2 == c3);//true
//可以通過類型別建立物件
Foo myFoo = (Foo) c3.newInstance();
myFoo.print(1, 2); // "你好啊朋友"
}
//反射獲取方法資訊
public static void printMethodMessage(Object obj) {
//獲取位元組碼,或稱類型別
Class c = obj.getClass();
System.out.println("類的名稱為:" + c.getName());//c.getSimpleName()則不帶包名
/**
* Method : 方法物件,方法也是一個物件!萬物皆物件
* getMethods獲取所有public方法,包括父類繼承而來的
* getDeclareMethods() 獲取所有自己宣告的方法,任何訪問許可權,不包父類的
*/
Method[] ms = c.getMethods();//或c.getDeclareMethods()
for (int i = 0; i < ms.length; i++) {
//得到方法的返回值型別的類型別,如方法返回一個String,則為String.class
Class returnType = ms[i].getReturnType();
//獲得類型別的不帶包名的名字,getName()則為帶包名的名字
System.out.print(returnType.getSimpleName() + " ");
//得到方法的名稱
System.out.print(ms[i].getName() + " (");
//獲取引數型別的類型別
Class[] paramTypes = ms[i].getParameterTypes();
for (Class clazz : paramTypes) {
System.out.print(clazz.getSimpleName() + " , ");
}
System.out.println(" )");
}
}
//反射獲取成員變數
public static void printFiledMessage(Object obj) {
//獲取位元組碼,或稱類型別
Class c = obj.getClass();
System.out.println("類的名稱為:" + c.getName());//c.getSimpleName()則不帶包名
/**
* 成員變數也是物件,萬物皆物件
* getFields()獲取所有public的成員變數資訊,包括父類的
* getDeclaredFields()獲取自己宣告的所有許可權的成員變數資訊
*/
Field[] fs = c.getDeclaredFields();
for (Field field : fs) {
//獲得成員變數的型別的類型別
Class fieldType = field.getType();
//獲得成員變數型別的不帶包名的名稱
String typeName = fieldType.getSimpleName();
//獲得成員變數的名稱
String fieldName = field.getName();
System.out.println(typeName + " " + fieldName);
}
}
//反射獲取成員建構函式
public static void printConstructorMessage(Object obj) {
//獲取位元組碼,或稱類型別
Class c = obj.getClass();
System.out.println("類的名稱為:" + c.getName());//c.getSimpleName()則不帶包名
/**
* 建構函式也是物件,萬物皆物件!
* getConstructors獲取所有public的建構函式,包括父類的
* getDeclaredConstructors()獲取自己宣告的所有許可權的建構函式,不包父類的
*/
Constructor[] cs = c.getDeclaredConstructors();
for (Constructor constructor : cs) {
System.out.print(constructor.getName() + " (");
//獲取建構函式的引數列表
Class[] paramTypes = constructor.getParameterTypes();
for (Class clazz : paramTypes) {
System.out.print(clazz.getSimpleName() + " ,");
}
System.out.println(" )");
}
}
//方法的反射呼叫
public static void printMethodInvoke() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Foo foo = new Foo();
//要獲取一個類的方法,先要獲取類型別(或稱位元組碼)
Class c = foo.getClass();
/**
* getMethod()獲取的是所有public方法,包父類的
* getDeclaredMethod()獲取自己宣告的所有許可權方法,不包父類的
*/
Method method = c.getDeclaredMethod("print", int.class, int.class);
//方法的反射呼叫 用Method物件來進行呼叫
//如果方法沒有返回值返回null,否則返回具體的返回值
Object o = method.invoke(foo, 10, 20);
}
//通過反射驗證泛型擦除
public static void printFanXingClear() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
ArrayList<String> list = new ArrayList<>();
list.add("你好");
/**
* list.add(45);是不是會出錯?
* 但是!泛型是防止錯誤輸入的,編譯後泛型擦除,不存在泛型
* 下面在執行時通過反射證明
*/
Class clazz = list.getClass();
Method method = clazz.getMethod("add",Object.class);
method.invoke(list,new Integer(45));
System.out.println(list);
}
}