反射相信大家平時學習時用的不多但見的很多,特別是各種開源框架中,到此都是反射。編譯時加載類是靜態加載、運行時加載類是動態加載,動態加載可通過反射實現。
一、定義
- 反射機制是在運行時,對於任意一個類,都能夠知道這個類的所有屬性和方法;
- 對於任意一個對象,都能夠調用它的任意一個方法。
- 在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);
}
}