Java開(kāi)發(fā)反射機(jī)制的實(shí)戰(zhàn)經(jīng)驗(yàn)總結(jié)
前言
我在實(shí)際項(xiàng)目當(dāng)中有經(jīng)常用到反射機(jī)制,故而將學(xué)會(huì)的反射用法做一些匯總筆記,當(dāng)做以后復(fù)盤(pán)所用。
存在這樣一個(gè)類(lèi):
package com.example.demo;
import com.alibaba.fastjson.annotation.JSONField;
public class User {
private String name;
@Value( value ="age_a")
private String age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
一、創(chuàng)建Class的三種方式
1 - Class clazz = Class.forName("com.example.demo.User");

注意一點(diǎn),這里的forName("xxx")的類(lèi)名需要全名,且為接口或類(lèi),否則加載不了。
2 - User user = new User();
Class clazz2 = user.getClass();

3 - Class clazz3 = User.class;
以上三種方式,都可以獲取到類(lèi)User的Class對(duì)象,通過(guò)Class,即可以開(kāi)始玩反射了。
二、反射獲取類(lèi)的所有屬性和屬性類(lèi)型
Class clazz = User.class;
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
System.out.println("屬性名:"+field.getName());
System.out.println("屬性的類(lèi)型:"+field.getGenericType().getTypeName());
}
打印輸出User的屬性和屬性類(lèi)型——
屬性名:name
屬性的類(lèi)型:java.lang.String
屬性名:age
屬性的類(lèi)型:java.lang.String
利用反射獲取到類(lèi)的字段屬性后,是不是可以利用反射來(lái)創(chuàng)建一個(gè)對(duì)象呢?答案是肯定的。
例如,可以類(lèi)似下面代碼,通過(guò)反射得到的字段屬性,進(jìn)而創(chuàng)建一個(gè)對(duì)象。
Map<String,Object> fileds = new HashMap<>();
fileds.put("name","張三");
fileds.put("age","10");
Object o = User.class.newInstance();
Field[] fields = o.getClass().getDeclaredFields();
for (Field field : fields) {
//設(shè)置后可用反射訪問(wèn)訪問(wèn)私有變量
field.setAccessible(true);
//通過(guò)反射給屬性賦值
field.set(o,fileds.get(field.getName()));
}
User user1 = (User) o;
System.out.println(user1.toString());
什么場(chǎng)景下可能需要這樣做的呢?像一些內(nèi)部數(shù)據(jù)與外部數(shù)據(jù)字段的映射,就可以通過(guò)類(lèi)似的字段反射方式,將源數(shù)據(jù)映射給目標(biāo)數(shù)據(jù),進(jìn)而得到可以插入數(shù)據(jù)庫(kù)的目標(biāo)對(duì)象。
三、反射動(dòng)態(tài)修改類(lèi)屬性的注解值
注意一點(diǎn),我們?cè)谠O(shè)置User類(lèi)時(shí),對(duì)其中一個(gè)字段加了注解:@Value( value ="age_a")。這是一種設(shè)置值的注解,既然是設(shè)置值,是否還可以在代碼運(yùn)行過(guò)程中,根據(jù)不同情況來(lái)動(dòng)態(tài)修改呢?
字段上的注解,其實(shí)都存放在一個(gè)memberValues屬性里,這是一個(gè)map,可以這樣來(lái)獲取——
Field[] fields = User.class.getDeclaredFields();
for (Field field : fields) {
//設(shè)置后可用反射訪問(wèn)訪問(wèn)私有變量
if ("age".equals(field.getName() )){
field.setAccessible(true);
//獲取 annotation 這個(gè)代理實(shí)例所持有的 InvocationHandler
InvocationHandler invocationHandler = Proxy.getInvocationHandler(field.getAnnotation(Value.class));
// 獲取 InvocationHandler 的 memberValues 字段
Field memberValues = invocationHandler.getClass().getDeclaredField("memberValues");
memberValues.setAccessible(true);
Map<String, Object> values = (Map<String, Object>) memberValues.get(invocationHandler);
System.out.println(values);
}
}
debug打斷點(diǎn),可以看到——

這個(gè)Map<String,Object>存儲(chǔ)的是該@注解里的所有屬性值,這里,@Value只有一個(gè)value屬性——
public @interface Value {
String value();
}
若把它換成類(lèi)似@JSONField(name="age_a"),把上邊的代碼稍微修改下,如:
Field[] fields = User.class.getDeclaredFields();
for (Field field : fields) {
if ("age".equals(field.getName() )){
field.setAccessible(true);
InvocationHandler invocationHandler = Proxy.getInvocationHandler(field.getAnnotation(JSONField.class));
......
}
}
@JSONField注解的內(nèi)部屬性有如下方式——

再運(yùn)行剛剛的代碼,可以看到,這里Map<String,Object>獲取存儲(chǔ)到的,便是這個(gè)注解里所有的屬性與對(duì)應(yīng)的屬性值。

