Java中反射的應(yīng)用
反射的應(yīng)用
1. 獲取運(yùn)行時(shí)類的完整結(jié)構(gòu)
通過反射獲取運(yùn)行時(shí)類的完整結(jié)構(gòu),包括一下所列都是可以獲取的,下面來試試
- Interface:實(shí)現(xiàn)的全部接口
- Superclass:所繼承的父類
- Constructor:全部的構(gòu)造器
- Method:全部的方法
- Field:全部的屬性
- Annotation:注解
新建一個(gè) Person.java 類
package com.javabasic.reflection; /** * @Description * @ClassName Person * @Author yuhuofei * @Date 2022/9/25 16:44 * @Version 1.0 */ public class Person { private String name; private Integer age; private String getName() { return name; } private void setName(String name) { this.name = name; } private Integer getAge() { return age; } private void setAge(Integer age) { this.age = age; } public Person() { } public Person(String name, Integer age) { this.name = name; this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
新建 TestReflection.java 類,測試一下通過反射獲取上面這個(gè)類的信息
package com.javabasic.reflection; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * @Description 反射的應(yīng)用 * @ClassName TestReflection * @Author yuhuofei * @Date 2022/10/2 15:57 * @Version 1.0 */ public class TestReflection { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException { Class<?> aClass = Class.forName("com.javabasic.reflection.Person"); System.out.println("==================獲取類名====================="); //獲取類的名字 System.out.println("類名:" + aClass.getSimpleName()); //獲取包名+類名 System.out.println("包名及類名:" + aClass.getName()); System.out.println("==================獲取類的屬性====================="); //獲取類的public類型屬性 Field[] fields1 = aClass.getFields(); for (Field field : fields1) { System.out.println("類的public類型屬性:" + field); } //獲取類的所有屬性 Field[] fields = aClass.getDeclaredFields(); for (Field field : fields) { System.out.println("類的所有屬性:" + field); } //獲取指定的屬性 Field name = aClass.getDeclaredField("name"); System.out.println("類的指定屬性:" + name); System.out.println("==================獲取類的方法====================="); //獲取本類及父類的所有public類型方法 Method[] methods = aClass.getMethods(); for (Method method : methods) { System.out.println("本類及父類的所有public類型方法:" + method); } //獲取本類及父類的所有方法 Method[] declaredMethods = aClass.getDeclaredMethods(); for (Method method : declaredMethods) { System.out.println("本類父類的所有方法:" + method); } //獲取指定方法 Method getName = aClass.getDeclaredMethod("getName", null); Method setName = aClass.getDeclaredMethod("setName", String.class); System.out.println("獲取指定的getName方法:" + getName); System.out.println("獲取指定的setName方法:" + setName); System.out.println("==================獲取類的構(gòu)造器====================="); //獲取所有public類型構(gòu)造器 Constructor<?>[] constructors = aClass.getConstructors(); for (Constructor constructor : constructors) { System.out.println("獲取所有public類型構(gòu)造器:" + constructor); } //獲取所有構(gòu)造器 Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors(); for (Constructor constructor : declaredConstructors) { System.out.println("獲取所有構(gòu)造器:" + constructor); } //獲取指定的構(gòu)造器 Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class, Integer.class); System.out.println("獲取指定的構(gòu)造器:" + declaredConstructor); } }
結(jié)果如下
2. 動(dòng)態(tài)創(chuàng)建對象動(dòng)態(tài)操作方法及屬性
2.1 動(dòng)態(tài)創(chuàng)建對象
通過調(diào)用 Class 對象的 newInstance() 方法,可以創(chuàng)建類的對象
有了類的對象,就可以通過對象.方法名
的形式使用方法了,這和平常的使用方式一樣,但需要注意設(shè)置的訪問權(quán)限問題
使用 Class 對象創(chuàng)建類的對象是需要條件的,不同情況下,條件不同
使用無參構(gòu)造器時(shí)的條件
- 無參構(gòu)造器存在
- 無參構(gòu)造器的訪問權(quán)限要足夠
//獲取Class對象 Class<?> aClass = Class.forName("com.javabasic.reflection.Person"); //通過一個(gè)無參構(gòu)造器創(chuàng)建一個(gè)Person的對象 Person person01 = (Person) aClass.newInstance(); System.out.println(person01);
使用有參構(gòu)造器時(shí)的條件
- 通過 Class 類的 getDeclaredConstructor(Class<?>… parameterTypes) 獲取當(dāng)前類的指定有參構(gòu)造器
- 向構(gòu)造器的形參中傳遞一個(gè)對象數(shù)組進(jìn)去,里面要包含構(gòu)造器中所需的各個(gè)參數(shù)
- 通過 Constructor 實(shí)例化對象
//獲取Class對象 Class<?> aClass = Class.forName("com.javabasic.reflection.Person"); //通過有參構(gòu)造器創(chuàng)建對象 Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class, Integer.class); Person person02 = (Person) declaredConstructor.newInstance("張三", 20); System.out.println(person02);
2.2 動(dòng)態(tài)操作方法及屬性
這里延用上面的 Person.java 類,然后新建一個(gè)測試類來測試
package com.javabasic.reflection; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * @Description * @ClassName Test01 * @Author yuhuofei * @Date 2022/10/2 17:08 * @Version 1.0 */ public class Test01 { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException { //獲取Class對象 Class<?> aClass = Class.forName("com.javabasic.reflection.Person"); //通過一個(gè)無參構(gòu)造器創(chuàng)建一個(gè)Person的對象 Person person01 = (Person) aClass.newInstance(); System.out.println(person01); //通過有參構(gòu)造器創(chuàng)建對象 Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class, Integer.class); Person person02 = (Person) declaredConstructor.newInstance("張三", 20); System.out.println(person02); //通過反射調(diào)用普通方法 Method method = aClass.getDeclaredMethod("setName", String.class); method.setAccessible(true); //關(guān)閉安全檢測,允許訪問private方法 Person person03 = (Person) aClass.newInstance(); method.invoke(person03, "李四"); //向person03對象中的setName方法傳參 System.out.println(person03); //通過反射調(diào)用屬性 Field field01 = aClass.getDeclaredField("name"); Field field02 = aClass.getDeclaredField("age"); field01.setAccessible(true); //關(guān)閉安全檢測,允許訪問private屬性 field02.setAccessible(true); Person person04 = (Person) aClass.newInstance(); field01.set(person04, "王五"); //設(shè)置名字 field02.set(person04, 19); //設(shè)置年齡 System.out.println(person04); } }
值得注意的是,如果方法或者屬性的訪問權(quán)限是 private,那么需要配置 .setAccessible(true);
,不然無法訪問
3. 反射操作泛型
java 采用泛型擦除的機(jī)制來引入泛型,但卻只是給編譯器 javac 使用的,用于確保數(shù)據(jù)的安全性和免去強(qiáng)制類型轉(zhuǎn)換的問題,而一旦編譯完成,所有與泛型相關(guān)的類型會(huì)全部擦除
為了通過反射能操作這些類型,java 新增了 ParameterizedType、GenericArrayType、TypeVariable、WildcardType 這幾種類型來代表不能被歸一到 Class 類中的類型但又和原始類型齊名的類型
- ParameterizedType:表示一種參數(shù)化類型,比如 Collection< String >
- GenericArrayType:表示一種元素類型是參數(shù)化類型或者類型變量的數(shù)組類型
- TypeVariable:各種類型變量的公共父接口
- WildcardType :代表一種通配符類型表達(dá)式
下面寫一個(gè)類來實(shí)踐一下,這里還是延用前面的 Person 類
package com.javabasic.reflection; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.List; import java.util.Map; /** * @Description 測試反射獲取泛型 * @ClassName Test02 * @Author yuhuofei * @Date 2022/10/2 20:06 * @Version 1.0 */ public class Test02 { public void getData(Map<String, Person> map, List<Person> list) { System.out.println("getData()"); } public Map<String, Person> getMap() { System.out.println("getMap()"); return null; } public static void main(String[] args) throws NoSuchMethodException { //獲取入?yún)⒎盒? Method method = Test02.class.getMethod("getData", Map.class, List.class); Type[] genericParameterTypes = method.getGenericParameterTypes(); for (Type genericParameterType : genericParameterTypes) { System.out.println("泛型:" + genericParameterType); //判斷拿到的是不是參數(shù)化類型,即是不是前面定義的Map或者List if (genericParameterType instanceof ParameterizedType) { //獲取真實(shí)參數(shù) Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println(actualTypeArgument); } } } System.out.println("============分割線==========="); //獲取返回值泛型 method = Test02.class.getMethod("getMap", null); Type genericReturnType = method.getGenericReturnType(); System.out.println("返回值類型:" + genericReturnType); if (genericReturnType instanceof ParameterizedType) { Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments(); for (Type at : actualTypeArguments) { System.out.println(at); } } } }
得到的結(jié)果如下:
4. 反射操作注解
定義一個(gè)注解,如下:
package com.javabasic.annotation; import java.lang.annotation.*; /** * @Description 自定義注解 * @InterfaceName MyAnnotation * @Author yuhuofei * @Date 2022/9/18 11:26 * @Version 1.0 */ @Target({ElementType.TYPE, ElementType.METHOD}) @Documented @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { /** * 描述信息 */ String value() default ""; }
定義一個(gè)使用上面的注解的類,如下:
package com.javabasic.annotation; /** * @Description 測試自定義注解的使用 * @ClassName TestAnnotation * @Author yuhuofei * @Date 2022/9/18 11:32 * @Version 1.0 */ @MyAnnotation(value = "打印出兩個(gè)整數(shù)之和,這是標(biāo)記在類上的注解") public class TestAnnotation { @MyAnnotation(value = "打印出兩個(gè)整數(shù)之和,這是標(biāo)記在方法上的注解") public static void printResult() { int a = 39; int b = 54; int result = a + b; System.out.println(result); } public static void main(String[] args) { printResult(); } }
定義一個(gè)測試類,測試通過反射操作注解,如下:
package com.javabasic.annotation; import java.lang.annotation.Annotation; import java.lang.reflect.Method; /** * @Description 測試反射獲得注解 * @ClassName Test03 * @Author yuhuofei * @Date 2022/10/2 20:36 * @Version 1.0 */ public class Test03 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException { Class c = Class.forName("com.javabasic.annotation.TestAnnotation"); //通過反射獲得注解 Annotation[] annotations = c.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation); } //獲得注解的value MyAnnotation annotation = (MyAnnotation) c.getAnnotation(MyAnnotation.class); String value = annotation.value(); System.out.println(value); //獲得類中方法或者屬性上指定的注解 System.out.println("============分割線==========="); Method method = c.getMethod("printResult", null); MyAnnotation an = method.getAnnotation(MyAnnotation.class); System.out.println(an); System.out.println(an.value()); } }
運(yùn)行結(jié)果:
到此這篇關(guān)于Java中反射的應(yīng)用的文章就介紹到這了,更多相關(guān)Java反射內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java class文件格式之屬性詳解_動(dòng)力節(jié)點(diǎn)java學(xué)院整理
這篇文章主要介紹了Java class文件格式之屬性詳解,需要的朋友可以參考下2017-06-06Java 內(nèi)省(Introspector)深入理解
這篇文章主要介紹了Java 內(nèi)省(Introspector)深入理解的相關(guān)資料,需要的朋友可以參考下2017-03-03SpringBoot為啥不用配置啟動(dòng)類的實(shí)現(xiàn)
這篇文章主要介紹了SpringBoot為啥不用配置啟動(dòng)類的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04Spring4下validation數(shù)據(jù)校驗(yàn)無效(maven)的解決
這篇文章主要介紹了Spring4下validation數(shù)據(jù)校驗(yàn)無效(maven)的解決,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06Java調(diào)用opencv實(shí)現(xiàn)圖片矯正功能
這篇文章主要為大家詳細(xì)介紹了Java如何調(diào)用opencv實(shí)現(xiàn)圖片矯正功能,文中的示例代碼簡潔易懂,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-09-09Spring boot詳解緩存redis實(shí)現(xiàn)定時(shí)過期方法
本篇文章分享的就是spring boot中的一個(gè)輪子,spring cache注解的方式實(shí)現(xiàn)接口數(shù)據(jù)緩存。默認(rèn)的配置想非常簡單,但是有一個(gè)弊端是緩存數(shù)據(jù)為永久緩存,本次將介紹如何設(shè)置接口緩存數(shù)據(jù)的過期時(shí)間2022-07-07Java中數(shù)組與集合的相互轉(zhuǎn)換實(shí)現(xiàn)解析
這篇文章主要介紹了Java中數(shù)組與集合的相互轉(zhuǎn)換實(shí)現(xiàn)解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-08-08Java中JSONObject與JSONArray的使用區(qū)別詳解
這篇文章主要介紹了Java中JSONObject與JSONArray的使用區(qū)別詳解,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-11-11