天天看點

常用工具類 — 反射工具類

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.......

拓展