Java系列之反射
最近知識(shí)比較零碎,不適合整理成文,來(lái)一篇以前的關(guān)于反射的學(xué)習(xí)筆記,主要內(nèi)容如下:
反射機(jī)制
反射獲取類(lèi)的信息
反射操作類(lèi)的信息
反射獲取泛型
反射獲取注解信息
反射機(jī)制
Java 的反射機(jī)制是指在運(yùn)行狀態(tài)下,對(duì)于任意一個(gè)類(lèi),都能夠知道這個(gè)類(lèi)的所有屬性和方法,反射是一種可在代碼運(yùn)行時(shí)動(dòng)態(tài)獲取類(lèi)的信息的一種機(jī)制,可通過(guò)反射獲取在編譯期不可能獲取到的類(lèi)的信息,當(dāng)一個(gè)任意類(lèi)被類(lèi)加載器(ClassLoader)首次加載之后會(huì)自動(dòng)生成一個(gè)該類(lèi)對(duì)應(yīng)的 Class 對(duì)象,這個(gè) Class 對(duì)象保存了對(duì)應(yīng)類(lèi)的所有信息。這種當(dāng)一個(gè)任意類(lèi)被類(lèi)加載器加載之后,動(dòng)態(tài)獲取 Class 對(duì)象的信息以及動(dòng)態(tài)操作 Class 對(duì)象的屬性和方法的功能稱(chēng)之為 Java 的反射機(jī)制。Java 反射機(jī)制的關(guān)鍵是獲取某個(gè)任意類(lèi)的 Class 對(duì)象,下面的內(nèi)容就是如何通過(guò)這個(gè) Class 對(duì)象獲取和動(dòng)態(tài)操作類(lèi)的相關(guān)信息,為了便于后文中對(duì)反射的使用,這里先創(chuàng)建一個(gè) User 類(lèi)供下文中使用,具體如下:1package?com.manu.reflection.bean;
2
3/**
4?*?反射測(cè)試類(lèi)
5?*/
6public?class?User?{
7
8????private?int?id;
9????private?String?name;
10????private?String?password;
11
12????public?User()?{
13????????super();
14????}
15????public?User(int?id,?String?name,?String?password)?{
16????????super();
17????????this.id?=?id;
18????????this.name?=?name;
19????????this.password?=?password;
20????}
21????public?int?getId()?{
22????????return?id;
23????}
24????public?void?setId(int?id)?{
25????????this.id?=?id;
26????}
27????public?String?getName()?{
28????????return?name;
29????}
30????public?void?setName(String?name)?{
31????????this.name?=?name;
32????}
33????public?String?getPassword()?{
34????????return?password;
35????}
36????public?void?setPassword(String?password)?{
37????????this.password?=?password;
38????}
39????@Override
40????public?String?toString()?{
41????????return?"User?[id="?+?id?+?",?name="?+?name?+?",?password="?+?password?+?"]";
42????}
43
44}
反射獲取類(lèi)的信息
這里總結(jié)一下如何獲取類(lèi)的基本信息,如類(lèi)的構(gòu)造方法、屬性、方法等,可通過(guò)某個(gè)類(lèi)對(duì)應(yīng)的 Class 對(duì)象對(duì)應(yīng)的 getter 方法獲取某個(gè)類(lèi)的名稱(chēng)、構(gòu)造方法、屬性、方法等,下面以獲取某個(gè)類(lèi)的構(gòu)造方法為例說(shuō)明獲取方式不同:getConstructors:表示獲取某個(gè)類(lèi)中修飾符為 public 的所有構(gòu)造方法,如 getFields()、getMethods() 還包括從父類(lèi)繼承來(lái)的 public 修飾的屬性和方法。
getDeclaredConstructors:表示獲取某個(gè)類(lèi)中所有聲明的構(gòu)造方法,只限定于本類(lèi)中。
1/**通過(guò)反射獲取某個(gè)類(lèi)的相關(guān)信息主要如上,來(lái)上述代碼的執(zhí)行結(jié)果如下:
2?*?反射獲取類(lèi)的信息
3?*/
4private?static?void?getReflectClassInfo()?{
5????try?{
6????????//獲取某個(gè)類(lèi)的Class對(duì)象
7????????String?name?=?"com.manu.reflection.bean.User";
8????????Class?clazz?=?Class.forName(name);
9
10????????//反射獲取類(lèi)的名稱(chēng)
11????????System.out.println("----------反射獲取類(lèi)的名稱(chēng)----------");
12????????String?n1?=?clazz.getName();//完整路徑:包名+類(lèi)名?(com.manu.reflection.bean.User)
13????????String?n2?=?clazz.getSimpleName();//類(lèi)名(User)
14????????System.out.println("獲取類(lèi)的名稱(chēng)n1:"+n1);
15????????System.out.println("獲取類(lèi)的名稱(chēng)n2:"+n2);
16
17????????//反射獲取類(lèi)的構(gòu)造方法
18????????System.out.println("----------反射獲取類(lèi)的構(gòu)造方法----------");
19????????Constructor?c1?=?clazz.getDeclaredConstructor(null);
20????????System.out.println("獲取無(wú)參構(gòu)造方法:"+c1);
21????????Constructor?c2?=?clazz.getDeclaredConstructor(int.class,String.class,String.class);
22????????System.out.println("獲取有參構(gòu)造方法:"+c2);
23????????Constructor[]?constructors?=?clazz.getDeclaredConstructors();
24????????for(Constructor?c:?constructors)?{
25????????????System.out.println("獲取所有的構(gòu)造方法:"+c);
26????????}
27
28????????//反射獲取類(lèi)的屬性
29????????System.out.println("----------反射獲取類(lèi)的屬性----------");
30????????Field?f1?=?clazz.getDeclaredField("name");
31????????System.out.println("獲取名稱(chēng)為name的屬性:"+f1);
32????????Field[]?fields?=?clazz.getDeclaredFields();
33????????for(Field?f?:?fields)?{
34????????????System.out.println("獲取所有的屬性:"+f);
35????????}
36
37????????//反射獲取類(lèi)的方法
38????????System.out.println("----------反射獲取類(lèi)的方法----------");
39????????Method?m1?=?clazz.getDeclaredMethod("getName",?null);//獲取無(wú)參方法
40????????Method?m2?=?clazz.getDeclaredMethod("setName",?String.class);//獲取有參方法
41????????System.out.println("獲取方法名為getName的方法m1:"+m1);
42????????System.out.println("獲取方法名為setName的方法m2:"+m2);
43????????Method[]?mathods?=?clazz.getDeclaredMethods();
44????????for(Method?m:?mathods)?{
45????????????System.out.println("獲取所有方法:"+m);
46????????}
47
48????}?catch?(Exception?e)?{
49????????e.printStackTrace();
50????}
51}
1----------反射獲取類(lèi)的名稱(chēng)----------
2獲取類(lèi)的名稱(chēng)n1:com.manu.reflection.bean.User
3獲取類(lèi)的名稱(chēng)n2:User
4----------反射獲取類(lèi)的構(gòu)造方法----------
5獲取無(wú)參構(gòu)造方法:public?com.manu.reflection.bean.User()
6獲取有參構(gòu)造方法:public?com.manu.reflection.bean.User(int,java.lang.String,java.lang.String)
7獲取所有的構(gòu)造方法:public?com.manu.reflection.bean.User()
8獲取所有的構(gòu)造方法:public?com.manu.reflection.bean.User(int,java.lang.String,java.lang.String)
9----------反射獲取類(lèi)的屬性----------
10獲取名稱(chēng)為name的屬性:private?java.lang.String?com.manu.reflection.bean.User.name
11獲取所有的屬性:private?int?com.manu.reflection.bean.User.id
12獲取所有的屬性:private?java.lang.String?com.manu.reflection.bean.User.name
13獲取所有的屬性:private?java.lang.String?com.manu.reflection.bean.User.password
14----------反射獲取類(lèi)的方法----------
15獲取方法名為getName的方法m1:public?java.lang.String?com.manu.reflection.bean.User.getName()
16獲取方法名為setName的方法m2:public?void?com.manu.reflection.bean.User.setName(java.lang.String)
17獲取所有方法:public?java.lang.String?com.manu.reflection.bean.User.toString()
18獲取所有方法:public?java.lang.String?com.manu.reflection.bean.User.getName()
19獲取所有方法:public?int?com.manu.reflection.bean.User.getId()
20獲取所有方法:public?void?com.manu.reflection.bean.User.setName(java.lang.String)
21獲取所有方法:public?java.lang.String?com.manu.reflection.bean.User.getPassword()
22獲取所有方法:public?void?com.manu.reflection.bean.User.setId(int)
23獲取所有方法:public?void?com.manu.reflection.bean.User.setPassword(java.lang.String)
反射操作類(lèi)的信息
通過(guò) Java 的反射機(jī)制可以獲取的某個(gè)類(lèi)的構(gòu)造方法、屬性以及方法,然后就可以對(duì)該類(lèi)進(jìn)行相關(guān)操作了,下面是將通過(guò) Java 的反射機(jī)制構(gòu)建該類(lèi)的對(duì)象、操作該類(lèi)對(duì)象的屬性以及調(diào)用該類(lèi)對(duì)象的方法,具體參考如下:1/**上述代碼的執(zhí)行結(jié)果如下:
2?*?反射操作類(lèi)的信息
3?*/
4private?static?void?setReflectClassInfo()?{
5????try?{
6????????//獲取某個(gè)類(lèi)的Class對(duì)象
7????????String?name?=?"com.manu.reflection.bean.User";
8????????Class?clazz?=?Class.forName(name);
9
10????????//反射操作類(lèi)的構(gòu)造方法
11????????System.out.println("----------反射操作類(lèi)的構(gòu)造方法----------");
12????????Constructor?c1?=?clazz.getDeclaredConstructor(null);//獲取無(wú)參構(gòu)造方法
13????????Constructor?c2?=?clazz.getDeclaredConstructor(int.class,String.class,String.class);//獲取帶參構(gòu)造方法
14????????User?u1?=?c1.newInstance();
15????????User?u2?=?c2.newInstance(1000,"jzman-blog","111111");
16????????System.out.println("u1:"+u1);
17????????System.out.println("u2:"+u2);
18
19????????//反射操作類(lèi)的屬性
20????????System.out.println("----------反射操作類(lèi)的屬性----------");
21????????User?u3?=?c1.newInstance();
22????????Field?field?=?clazz.getDeclaredField("name");
23????????field.setAccessible(true);//設(shè)置該屬性不需要安全檢查,可直接放訪問(wèn)
24????????field.set(u3,?"jzman");//反射設(shè)置User對(duì)象的name屬性值
25????????System.out.println("u3:"+u3);
26????????System.out.println("獲取User對(duì)象u3的name屬性值:"+field.get(u3));
27
28????????//反射操作類(lèi)的方法
29????????System.out.println("----------反射操作類(lèi)的方法----------");
30????????User?u4?=?c1.newInstance();
31????????Method?method?=?clazz.getDeclaredMethod("setPassword",?String.class);
32????????method.invoke(u4,?"222222");//設(shè)置User對(duì)象u4的password屬性,等同于u4.setPassword("222222);
33????????System.out.println("u4:"+u4);
34
35????}?catch?(Exception?e)?{
36????????e.printStackTrace();
37????}
38}
1----------反射操作類(lèi)的構(gòu)造方法----------實(shí)際開(kāi)發(fā)中肯定會(huì)遇到某個(gè)組件中需要某個(gè)屬性,但是該屬性又是私有的,這時(shí)候就可以使用 Java 的反射機(jī)制了。
2u1:User?[id=0,?name=null,?password=null]
3u2:User?[id=1000,?name=jzman-blog,?password=111111]
4----------反射操作類(lèi)的屬性----------
5u3:User?[id=0,?name=jzman,?password=null]
6獲取User對(duì)象u3的name屬性值:jzman
7----------反射操作類(lèi)的方法----------
8u4:User?[id=0,?name=null,?password=222222]
反射操作泛型
Java 采用泛型擦除的機(jī)制,Java 中的泛型僅僅是給編譯器 javac 使用的,這樣可確保數(shù)據(jù)的安全性的免去強(qiáng)制類(lèi)型轉(zhuǎn)換的麻煩,當(dāng)編譯完成之后,所有和泛型相關(guān)的類(lèi)型將會(huì)被全部擦除,為了能夠使用反射操作這些類(lèi)型,新增了四種類(lèi)型 GenericArrayType、ParameterizedType、TypeVariable?和 WildcardType 來(lái)表示不能被歸一到 Class 類(lèi)中的類(lèi)型但又是和原始類(lèi)型齊名的類(lèi)型,也就是說(shuō)正常的能夠獲取對(duì)應(yīng)的 Class 對(duì)象的 Type 還是 Class 對(duì)象,如基本數(shù)據(jù)類(lèi)型。反射操作泛型就要涉及到 Java 中的 Type, 在 Java 中 Type 表示所有類(lèi)型的公共接口,這些類(lèi)型包括原始類(lèi)型、參數(shù)化類(lèi)型、數(shù)組類(lèi)型、類(lèi)型變量和基本類(lèi)型,其聲明如下:1/**Type 有四個(gè)直接子接口,具體含義如下:
2?*?Type?是?Java?語(yǔ)言中所有類(lèi)型的公共接口
3?*?這些類(lèi)型包括原始類(lèi)型、參數(shù)化類(lèi)型、數(shù)組類(lèi)型、類(lèi)型變量和基本類(lèi)型
4?*/
5public?interface?Type?{
6????default?String?getTypeName()?{
7????????return?toString();
8????}
9}
1GenericArrayType:表示泛型數(shù)組類(lèi)型,如 ArraryList下面以 GenericArrayType 和 ParameterizedType ?為例來(lái)說(shuō)明如何使用反射來(lái)操作泛型類(lèi)型,具體如下:[] listArrary
2ParameterizedType:表示參數(shù)化類(lèi)型(泛型),如 ArrayListlist
3TypeVariable:表示類(lèi)型變量,如T
4WildcardType:表示一個(gè)通配符類(lèi)型,如 ?、? extends Number 或 ? super?Integer
1//用來(lái)測(cè)試獲取泛型的屬性上述代碼中方法 refectGenericType 的執(zhí)行結(jié)果如下:
2private?String[]?array;
3private?List[]?listArray;
4//用來(lái)測(cè)試獲取泛型的方法
5private?String?testGenericType(Map?map,?String[]?array,?int?name,User?user) ?{
6????System.out.println("testGenericType");
7????return?null;
8}
9
10//通過(guò)反射獲取泛型
11private?static?void?refectGenericType()?{
12????try?{???????
13????????System.out.println("---------GenericArrayType---------");
14
15????????//獲取泛型數(shù)組(GenericArrayType)
16????????Field?field?=?ReflectTest02.class.getDeclaredField("listArray");//獲取屬性listArray
17????????GenericArrayType?type?=?(GenericArrayType)?field.getGenericType();
18????????System.out.println("獲取泛型數(shù)組:"+type);
19
20????????System.out.println("---------ParameterizedType---------");
21
22????????//獲取參數(shù)化類(lèi)型(泛型)(ParameterizedType)
23????????Method?method?=?ReflectTest02.class.getDeclaredMethod("testGenericType",?Map.class,String[].class,int.class,User.class);
24????????Type[]?types?=?method.getGenericParameterTypes();//獲得方法參數(shù)類(lèi)型
25????????for(Type?type1:?types)?{
26????????????System.out.println("方法參數(shù)類(lèi)型:"+type1);
27????????????if(type1?instanceof?ParameterizedType)?{
28????????????????System.out.println("ParameterizedType:"+type1);
29????????????}
30????????}
31
32????}?catch?(Exception?e)?{
33????????e.printStackTrace();
34????}
35}
1---------GenericArrayType---------可參照代碼查看對(duì)應(yīng)的輸出結(jié)果。
2獲取泛型數(shù)組:java.util.List[]
3---------ParameterizedType---------
4方法參數(shù)類(lèi)型:java.util.Map
5ParameterizedType:java.util.Map
6方法參數(shù)類(lèi)型:class?[Ljava.lang.String;
7方法參數(shù)類(lèi)型:int
8方法參數(shù)類(lèi)型:class?com.manu.reflection.bean.User
9
反射獲取注解信息
通過(guò)反射還可以獲取注解信息,記如果對(duì)注解比較陌生可以參考之前分享的一篇文章 Java 系列之注解,這里簡(jiǎn)單模仿數(shù)據(jù)庫(kù)表字段與 Java 對(duì)象的屬性是如何通過(guò)注解信息一一對(duì)應(yīng)的,為什么我們使用一些數(shù)據(jù)庫(kù)框架的時(shí)候,通過(guò)一些表注解、字段注解就能夠創(chuàng)建表,這里就涉及到使用反射來(lái)獲取注解信息來(lái)生成 SQL,進(jìn)而生成對(duì)應(yīng)的表。創(chuàng)建兩個(gè)注解分別作為表注解和字段注解,參考如下:1//表注解然后,創(chuàng)建一個(gè)類(lèi)并使用該注解,參考如下:
2@Target(ElementType.TYPE)
3@Retention(RetentionPolicy.RUNTIME)
4public?@interface?TableAnnotation?{
5????String?value();
6}
7
8//字段注解
9@Target(ElementType.FIELD)
10@Retention(RetentionPolicy.RUNTIME)
11public?@interface?FieldAnnotation?{
12????String?column();
13????int?length();
14????String?type();
15}
1/**最后,獲取注解信息,參考如下:
2?*?反射讀取注解信息測(cè)試Bean
3?*?@author?jzman
4?*/
5
6@TableAnnotation(value?=?"student_table")
7public?class?Student?{
8????@FieldAnnotation(column?=?"uid",?length?=?20,?type?=?"int")
9????private?int?sId;
10????@FieldAnnotation(column?=?"name",?length?=?10,?type?=?"varchar")
11????private?String?sName;
12????@FieldAnnotation(column?=?"uiaged",?length?=?3,?type?=?"varchar")
13????private?int?sAge;
14
15????//setter、getter方法
16????//...
17}
1/**上述代碼的執(zhí)行結(jié)果如下:
2?*?反射獲取注解信息
3?*?@author?jzman
4?*/
5public?class?ReflectTest03?{
6????public?static?void?main(String[]?args)?{
7????????try?{
8????????????Class?clazz?=?Class.forName("com.manu.reflection.bean.Student");
9
10????????????//反射獲取類(lèi)的注解信息
11????????????TableAnnotation?tableAnnotation?=?(TableAnnotation)?clazz.getAnnotation(TableAnnotation.class);
12????????????System.out.println("反射獲取類(lèi)的注解信息:"+tableAnnotation);
13
14????????????//反射獲取屬性的注解信息
15????????????Field?field?=?clazz.getDeclaredField("sName");
16????????????FieldAnnotation?fieldAnnotation?=?field.getAnnotation(FieldAnnotation.class);
17????????????System.out.println("反射獲取屬性的注解信息:"+fieldAnnotation);
18
19????????????//獲取其他注解信息使用方式類(lèi)似
20????????????//...
21????????}?catch?(Exception?e)?{
22????????????e.printStackTrace();
23????????}
24????}
25}
1反射獲取類(lèi)的注解信息:@com.manu.reflection.TableAnnotation(value=student_table)顯然,通過(guò)反射獲取到了相應(yīng)的注解信息,這些注解信息標(biāo)注了該類(lèi)對(duì)應(yīng)數(shù)據(jù)庫(kù)表的一些關(guān)鍵信息,然后就可以生成對(duì)應(yīng)的 SQL 語(yǔ)句,這樣就不難理解數(shù)據(jù)庫(kù)表字段與Java對(duì)象屬性的映射關(guān)系了。使用反射獲取私有屬性或私有方法是必須設(shè)置 setAccessible 為 true 才能跳過(guò) Java 安全檢查,從而獲取私有的屬性、方法等,同時(shí)設(shè)置 setAccessible 為 true 在一定程度上可以提高反射的運(yùn)行速度。推薦閱讀:
2反射獲取屬性的注解信息:@com.manu.reflection.FieldAnnotation(column=name,?length=10,?type=varchar)
評(píng)論
圖片
表情
