一. 什么是反射
在運行狀態中,對於任意一個類,都能夠知道其所有屬性和方法,對於任意一個對象,都能夠調用其任意方法和屬性,這種動態獲取信息、動態調用方法的能力稱為Java語言的反射機制,大部分框架都有用到反射機制,了解反射的使用方法非常重要。
一個類通常包含了屬性、方法、構造函數等,而Java一般情況下是現有類再有對象,通過對象調用各種屬性和方法,而Java反射則是通過已有的對象,反過來得到其所屬類的相關信息,調用所屬類的相關方法。
二. 反射的基礎Class
2.1 Class類概述
我們知道在Java的世界中,萬事萬物皆對象。其實類本身也是對象,任何一個類都是Class類的實例對象。
//定義了一個SuperHero的類
public class SuperHero {}
如上面定義的SuperHero類,是類也是對象,
對象:SuperHero類是Class類的實例,Class類是SuperHero的類類型,故而為對象;
類:以類的方式創建,SuperHero本身可以調用SuperHero ironMan = new SuperHero ()
被實例化,ironMan 就是創建的實體,故而也是類。
Class類很特殊,它表示了某個類的類類型,被不可被繼承,每個類的Class對象僅有一個,Class類沒有公共構造函數。 相反, Class對象由Java虛擬機自動構建,因為加載了類,並且通過調用類加載器中的defineClass
方法,原始Java類型( boolean
, byte
, char
, short
, int
, long
, float
和double
),和關鍵字void
也表示為Class對象
//Class源碼,final修飾不可被繼承,構造函數是private的,不可手動實例化
public final class Class<T> {
private Class(ClassLoader loader) {
// Initialize final field for classLoader. The initialization value of non-null
// prevents future JIT optimizations from assuming this final field is null.
classLoader = loader;
}
}
public static void main(String[] args) {
try {
Class clazz1 = Class.forName("java.lang.Integer");
Class clazz2 = Class.forName("java.lang.Integer");
System.out.println(clazz1 == clazz2);
System.out.println(int.class);
System.out.println(void.class);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
運行結果為:
true
int
void
2.2 Class類對象獲取的三種方式
先定義一個類
package reflectdemo;
import java.io.Serializable;
/**
* 超級英雄類
*/
public class SuperHero implements Serializable {
public static final String ADDRESS = "earth";
private String id;
private String name;
private Integer age;
private String skill;
public SuperHero() {
}
public SuperHero(String id, String name, Integer age, String skill) {
this.id = id;
this.name = name;
this.age = age;
this.skill = skill;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getSkill() {
return skill;
}
public void setSkill(String skill) {
this.skill = skill;
}
public void print(){
System.out.println("超級英雄:" + this.name);
}
}
2.2.1 通過對象獲取Class
public static void main(String[] args) {
SuperHero ironMan = new SuperHero("1","鋼鐵俠",35, "戰甲");
Class clazz = ironMan.getClass();
System.out.println(clazz.getName());
}
輸出結果:
reflectdemo.SuperHero
2.2.2 通過類獲取Class
public static void main(String[] args) {
Class clazz = SuperHero.getClass();
System.out.println(clazz.getName());
}
輸出結果:
reflectdemo.SuperHero
2.2.3 傳入類路徑獲取Class
public static void main(String[] args) {
try {
Class clazz = Class.forName("reflectdemo.SuperHero");
System.out.println(clazz.getName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
輸出結果:
reflectdemo.SuperHero
三種創建方式:
第一種方式對象已經有了,所有的操作直接通過該對象進行即可,
第二種方式需要import將類引入,也不是常用的方式,
第三種僅需傳入類的路徑,即可得到類的相關信息,是最常用的方式。
2.2.4 獲取類信息的常用方法
public static void main(String[] args) {
try {
Class clazz = Class.forName("reflectdemo.SuperHero");
//獲取類名稱(含路徑)
System.out.println(clazz.getName());
//獲取類名稱(不含路徑)
System.out.println(clazz.getSimpleName());
//獲取所在包
System.out.println(clazz.getPackage());
//通過Class創建對象
SuperHero hero = (SuperHero)clazz.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
輸出結果:
reflectdemo.SuperHero
SuperHero
package reflectdemo
這里提前說明一下:Class中兩個功能相同的方法,若其中一個帶有Declared字樣,表示針對類中所有聲明的變量、方法、構造函數等,而對應不帶Declared字樣的方法,則表示僅對公有(public)成員變量、方法起作用,下面不再重復描述,下面僅對帶有Declared字樣的方法進行講解。
三. 反射-構造函數
3.1 getDeclaredConstructor(Class<?>...parameterTypes)
public class ClassUtils {
/**
* 獲取構造函數
* @param clazz 類
* @param params 構造函數參數類型
* @throws NoSuchMethodException
*/
public static void getDeclaredConstructor(Class clazz, Class[] params) throws NoSuchMethodException {
System.out.println(clazz.getDeclaredConstructor(params));
}
}
public class ClassTest {
public static void main(String[] args) {
try {
Class clazz = Class.forName("reflectdemo.SuperHero");
//打印無參構造函數
ClassUtils.getDeclaredConstructor(clazz, null);
//打印有參構造函數
ClassUtils.getDeclaredConstructor(clazz, new Class[]{String.class, String.class, Integer.class, String.class});
} catch (Exception e) {
e.printStackTrace();
}
}
}
輸出結果為:
public reflectdemo.SuperHero()
public reflectdemo.SuperHero(java.lang.String,java.lang.String,java.lang.Integer,java.lang.String)
3.2 getDeclaredConstructors()
public class ClassUtils {
/**
* 遍歷構造函數
* @param clazz 類
*/
public static void getDeclaredConstructors(Class clazz){
//獲取所有的構造函數
Constructor[] constructors = clazz.getDeclaredConstructors();
for (Constructor constructor : constructors) {
//直接打印構造函數
System.out.println(constructor);
//打印構造函數名稱
System.out.println(constructor.getName());
//打印構造函數參數
Parameter[] parameters = constructor.getParameters();
for(Parameter parameter : parameters){
System.out.print(parameter);
System.out.print(", ");
}
System.out.println("---------------------");
}
}
}
public class ClassTest {
public static void main(String[] args) {
try {
Class clazz = Class.forName("reflectdemo.SuperHero");
//遍歷構造函數
ClassUtils.getDeclaredConstructors(clazz);
} catch (Exception e) {
e.printStackTrace();
}
}
}
輸出結果為:
public reflectdemo.SuperHero()
reflectdemo.SuperHero
---------------------
public reflectdemo.SuperHero(java.lang.String,java.lang.String,java.lang.Integer,java.lang.String)
reflectdemo.SuperHero
java.lang.String arg0, java.lang.String arg1, java.lang.Integer arg2, java.lang.String arg3, ---------------------
四. 反射-成員變量
4.1 getDeclaredField(String name)
public class ClassUtils {
/**
* 獲取屬性字段
* @param clazz 類
* @param fieldName 屬性名稱
* @throws Exception
*/
public static void getDeclaredField(Class clazz, String fieldName) throws Exception{
System.out.println(clazz.getDeclaredField(fieldName));
}
}
public class ClassTest {
public static void main(String[] args) {
try {
Class clazz = Class.forName("reflectdemo.SuperHero");
//測試公有屬性
ClassUtils.getDeclaredField(clazz, "ADDRESS");
//測試私有屬性
ClassUtils.getDeclaredField(clazz, "name");
} catch (Exception e) {
e.printStackTrace();
}
}
}
輸出結果為:
public static final java.lang.String reflectdemo.SuperHero.ADDRESS
private java.lang.String reflectdemo.SuperHero.name
4.3 getDeclaredFields()
public class ClassUtils {
/**
* 遍歷clazz對象已有的成員變量
* @param clazz
*/
public static void getDeclaredFields(Class clazz){
Field[] fields = clazz.getDeclaredFields();
for (Field field: fields) {
//如果要設置值,需要加入下面這句,反射對象在使用時不使用Java語言訪問檢查
//field.setAccessible(true);
//直接打印Field
System.out.println(field);
//手動獲取變量類型和變量名稱
System.out.println(field.getType().getName() + " " +field.getName());
System.out.println("--------------------");
}
}
}
public class ClassTest {
public static void main(String[] args) {
try {
Class clazz = Class.forName("reflectdemo.SuperHero");
//遍歷成員變量
ClassUtils.getDeclaredFields(clazz);
} catch (Exception e) {
e.printStackTrace();
}
}
}
輸出結果為:
public static final java.lang.String reflectdemo.SuperHero.ADDRESS
java.lang.String ADDRESS
--------------------
private java.lang.String reflectdemo.SuperHero.id
java.lang.String id
--------------------
private java.lang.String reflectdemo.SuperHero.name
java.lang.String name
--------------------
private java.lang.Integer reflectdemo.SuperHero.age
java.lang.Integer age
--------------------
private java.lang.String reflectdemo.SuperHero.skill
java.lang.String skill
--------------------
五. 反射-成員方法
5.1 getDeclaredMethod(String name, Class<?>... parameterTypes)
public class ClassUtils {
/**
* 獲取成員方法
* @param clazz 類
* @param methodName 方法名稱
* @param params 參數列表
* @throws Exception
*/
public static void getDeclaredMethod(Class clazz, String methodName, Class[] params) throws Exception{
Method method = clazz.getDeclaredMethod(methodName, params);
System.out.println("直接打印");
System.out.println(method);
System.out.println("手動構建");
//獲取返回類型
System.out.print(method.getReturnType().getSimpleName() + " ");
//獲取方法名稱
System.out.print(method.getName() + "(");
//獲取參數類型
Class[] paramTypes = method.getParameterTypes();
for(int i = 0; i < paramTypes.length; i++){
Class param = paramTypes[i];
if(i < paramTypes.length - 1){
System.out.print(param.getSimpleName() + ", ");
}else {
System.out.print(param.getSimpleName());
}
}
System.out.print(")");
System.out.println();
}
}
public class ClassTest {
public static void main(String[] args) {
try {
Class clazz = Class.forName("reflectdemo.SuperHero");
//打印無參數方法
ClassUtils.getDeclaredMethod(clazz, "getName", null);
//打印有參數方法
ClassUtils.getDeclaredMethod(clazz, "setName", new Class[]{String.class});
} catch (Exception e) {
e.printStackTrace();
}
}
}
輸出結果為:
直接打印
public java.lang.String reflectdemo.SuperHero.getName()
手動構建
String getName()
直接打印
public void reflectdemo.SuperHero.setName(java.lang.String)
手動構建
void setName(String)
5.2 getDeclaredMethods()
public class ClassUtils {
/**
* 遍歷方法
* @param clazz
*/
public static void getDeclaredMethods(Class clazz){
//獲取類中所有聲明的方法
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods){
System.out.println(method);
}
}
}
public class ClassTest {
public static void main(String[] args) {
try {
Class clazz = Class.forName("reflectdemo.SuperHero");
//遍歷方法
ClassUtils.getDeclaredMethods(clazz);
} catch (Exception e) {
e.printStackTrace();
}
}
}
輸出結果為:
public java.lang.String reflectdemo.SuperHero.getName()
public java.lang.String reflectdemo.SuperHero.getId()
public void reflectdemo.SuperHero.setName(java.lang.String)
public void reflectdemo.SuperHero.print()
public java.lang.String reflectdemo.SuperHero.getSkill()
public void reflectdemo.SuperHero.setAge(java.lang.Integer)
public void reflectdemo.SuperHero.setSkill(java.lang.String)
public void reflectdemo.SuperHero.setId(java.lang.String)
public java.lang.Integer reflectdemo.SuperHero.getAge()
5.3 方法執行
public class ClassUtils {
/**
* 執行set方法(通過Method的invoke方法)
* @param o 待執行的實體
* @param methodName 方法名稱
* @param params 方法參數類型
* @throws Exception
*/
public static void invokeSetMethod(Object o, String methodName, Class[] params) throws Exception {
Method method = o.getClass().getDeclaredMethod(methodName, params);
method.invoke(o, "鋼鐵俠");
}
/**
* 執行get方法(通過Method的invoke方法)
* @param o 待執行的實體
* @param methodName 方法名稱
* @throws Exception
*/
public static void invokeGetMethod(Object o, String methodName) throws Exception{
Method method = o.getClass().getDeclaredMethod(methodName);
Object obj = method.invoke(o);
System.out.println(obj);
}
}
public class ClassTest {
public static void main(String[] args) {
try {
Class clazz = Class.forName("reflectdemo.SuperHero");
//創建實體
Object o = clazz.newInstance();
//調用set方法
ClassUtils.invokeSetMethod(o, "setName", new Class[]{String.class});
//調用get方法
ClassUtils.invokeGetMethod(o, "getName");
} catch (Exception e) {
e.printStackTrace();
}
}
}
輸出結果為:
鋼鐵俠
下面是對invoke方法的API說明
public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
在具有指定參數的
方法
對象上調用此方法
對象表示的基礎方法。如果底層方法是靜態的,則指定的
obj
參數將被忽略。 它可能為null。如果底層方法所需的形式參數的數量為0,則提供的
args
數組的長度為0或為空。如果底層方法是一個實例方法,它將使用動態方法查找來調用,如“Java語言規范”第二版,第15.12.4.4節所述; 特別是將會發生基於目標對象的運行時類型的覆蓋。
如果底層方法是靜態的,則如果尚未初始化該方法,那么聲明該方法的類將被初始化。
如果方法正常完成,則返回的值將返回給調用者; 如果值具有原始類型,則首先將其適當地包裝在對象中。 但是,如果該值具有基本類型的數組的類型,則該數組的元素不會包含在對象中; 換句話說,返回一個原始類型的數組。 如果底層方法返回類型為void,則調用返回null。
參數
obj
- 從底層方法被調用的對象
args
- 用於方法調用的參數結果
由該對象表示的方法在
obj
上調用args
異常
IllegalAccessException
- 如果這個方法
對象正在強制執行Java語言訪問控制,並且底層方法是無法訪問的。
IllegalArgumentException
- 如果方法是一個實例方法,並且指定的對象參數不是聲明底層方法(或其子類或實現者)的類或接口的實例; 如果實際和正式參數的數量不同; 如果原始參數的解包轉換失敗; 或者如果在可能的展開之后,通過方法調用轉換,參數值不能轉換為相應的形式參數類型。
InvocationTargetException
- 如果底層方法拋出異常。
NullPointerException
- 如果指定的對象為空,該方法為實例方法。
ExceptionInInitializerError
- 如果由此方法引發的初始化失敗。
六. 總結
本文對反射的定義,反射使用過程中重要的、常用的類和方法進行了講解,包括Class類,Constructor類,Field類,Method類的說明及使用。反射機制允許在運行時判斷任意一個對象所屬的類、構造任意一個類的對象、判斷任意一個類所具有的成員變量和方法、調用任意一個對象的方法。大大提高了系統的靈活性和擴展性,不過凡事都有兩面性,反射破壞了Java封裝的特性,相對來說不安全,需要根據場景酌情考慮,若有不對之處,請批評指正,望共同進步,謝謝!