到了這一步,回到先前上邊的問(wèn)題,若要?jiǎng)討B(tài)改變這個(gè)注解的值,怎么處理呢?
其實(shí),很簡(jiǎn)單,只需要直接進(jìn)行值設(shè)置就可以了,例如——
InvocationHandler invocationHandler = Proxy.getInvocationHandler(field.getAnnotation(Value.class));
Field memberValues = invocationHandler.getClass().getDeclaredField("memberValues");
memberValues.setAccessible(true);
Map<String, Object> values = (Map<String, Object>) memberValues.get(invocationHandler);
values.put("value","new_age");
memberValues.setAccessible(false);
只是,注意一點(diǎn)是,這里的key需要對(duì)應(yīng)上注解里是屬性值。
四、反射獲取類(lèi)的方法及調(diào)用方式
Object o=User.class.newInstance();
//通過(guò)反射獲取到User的setAge方法,后面的String.class表示這個(gè)setAge方法的參數(shù)類(lèi)型,若有多個(gè),則按順序列出
//同時(shí),若為其他類(lèi)型,如List,Long,則為L(zhǎng)ist.class,Long.class
Method m = (Method) o.getClass().getMethod("setAge",String.class);
m.invoke(o,"name");
User user = (User) o;
System.out.println(user);
打印可見(jiàn),age已為name,說(shuō)明setAge調(diào)用成功了。

這類(lèi)使用場(chǎng)景,在代理當(dāng)中出現(xiàn)比較多。
最后,通過(guò)反射實(shí)現(xiàn)一個(gè)Map轉(zhuǎn)成對(duì)象的封裝工具——
public Object MapToObject(Object object,Map<String, Object> map) throws IllegalAccessException {
Class cla = object.getClass();
Field[] fields = cla.getDeclaredFields();
for(Field field:fields){
field.setAccessible(true);
if("serialVersionUID".equals(field.getName()))continue;
if(map.get(field.getName())!=null) {
Object value=map.get(field.getName());
value=convertValType(value,field.getType());
field.set(object, value);
}
}
return object;
}
private static Object convertValType(Object value, Class<?> fieldTypeClass) {
Object o = null;
if (Long.class.getName().equals(fieldTypeClass.getName())
|| long.class.getName().equals(fieldTypeClass.getName())) {
o = Long.parseLong(value.toString());
} else if (Integer.class.getName().equals(fieldTypeClass.getName())
|| int.class.getName().equals(fieldTypeClass.getName())) {
o = Integer.parseInt(value.toString());
} else if (Float.class.getName().equals(fieldTypeClass.getName())
|| float.class.getName().equals(fieldTypeClass.getName())) {
o = Float.parseFloat(value.toString());
} else if (Double.class.getName().equals(fieldTypeClass.getName())
|| double.class.getName().equals(fieldTypeClass.getName())) {
o = Double.parseDouble(value.toString());
} else {
retVal = o;
}
return retVal;
}
總結(jié)
到此這篇關(guān)于Java反射機(jī)制的文章就介紹到這了,更多相關(guān)Java開(kāi)發(fā)反射機(jī)制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring?AOP?后置通知修改響應(yīng)httpstatus方式
這篇文章主要介紹了Spring?AOP?后置通知修改響應(yīng)httpstatus方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
java 實(shí)現(xiàn)下壓棧的操作(能動(dòng)態(tài)調(diào)整數(shù)組大小)
這篇文章主要介紹了java 實(shí)現(xiàn)下壓棧的操作(能動(dòng)態(tài)調(diào)整數(shù)組大小),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-02-02
Java Elastic Job動(dòng)態(tài)添加任務(wù)實(shí)現(xiàn)過(guò)程解析
這篇文章主要介紹了Java Elastic Job動(dòng)態(tài)添加任務(wù)實(shí)現(xiàn)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08
MyBatis入門(mén)學(xué)習(xí)教程-MyBatis快速入門(mén)
MyBatis是一個(gè)支持普通SQL查詢,存儲(chǔ)過(guò)程和高級(jí)映射的優(yōu)秀持久層框架,這篇文章主要給大家分享MyBatis的一篇快速入門(mén)教程2021-06-06
關(guān)于java String中intern的深入講解
這篇文章主要給大家介紹了關(guān)于java String中intern的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
Socket結(jié)合線程池使用實(shí)現(xiàn)客戶端和服務(wù)端通信demo
這篇文章主要為大家介紹了Socket結(jié)合線程池的使用來(lái)實(shí)現(xiàn)客戶端和服務(wù)端通信實(shí)戰(zhàn)demo,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-03-03
Eclipse創(chuàng)建JavaWeb工程的完整步驟記錄
很多新手不知道Eclipse怎么創(chuàng)建Java Web項(xiàng)目,一起來(lái)看看吧,這篇文章主要給大家介紹了關(guān)于Eclipse創(chuàng)建JavaWeb工程的完整步驟,需要的朋友可以參考下2023-10-10

