Java反射技術(shù)詳解
前言
相信很多人都知道反射可以說是Java中最強(qiáng)大的技術(shù)了,它可以做的事情太多太多,很多優(yōu)秀的開源框架都是通過反射完成的,比如最初的很多注解框架,后來因?yàn)閖ava反射影響性能,所以被運(yùn)行時注解APT替代了,java反射有個開源框架jOOR相信很多人都用過,不過我們還是要學(xué)習(xí)反射的基礎(chǔ)語法,這樣才能自己寫出優(yōu)秀的框架,當(dāng)然這里所講的反射技術(shù),是學(xué)習(xí)Android插件化技術(shù)、Hook技術(shù)等必不可少的!
一、基本反射技術(shù)
1.1 根據(jù)一個字符串得到一個類
getClass方法
String name = "Huanglinqing"; Class c1 = name.getClass(); System.out.println(c1.getName());
打印結(jié)果如下:
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) { }
這里也通過捕獲異常,因?yàn)槲覀儌鞯倪@個字符串可能不合法,字符串合法命名是類的命名空間和類的名稱組成
打印結(jié)果如下:
我們還可以通過c1.getSuperclass()獲取到他的父類
Type屬性
基本類型都有type屬性,可以得到這個基本類型的類型,比如:
Class c1 = Boolean.TYPE; Class c2 = Byte.TYPE; Class c3 = Float.TYPE; Class c4 = Double.TYPE;
二、獲取類的成員
當(dāng)類中方法定義為私有的時候我們能調(diào)用?不能!當(dāng)變量是私有的時候我們能獲取嗎?不能!但是反射可以,比如源碼中有你需要用到的方法,但是那個方法是私有的,這個時候你就可以通過反射去執(zhí)行這個私有方法,并且獲取私有變量。
獲取類的構(gòu)造函數(shù)
為了便于測試,我們定義一個Test類,Test類如下:(省略get和set方法)
Test類中我們定義是三個私有變量,生成兩個公有的含參構(gòu)造方法和一個私有的含參構(gòu)造方法以及一個公有的無參構(gòu)造方法。
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() { }
下面我們通過反射獲取這些構(gòu)造方法
獲取類的所有構(gòu)造方法
Test test = new Test(); Class c4 = test.getClass(); Constructor[] constructors ; constructors = c4.getDeclaredConstructors();
通過getDeclaredConstructors可以返回類的所有構(gòu)造方法,返回的是一個數(shù)組因?yàn)闃?gòu)造方法可能不止一個,通過getModifiers可以得到構(gòu)造方法的類型,getParameterTypes可以得到構(gòu)造方法的所有參數(shù),返回的是一個Class數(shù)組,所以我們?nèi)绻氆@取所有構(gòu)造方法以及每個構(gòu)造方法的參數(shù)類型,可以有如下代碼:
for (int i = 0; i < constructors.length; i++) { System.out.print(Modifier.toString(constructors[i].getModifiers()) + "參數(shù):"); Class[] parametertypes = constructors[i].getParameterTypes(); for (int j = 0; j < parametertypes.length; j++) { System.out.print(parametertypes[j].getName() + " "); } System.out.println(""); }
運(yùn)行結(jié)果如下所示:
這樣我們就得到了類中所有構(gòu)造方法和構(gòu)造方法中的參數(shù),那么我們?nèi)绾潍@取特定的構(gòu)造方法呢?
獲取類中特定的構(gòu)造方法
我們可以通過getConstructors
方法獲取類中 所有的public類型的構(gòu)造方法,代碼和上面一樣就不演示了。
我們可以通過getDeclaredConstructor()
方法傳參獲取特定參數(shù)類型的構(gòu)造方法,這里注意是getDeclaredConstructor()
不是 getDeclaredConstructors()
,所以返回的是一個Class對象而不是一個Class數(shù)組。
獲取無參構(gòu)造方法直接不傳參數(shù),如下所示:
try { constructors = c4.getDeclaredConstructor(); System.out.print(Modifier.toString(constructors.getModifiers()) + ); } catch (NoSuchMethodException e) { e.printStackTrace(); }
這里要進(jìn)行異常捕獲,因?yàn)榭赡懿淮嬖趯?yīng)的構(gòu)造方法,打印結(jié)果如下:
如果我們想獲取有兩個參數(shù)分別為int和String類型的構(gòu)造方法,代碼如下:
Class[] p = {int.class,String.class}; try { constructors = c4.getDeclaredConstructor(p); System.out.print(Modifier.toString(constructors.getModifiers()) + "參數(shù):"); Class[] parametertypes = constructors.getParameterTypes(); for (int j = 0; j < parametertypes.length; j++) { System.out.print(parametertypes[j].getName() + " "); } } catch (NoSuchMethodException e) { e.printStackTrace(); }
這里我們同樣打印出構(gòu)造方法的參數(shù):
調(diào)用構(gòu)造方法
從這里開始慢慢到了關(guān)鍵的一步,得到類的實(shí)例,我們主要借助于newInstance方法,為了方便演示我們將測試類的兩個構(gòu)造方法打印出來.
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); }
我們先來調(diào)用public的方法,如下所示:
Class[] p = {int.class,String.class}; constructors = c4.getDeclaredConstructor(p); constructors.newInstance(24,"HuangLinqing");
運(yùn)行打印結(jié)果如下:
那么調(diào)用私有構(gòu)造方法呢,和上面一樣,只是我們要設(shè)置constructors.setAccessible(true);代碼如下:
Class[] p = {String.class}; constructors = c4.getDeclaredConstructor(p); constructors.setAccessible(true); constructors.newInstance("HuangLinqing");
打印結(jié)果如下:
調(diào)用類的私有方法
如何調(diào)用類中的私有方法呢,我們先在測試類中編寫一個測試的私有方法 如下:
private void welcome(String tips){ System.out.println(tips); }
我們知道如果我們要正常的調(diào)用類的方法都是通過類.方法調(diào)用,所以我們調(diào)用私有方法也需要得到類的實(shí)例,而我們上面newInstace已經(jīng)得到了類的實(shí)例,這樣就好辦了。
Class[] p4 = {String.class}; Method method = c4.getDeclaredMethod("welcome",p4); method.setAccessible(true);
我們首先通過 getDeclaredMethod方法獲取到這個私有方法,第一個參數(shù)是方法名,第二個參數(shù)是參數(shù)類型
然后通過invoke方法執(zhí)行,invoke需要兩個參數(shù)一個是類的實(shí)例,一個是方法參數(shù)。
Class[] p4 = {String.class}; Method method = c4.getDeclaredMethod("welcome",p4); method.setAccessible(true); Object arg1s[] = {"歡迎關(guān)注代碼男人技術(shù)公眾號"}; method.invoke(test,arg1s);
test類的實(shí)例當(dāng)不能new 獲取的時候我們也可以通過反射獲取,就是上面的newInstance方法。
打印結(jié)果如下:
獲取類的私有字段并修改值
看到這里你可能會說,有了set方法,什么私有不私有,test.set不就可以了,但是這里要注意我們是沒有辦法得到這個類的實(shí)例的,要不然都可以得到實(shí)例就沒有反射一說了。我們在通過反射得到類的實(shí)例之后先獲取字段:
Field field = c4.getDeclaredField("name"); field.setAccessible(true); field.set(o,"代碼男人");
是我們上面通過反射構(gòu)造方法獲取的實(shí)例, 打印field.get(o).toString()
的值如下:
不過要注意的是我們修改了name的值只對當(dāng)前的實(shí)例對象有效。
Java的基本反射語法就是這樣了,歡迎加入技術(shù)群一起探討!
最后反射封裝類如下:
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 { /** * 獲取無參構(gòu)造函數(shù) * @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; } /** * 獲取無參構(gòu)造方法 * @param clazz * @return */ public static Object createObject(Class clazz) { Class[] pareTyple = new Class[]{}; Object[] pareVaules = new Object[]{}; return createObject(clazz, pareTyple, pareVaules); } /** * 獲取一個參數(shù)的構(gòu)造函數(shù) 已知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, pareTyples, pareVaules); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; } /** * 獲取單個參數(shù)的構(gòu)造方法 已知類 * * @param clazz * @param pareTyple * @param pareVaule * @return */ public static Object createObject(Class clazz, Class pareTyple, Object pareVaule) { Class[] pareTyples = new Class[]{pareTyple}; Object[] pareVaules = new Object[]{pareVaule}; return createObject(clazz, pareTyples, pareVaules); } /** * 獲取多個參數(shù)的構(gòu)造方法 已知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, pareTyples, pareVaules); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; } /** * 獲取構(gòu)造方法 * * @param clazz * @param pareTyples * @param pareVaules * @return */ public static Object createObject(Class clazz, Class[] pareTyples, Object[] pareVaules) { try { Constructor ctor = clazz.getDeclaredConstructor(pareTyples); ctor.setAccessible(true); return ctor.newInstance(pareVaules); } catch (Exception e) { e.printStackTrace(); } return null; } /** * 獲取多個參數(shù)的方法 * @param obj * @param methodName * @param pareTyples * @param pareVaules * @return */ public static Object invokeInstanceMethod(Object obj, String methodName, Class[] pareTyples, Object[] pareVaules) { if (obj == null) { return null; } try { //調(diào)用一個private方法 //在指定類中獲取指定的方法 Method method = obj.getClass().getDeclaredMethod(methodName, pareTyples); method.setAccessible(true); return method.invoke(obj, pareVaules); } catch (Exception e) { e.printStackTrace(); } return null; } /** * 獲取一個參數(shù)的方法 * @param obj * @param methodName * @param pareTyple * @param pareVaule * @return */ public static Object invokeInstanceMethod(Object obj, String methodName, Class pareTyple, Object pareVaule) { Class[] pareTyples = {pareTyple}; Object[] pareVaules = {pareVaule}; return invokeInstanceMethod(obj, methodName, pareTyples, 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, methodName, pareTyples, pareVaules); } /** * 無參靜態(tài)方法 * @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, pareTyples, pareVaules); } /** * 獲取一個參數(shù)的靜態(tài)方法 * @param className * @param method_name * @param pareTyple * @param pareVaule * @return */ public static Object invokeStaticMethod(String className, String method_name, Class pareTyple, Object pareVaule) { Class[] pareTyples = new Class[]{pareTyple}; Object[] pareVaules = new Object[]{pareVaule}; return invokeStaticMethod(className, method_name, pareTyples, pareVaules); } /** * 獲取多個參數(shù)的靜態(tài)方法 * @param className * @param method_name * @param pareTyples * @param pareVaules * @return */ public static Object invokeStaticMethod(String className, String method_name, Class[] pareTyples, Object[] pareVaules) { try { Class obj_class = Class.forName(className); return invokeStaticMethod(obj_class, method_name, pareTyples, pareVaules); } catch (Exception e) { e.printStackTrace(); } return null; } /** * 無參靜態(tài)方法 * @param method_name * @return */ public static Object invokeStaticMethod(Class clazz, String method_name) { Class[] pareTyples = new Class[]{}; Object[] pareVaules = new Object[]{}; return invokeStaticMethod(clazz, method_name, pareTyples, pareVaules); } /** * 一個參數(shù)靜態(tài)方法 * @param clazz * @param method_name * @param classType * @param pareVaule * @return */ public static Object invokeStaticMethod(Class clazz, String method_name, Class classType, Object pareVaule) { Class[] classTypes = new Class[]{classType}; Object[] pareVaules = new Object[]{pareVaule}; return invokeStaticMethod(clazz, method_name, classTypes, pareVaules); } /** * 多個參數(shù)的靜態(tài)方法 * @param clazz * @param method_name * @param pareTyples * @param pareVaules * @return */ public static Object invokeStaticMethod(Class clazz, String method_name, Class[] pareTyples, 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, Object obj, 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, Object obj, 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 obj, String filedName, Object filedVaule) { try { Class obj_class = Class.forName(className); setFieldObject(obj_class, obj, 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, null, filedName); } public static void setStaticFieldObject(String classname, String filedName, Object filedVaule) { setFieldObject(classname, null, filedName, filedVaule); } public static void setStaticFieldObject(Class clazz, String filedName, Object filedVaule) { setFieldObject(clazz, null, filedName, filedVaule); } }
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
spring cloud Hystrix斷路器的使用(熔斷器)
這篇文章主要介紹了spring cloud Hystrix斷路器的使用(熔斷器),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-08-08解決springboot項(xiàng)目啟動報錯Field xxxMapper in com...xx
這篇文章主要介紹了解決springboot項(xiàng)目啟動報錯Field xxxMapper in com...xxxContr問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12如何在Redis中實(shí)現(xiàn)分頁排序查詢過程解析
這篇文章主要介紹了如何在Redis中實(shí)現(xiàn)分頁排序查詢過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-07-07Java在高并發(fā)場景下實(shí)現(xiàn)點(diǎn)贊計數(shù)器
點(diǎn)贊計數(shù)器的本質(zhì)就是對某個變量在高并發(fā)情況下的修改,這篇文章主要為大家介紹了Java實(shí)現(xiàn)點(diǎn)贊計數(shù)器的示例代碼,感興趣的小伙伴可以了解一下2023-06-06一文詳解Java項(xiàng)目中如何優(yōu)雅的使用枚舉類型
枚舉類型在開發(fā)中是很常見的,有非常多的應(yīng)用場景,這篇文章我們就來學(xué)習(xí)一下項(xiàng)目中如何優(yōu)雅的使用枚舉類型,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-03-03kafka并發(fā)寫大消息異常TimeoutException排查記錄
這篇文章主要為大家介紹了kafka并發(fā)寫大消息異常TimeoutException的排查記錄及解決方案,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-02-02