JAVA反射機制是在運作狀态中,對于任意一個實體類,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能夠調用它的任意方法和屬性;這種動态擷取資訊以及動态調用對象方法的功能稱為java語言的反射機制。
反射類是現有很多架構的靈魂,并且這些架構均做了很好的封裝,但是在某些應用場景中也會需要我們重寫反射類來做一些特殊處理,下面提供了一系列的擷取某一個類資訊的工具方法,僅供參考,可根據需要做相應的修改。
01—将指定的類載入系統
/**
* 将指定的類載入系統
* @param name 類名稱
* @return 類對象
* @throws ClassNotFoundException
*/
public static Class<?> classForName(String name) throws ClassNotFoundException {
try {
return Thread.currentThread().getContextClassLoader().loadClass(name);
} catch (ClassNotFoundException e) {
// 可以根據需要做相應的處理,此處僅做簡單處理
Logger.error(e, "類 %s 加載出錯!", name);
} catch (SecurityException e) {
// 可以根據需要做相應的處理,此處僅做簡單處理
Logger.error(e, "類 %s 加載出錯!", name);
}
return Class.forName(name);
}
02—根據類對象擷取包名
/**
* 根據類對象擷取包名
* @param clazz 類對象
* @return 包名【若沒有包,則傳回null】
*/
public static String getPackage(Class<?> clazz) {
Package pck = clazz.getPackage();
if (null != pck) {
return pck.getName();
} else {
return null;
}
}
03—擷取所繼承父類的全類名
/**
* 擷取繼承的父類的全類名
* @param clazz 類對象
* @return 繼承的父類的全類名【若沒有包,則傳回null】
*/
public static String getSuperClassName(Class<?> clazz) {
Class<?> superClass = clazz.getSuperclass();
if (null != superClass) {
return superClass.getName();
} else {
return null;
}
}
04—擷取實作的接口名
/**
* 擷取實作的接口名
* @param clazz 類對象
* @return List<String> 存有所有的接口名的清單【每一個接口名的類型為String,最後儲存到一個List集合中】
*/
public static List<String> getInterfaces(Class<?> clazz) {
Class<?>[] interfaces = clazz.getInterfaces();
int len = interfaces.length;
List<String> list = new ArrayList<String>();
for (int i = 0; i < len; i++) {
Class<?> itfc = interfaces[i];
// 接口名
String interfaceName = itfc.getSimpleName();
list.add(interfaceName);
}
return list;
}
05—擷取所有屬性
/**
* 擷取所有屬性
* @param clazz 類對象
* @return 存有所有的屬性的清單【每一個屬性添加到StringBuilder中,最後儲存到一個List集合中】
*/
public static List<StringBuilder> getFields(Class<?> clazz) {
Field[] fields = clazz.getDeclaredFields();
int len = fields.length;
List<StringBuilder> list = new ArrayList<StringBuilder>();
StringBuilder sb = null;
for (int i = 0; i < len; i++) {
Field field = fields[i];
sb = new StringBuilder();
// 修飾符
String modifier = Modifier.toString(field.getModifiers());
sb.append(modifier + " ");
// 資料類型
Class<?> type = field.getType();
String typeName = type.getSimpleName();
sb.append(typeName + " ");
// 屬性名
String fieldName = field.getName();
sb.append(fieldName + ";");
list.add(sb);
}
return list;
}
06—擷取所有類中的方法
/**
* 擷取所有自身的方法
* @param clazz 類對象
* @return 存有所有自身的方法的清單【每一個方法添加到StringBuilder中,最後儲存到一個List集合中】
*/
public static List<StringBuilder> getMethods(Class<?> clazz) {
Method[] methods = clazz.getDeclaredMethods();
int len = methods.length;
List<StringBuilder> list = new ArrayList<StringBuilder>();
StringBuilder sb = null;
for (int i = 0; i < len; i++) {
Method method = methods[i];
sb = new StringBuilder();
// 修飾符
String modifier = Modifier.toString(method.getModifiers());
sb.append(modifier + " ");
// 傳回值類型
Class<?> returnClass = method.getReturnType();
String returnType = returnClass.getSimpleName();
sb.append(returnType + " ");
// 方法名
String methodName = method.getName();
sb.append(methodName + " (");
// 形參清單
Class<?>[] parameterTypes = method.getParameterTypes();
int length = parameterTypes.length;
for (int j = 0; j < length; j++) {
Class<?> parameterType = parameterTypes[j];
// 形參類型
String parameterTypeName = parameterType.getSimpleName();
if (j < length - 1) {
sb.append(parameterTypeName + ", ");
} else {
sb.append(parameterTypeName);
}
}
sb.append(") {}");
list.add(sb);
}
return list;
}
07—根據名稱生成指定的對象
/**
* 根據名稱生成指定的對象
* @param name 類名稱
* @return Object 具體的對象【若發生異常,則傳回null】
*/
public static Object objectForName(String name) {
try {
return Class.forName(name).newInstance();
} catch (Exception ex) {
// 可以根據需要做相應的處理,此處僅做簡單處理
Logger.error("根據類名稱 %s 擷取對象執行個體出錯!", name);
}
return null;
}
08—根據屬性名稱擷取對應的屬性值
/**
* 根據屬性名稱擷取對應的屬性值
* @param obj 對象值
* @param key 屬性名稱
* @return 屬性值【若發生異常,則傳回null】
*/
public static Object getFieldValue(Object obj, String key) {
Class clazz = obj.getClass();
Method[] methods = clazz.getDeclaredMethods();
for (Method method:methods) {
if (("get" + key).equalsIgnoreCase(method.getName())) {
try {
method.setAccessible(true);
return method.invoke(obj);
} catch (IllegalArgumentException e) {
// 可以根據需要做相應的處理,此處僅做簡單處理
Logger.error(e, "%s調用方法%s出錯", clazz.getName(), method.getName() + "值錯誤");
} catch (IllegalAccessException e) {
// 可以根據需要做相應的處理,此處僅做簡單處理
Logger.error(e, "%s調用方法%s出錯", clazz.getName(), method.getName() + "值錯誤");
} catch (InvocationTargetException e) {
// 可以根據需要做相應的處理,此處僅做簡單處理
Logger.error(e, "%s調用方法%s出錯", clazz.getName(), method.getName() + "值錯誤");
}
}
}
Logger.warn("%s類找不到%s方法", obj.getClass().getName(), "get" + StringUtils.capitalize(key));
return null;
}
09—設定對象的屬性值
/**
* 設定對象屬性值
* @param obj 對象值
* @param key 屬性名稱
* @param value 屬性值
* @return
*/
public static boolean setFieldValue(Object obj, String key, Object value) {
Class clazz = obj.getClass();
Method[] methods = clazz.getDeclaredMethods();
for (Method method:methods) {
if (("set" + key).equalsIgnoreCase(method.getName())) {
try {
method.setAccessible(true);
method.invoke(obj, value);
} catch (IllegalArgumentException e) {
// 可以根據需要做相應的處理,此處僅做簡單處理
Logger.error(e, "%s調用方法%s出錯", clazz.getName(), method.getName() + "參數錯誤");
} catch (IllegalAccessException e) {
// 可以根據需要做相應的處理,此處僅做簡單處理
Logger.error(e, "%s調用方法%s出錯", clazz.getName(), method.getName() + "參數錯誤");
} catch (InvocationTargetException e) {
// 可以根據需要做相應的處理,此處僅做簡單處理
Logger.error(e, "%s調用方法%s出錯", clazz.getName(), method.getName() + "參數錯誤");
}
return true;
}
}
Logger.warn("%s找不到%s方法", clazz.getName(), "set" + StringUtils.capitalize(key));
return false;
}
10—指定調用對應的方法
/**
* 根據傳入的方法對象,調用對應的方法
* @param method 方法對象
* @param obj 要調用的執行個體對象【如果是調用靜态方法,則可以傳入null】
* @param args 傳入方法的實參【非必填】
* @return 方法的傳回值【沒有傳回值,則傳回null】
*/
public static Object invokeMethod(Method method, Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
method.setAccessible(true);
try {
return method.invoke(obj, args);
} catch (IllegalArgumentException e) {
// 可以根據需要做相應的處理,此處僅做簡單處理
Logger.error(e, "調用方法%s出錯", method.getName());
} catch (IllegalAccessException e) {
// 可以根據需要做相應的處理,此處僅做簡單處理
Logger.error(e, "調用方法%s出錯", method.getName());
} catch (InvocationTargetException e) {
// 可以根據需要做相應的處理,此處僅做簡單處理
Logger.error(e, "調用方法%s出錯", method.getName());
}
return null;
}
補充說明:
上面的代碼中,大家也能看到 f.setAccessible(true) 這個方法,下面對這個方法做一個簡單的說明: Accessable屬性是繼承自AccessibleObject 類,AccessibleObject 類是 Field、Method 和 Constructor 對象的基類。它提供了将反射的對象标記為在使用時取消預設 Java 語言通路控制檢查的能力。對于公共成員、預設(打包)通路成員、受保護成員和私有成員,在分别使用 Field、Method 或 Constructor 對象來設定或獲得字段、調用方法,或者建立和初始化類的新執行個體的時候,會執行通路檢查。
public void setAccessible(boolean flag)
throws SecurityException
有一些人錯誤的認為setAccessible是啟用和禁用通路安全檢查的開關,并不是為true就能通路,而false就不能通路。
正确的結論
accessible 标志設定為 true 則訓示反射的對象在使用時應該取消 Java 語言通路檢查。值為 false 則訓示反射的對象應該實施 Java 語言通路檢查。
作用
A、提高性能
由于JDK的安全檢查耗時較多.是以通過setAccessible(true)的方式關閉安全檢查就可以達到提升反射速度的目的 。使用了method.setAccessible(true)後,性能有了20倍的提升。
B、通路私有private變量的時候
Java代碼中,常常将一個類的成員變量置為private,在類的外面擷取此類的私有成員變量的value時,需要注意setAccessible(true),否則或報錯:can not access a member of.......
拓展