Java反射技術詳解及例項解析
前言
相信很多人都知道反射可以說是Java中最強大的技術了,它可以做的事情太多太多,很多優秀的開源框架都是通過反射完成的,比如最初的很多註解框架,後來因為java反射影響效能,所以被執行時註解APT替代了,java反射有個開源框架jOOR相信很多人都用過,不過我們還是要學習反射的基礎語法,這樣才能自己寫出優秀的框架,當然這裡所講的反射技術,是學習Android外掛化技術、Hook技術等必不可少的!
一、基本反射技術
1.1 根據一個字串得到一個類
getClass方法
String name = "Huanglinqing"; Class c1 = name.getClass(); System.out.println(c1.getName());
列印結果如下:
Class.forName
比如我們獲取java.lang.String的類名
String name = "java.lang.String"; Class c1 = null; try { c1 = Class.forName(name); System.out.println(c1.getName()); } catch (ClassNotFoundException e) { }
這裡也通過捕獲異常,因為我們傳的這個字串可能不合法,字串合法命名是類的名稱空間和類的名稱組成
列印結果如下:
我們還可以通過c1.getSuperclass()獲取到他的父類
Type屬性
基本型別都有type屬性,可以得到這個基本型別的型別,比如:
Class c1 = Boolean.TYPE; Class c2 = Byte.TYPE; Class c3 = Float.TYPE; Class c4 = Double.TYPE;
停停停!這些東西有啥子用,如此雞肋! 別急,一切都是為後續做準備。
二、獲取類的成員
當類中方法定義為私有的時候我們能呼叫?不能!當變數是私有的時候我們能獲取嗎?不能!但是反射可以,比如原始碼中有你需要用到的方法,但是那個方法是私有的,這個時候你就可以通過反射去執行這個私有方法,並且獲取私有變數。
獲取類的建構函式
為了便於測試,我們定義一個Test類,Test類如下:(省略get和set方法)
Test類中我們定義是三個私有變數,生成兩個公有的含參構造方法和一個私有的含參構造方法以及一個公有的無參構造方法。
public class Test { private int age; private String name; private int testint; public Test(int age) { this.age = age; } public Test(int age,String name) { this.age = age; this.name = name; } private Test(String name) { this.name = name; } public Test() { }
下面我們通過反射獲取這些構造方法
獲取類的所有構造方法
Test test = new Test(); Class c4 = test.getClass(); Constructor[] constructors ; constructors = c4.getDeclaredConstructors();
通過getDeclaredConstructors可以返回類的所有構造方法,返回的是一個數組因為構造方法可能不止一個,通過getModifiers可以得到構造方法的型別,getParameterTypes可以得到構造方法的所有引數,返回的是一個Class陣列,所以我們如果想獲取所有構造方法以及每個構造方法的引數型別,可以有如下程式碼:
for (int i = 0; i < constructors.length; i++) { System.out.print(Modifier.toString(constructors[i].getModifiers()) + "引數:"); Class[] parametertypes = constructors[i].getParameterTypes(); for (int j = 0; j < parametertypes.length; j++) { System.out.print(parametertypes[j].getName() + " "); } System.out.println(""); }
執行結果如下所示:
這樣我們就得到了類中所有構造方法和構造方法中的引數,那麼我們如何獲取特定的構造方法呢?
獲取類中特定的構造方法
我們可以通過getConstructors方法獲取類中 所有的public型別的構造方法,程式碼和上面一樣就不演示了。
我們可以通過getDeclaredConstructor()方法傳參獲取特定引數型別的構造方法,這裡注意是getDeclaredConstructor()不是 getDeclaredConstructors() ,所以返回的是一個Class物件而不是一個Class陣列。
獲取無參構造方法直接不傳引數,如下所示:
try { constructors = c4.getDeclaredConstructor(); System.out.print(Modifier.toString(constructors.getModifiers()) + ); } catch (NoSuchMethodException e) { e.printStackTrace(); }
這裡要進行異常捕獲,因為可能不存在對應的構造方法,列印結果如下:
如果我們想獲取有兩個引數分別為int和String型別的構造方法,程式碼如下:
Class[] p = {int.class,String.class}; try { constructors = c4.getDeclaredConstructor(p); System.out.print(Modifier.toString(constructors.getModifiers()) + "引數:"); Class[] parametertypes = constructors.getParameterTypes(); for (int j = 0; j < parametertypes.length; j++) { System.out.print(parametertypes[j].getName() + " "); } } catch (NoSuchMethodException e) { e.printStackTrace(); }
這裡我們同樣打印出構造方法的引數:
呼叫構造方法
從這裡開始慢慢到了關鍵的一步,得到類的例項,我們主要藉助於newInstance方法,為了方便演示我們將測試類的兩個構造方法打印出來.
public Test(int age,String name) { this.age = age; this.name = name; System.out.println("hello" + name + "i am" + age); } private Test(String name) { this.name = name; System.out.println("My Name is" + name); }
我們先來呼叫public的方法,如下所示:
Class[] p = {int.class,String.class}; constructors = c4.getDeclaredConstructor(p); constructors.newInstance(24,"HuangLinqing");
執行列印結果如下:
那麼呼叫私有構造方法呢,和上面一樣,只是我們要設定constructors.setAccessible(true);程式碼如下:
Class[] p = {String.class}; constructors = c4.getDeclaredConstructor(p); constructors.setAccessible(true); constructors.newInstance("HuangLinqing");
列印結果如下:
呼叫類的私有方法
如何呼叫類中的私有方法呢,我們先在測試類中編寫一個測試的私有方法 如下:
private void welcome(String tips){ System.out.println(tips); }
我們知道如果我們要正常的呼叫類的方法都是通過類.方法呼叫,所以我們呼叫私有方法也需要得到類的例項,而我們上面newInstace已經得到了類的例項,這樣就好辦了。
Class[] p4 = {String.class}; Method method = c4.getDeclaredMethod("welcome",p4); method.setAccessible(true);
我們首先通過getDeclaredMethod方法獲取到這個私有方法,第一個引數是方法名,第二個引數是引數型別
然後通過invoke方法執行,invoke需要兩個引數一個是類的例項,一個是方法引數。
Class[] p4 = {String.class}; Method method = c4.getDeclaredMethod("welcome",p4); method.setAccessible(true); Object arg1s[] = {"歡迎關注程式碼男人技術公眾號"}; method.invoke(test,arg1s);
test類的例項當不能new 獲取的時候我們也可以通過反射獲取,就是上面的newInstance方法。列印結果如下:
獲取類的私有欄位並修改值
看到這裡你可能會說,有了set方法,什麼私有不私有,test.set不就可以了,但是這裡要注意我們是沒有辦法得到這個類的例項的,要不然都可以得到例項就沒有反射一說了。我們在通過反射得到類的例項之後先獲取欄位:
Field field = c4.getDeclaredField("name"); field.setAccessible(true); field.set(o,"程式碼男人");
o是我們上面通過反射構造方法獲取的例項,列印field.get(o).toString()的值如下:
不過要注意的是我們修改了name的值只對當前的例項物件有效。
Java的基本反射語法就是這樣了,歡迎加入技術群一起探討!
最後反射封裝類如下:
package jnidemo.hlq.com.hookdemo; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * @author Huanglinqing * @date 2019/4/28 */ public class Reflex { /** * 獲取無參建構函式 * @param className * @return */ public static Object createObject(String className) { Class[] pareTyples = new Class[]{}; Object[] pareVaules = new Object[]{}; try { Class r = Class.forName(className); return createObject(r,pareTyples,pareVaules); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; } /** * 獲取無參構造方法 * @param clazz * @return */ public static Object createObject(Class clazz) { Class[] pareTyple = new Class[]{}; Object[] pareVaules = new Object[]{}; return createObject(clazz,pareTyple,pareVaules); } /** * 獲取一個引數的建構函式 已知className * * @param className * @param pareTyple * @param pareVaule * @return */ public static Object createObject(String className,Class pareTyple,Object pareVaule) { Class[] pareTyples = new Class[]{pareTyple}; Object[] pareVaules = new Object[]{pareVaule}; try { Class r = Class.forName(className); return createObject(r,pareVaules); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; } /** * 獲取單個引數的構造方法 已知類 * * @param clazz * @param pareTyple * @param pareVaule * @return */ public static Object createObject(Class clazz,Object pareVaule) { Class[] pareTyples = new Class[]{pareTyple}; Object[] pareVaules = new Object[]{pareVaule}; return createObject(clazz,pareVaules); } /** * 獲取多個引數的構造方法 已知className * @param className * @param pareTyples * @param pareVaules * @return */ public static Object createObject(String className,Class[] pareTyples,Object[] pareVaules) { try { Class r = Class.forName(className); return createObject(r,pareVaules); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; } /** * 獲取構造方法 * * @param clazz * @param pareTyples * @param pareVaules * @return */ public static Object createObject(Class clazz,Object[] pareVaules) { try { Constructor ctor = clazz.getDeclaredConstructor(pareTyples); ctor.setAccessible(true); return ctor.newInstance(pareVaules); } catch (Exception e) { e.printStackTrace(); } return null; } /** * 獲取多個引數的方法 * @param obj * @param methodName * @param pareTyples * @param pareVaules * @return */ public static Object invokeInstanceMethod(Object obj,String methodName,Object[] pareVaules) { if (obj == null) { return null; } try { //呼叫一個private方法 //在指定類中獲取指定的方法 Method method = obj.getClass().getDeclaredMethod(methodName,pareTyples); method.setAccessible(true); return method.invoke(obj,pareVaules); } catch (Exception e) { e.printStackTrace(); } return null; } /** * 獲取一個引數的方法 * @param obj * @param methodName * @param pareTyple * @param pareVaule * @return */ public static Object invokeInstanceMethod(Object obj,Object pareVaule) { Class[] pareTyples = {pareTyple}; Object[] pareVaules = {pareVaule}; return invokeInstanceMethod(obj,methodName,pareVaules); } /** * 獲取無參方法 * @param obj * @param methodName * @return */ public static Object invokeInstanceMethod(Object obj,String methodName) { Class[] pareTyples = new Class[]{}; Object[] pareVaules = new Object[]{}; return invokeInstanceMethod(obj,pareVaules); } /** * 無參靜態方法 * @param className * @param method_name * @return */ public static Object invokeStaticMethod(String className,String method_name) { Class[] pareTyples = new Class[]{}; Object[] pareVaules = new Object[]{}; return invokeStaticMethod(className,method_name,pareVaules); } /** * 獲取一個引數的靜態方法 * @param className * @param method_name * @param pareTyple * @param pareVaule * @return */ public static Object invokeStaticMethod(String className,String method_name,Object pareVaule) { Class[] pareTyples = new Class[]{pareTyple}; Object[] pareVaules = new Object[]{pareVaule}; return invokeStaticMethod(className,pareVaules); } /** * 獲取多個引數的靜態方法 * @param className * @param method_name * @param pareTyples * @param pareVaules * @return */ public static Object invokeStaticMethod(String className,Object[] pareVaules) { try { Class obj_class = Class.forName(className); return invokeStaticMethod(obj_class,pareVaules); } catch (Exception e) { e.printStackTrace(); } return null; } /** * 無參靜態方法 * @param method_name * @return */ public static Object invokeStaticMethod(Class clazz,String method_name) { Class[] pareTyples = new Class[]{}; Object[] pareVaules = new Object[]{}; return invokeStaticMethod(clazz,pareVaules); } /** * 一個引數靜態方法 * @param clazz * @param method_name * @param classType * @param pareVaule * @return */ public static Object invokeStaticMethod(Class clazz,Class classType,Object pareVaule) { Class[] classTypes = new Class[]{classType}; Object[] pareVaules = new Object[]{pareVaule}; return invokeStaticMethod(clazz,classTypes,pareVaules); } /** * 多個引數的靜態方法 * @param clazz * @param method_name * @param pareTyples * @param pareVaules * @return */ public static Object invokeStaticMethod(Class clazz,Object[] pareVaules) { try { Method method = clazz.getDeclaredMethod(method_name,pareTyples); method.setAccessible(true); return method.invoke(null,pareVaules); } catch (Exception e) { e.printStackTrace(); } return null; } public static Object getFieldObject(String className,Object obj,String filedName) { try { Class obj_class = Class.forName(className); return getFieldObject(obj_class,obj,filedName); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; } public static Object getFieldObject(Class clazz,String filedName) { try { Field field = clazz.getDeclaredField(filedName); field.setAccessible(true); return field.get(obj); } catch (Exception e) { e.printStackTrace(); } return null; } public static void setFieldObject(Class clazz,String filedName,Object filedVaule) { try { Field field = clazz.getDeclaredField(filedName); field.setAccessible(true); field.set(obj,filedVaule); } catch (Exception e) { e.printStackTrace(); } } public static void setFieldObject(String className,Object filedVaule) { try { Class obj_class = Class.forName(className); setFieldObject(obj_class,filedName,filedVaule); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Object getStaticFieldObject(String className,String filedName) { return getFieldObject(className,null,filedName); } public static Object getStaticFieldObject(Class clazz,String filedName) { return getFieldObject(clazz,filedName); } public static void setStaticFieldObject(String classname,Object filedVaule) { setFieldObject(classname,filedVaule); } public static void setStaticFieldObject(Class clazz,Object filedVaule) { setFieldObject(clazz,filedVaule); } }
到此這篇關於Java反射技術詳解及例項解析的文章就介紹到這了,更多相關Java反射技術示例詳解內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!