Java 的反射、內(nèi)省,你會(huì)用嗎?
你好,我是看山。
Java的反射和內(nèi)省是兩個(gè)在運(yùn)行時(shí)操作類和對(duì)象的強(qiáng)大機(jī)制,它們之間存在關(guān)聯(lián)和區(qū)別。很多時(shí)候我們用錯(cuò)了,就會(huì)有性能上的損失。
概念
反射(Reflection)
反射是Java語(yǔ)言的一個(gè)特性,允許程序在運(yùn)行時(shí)檢查和操作類、方法、字段等。反射可以動(dòng)態(tài)地獲取類的所有屬性和方法,并且可以動(dòng)態(tài)調(diào)用這些方法。
反射強(qiáng)調(diào)的是運(yùn)行狀態(tài),即在程序運(yùn)行時(shí)能夠訪問和修改類的狀態(tài)或行為。反射提供了更底層的類結(jié)構(gòu)和行為訪問機(jī)制。
反射的核心類都在java.reflect包下,主要類如下:
圖片
圖片
內(nèi)省(Introspection)
內(nèi)省是基于反射實(shí)現(xiàn)的,主要用于操作符合JavaBean規(guī)范的類。
JavaBean是一種特殊的Java類,通常用于封裝多個(gè)屬性為一個(gè)單一的對(duì)象。
內(nèi)省機(jī)制通過(guò)反射獲取屬性描述器(PropertyDescriptor),然后可以方便地獲取和設(shè)置屬性值。
內(nèi)省操作只針對(duì)JavaBean,只有符合JavaBean規(guī)則的類的成員才可以采用內(nèi)省API進(jìn)行操作。
內(nèi)省的核心類在java.beans包下,主要類如下:
圖片
關(guān)系與區(qū)別
- 適用范圍:
反射可以操作任何類的所有成員,包括私有成員。
內(nèi)省主要針對(duì)JavaBean,只能操作符合JavaBean規(guī)范的類的成員。
- 操作方式:
- 反射是先得到類的字節(jié)碼(Class)后再進(jìn)行各種操作。
- 內(nèi)省是先得到屬性描述器(PropertyDescriptor)后再進(jìn)行各種操作。
- 復(fù)雜度:
- 反射提供了更底層的訪問機(jī)制,使用起來(lái)相對(duì)復(fù)雜。
- 內(nèi)省提供了一套比較簡(jiǎn)單和有層次的API,更專注于JavaBean的屬性操作。
- 應(yīng)用場(chǎng)景:
- 反射適用于需要?jiǎng)討B(tài)獲取和調(diào)用類信息的場(chǎng)景,如框架開發(fā)。
- 內(nèi)省適用于需要操作JavaBean屬性的場(chǎng)景。
來(lái)點(diǎn)小栗子
使用反射創(chuàng)建對(duì)象并調(diào)用方法
我們通過(guò)反射創(chuàng)建對(duì)象并調(diào)用指定方法:
public static void main(String[] args)
throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException,
IllegalAccessException, NoSuchFieldException {
final Class<?> clazz = MyClass.class;
// Class<?> clazz = new MyClass().getClass();
// Class<?> clazz = Class.forName("cn.howardliu.tutorials.core.reflect.ReflectDemo.MyClass");
final Constructor<?> constructor = clazz.getConstructor();
// Constructor<?> constructor = clazz.getConstructor(String.class);
final Object o = constructor.newInstance();
final Field field = clazz.getDeclaredField("name");
field.setAccessible(true);
field.set(o, "看山");
final Method method = clazz.getMethod("echoName");
final Object result = method.invoke(o);
System.out.println(result);
}
@Data
@NoArgsConstructor
@AllArgsConstructor
publicstaticclass MyClass {
private String name;
public String echoName() {
return name;
}
}
首先,我們獲取class對(duì)象,有三種方式:
- 通過(guò)類名直接獲?。篊lass<?> clazz = MyClass.class;
- 通過(guò)實(shí)例獲?。篊lass<?> clazz = new MyClass().getClass();
- 通過(guò)加載類:Class<?> clazz = Class.forName("cn.howardliu.tutorials.core.reflect.ReflectDemo.MyClass"); 。
然后獲取構(gòu)造器對(duì)象,使用Class對(duì)象的getConstructor()或getDeclaredConstructor()方法獲取構(gòu)造器對(duì)象。
- 無(wú)參構(gòu)造函數(shù):Constructor<?> constructor = clazz.getConstructor();
- 有參構(gòu)造函數(shù):Constructor<?> constructor = clazz.getConstructor(String.class); 。
使用newInstance()創(chuàng)建實(shí)體對(duì)象:final Object o = constructor.newInstance();。
示例中我們對(duì)name屬性賦值,首先得獲取Field對(duì)象,可以用getDeclaredField()或者getField()。
因?yàn)閚ame屬性是非public的,調(diào)用setAccessible設(shè)置可訪問,然后賦值。
最后就是獲取Method對(duì)象,使用invoke方法實(shí)現(xiàn)對(duì)象的動(dòng)作。
以上就是反射常用的邏輯了。反射還提供了基于枚舉的API,有需要的時(shí)候可以用一用。
通過(guò)內(nèi)省給對(duì)象賦值
內(nèi)省就是轉(zhuǎn)為JavaBean準(zhǔn)備的,主要包括下面幾個(gè)類:
- Introspector類:這是內(nèi)省API的核心類,提供了獲取BeanInfo對(duì)象的方法,例如Introspector.getBeanInfo()方法。
- BeanInfo類:這個(gè)類包含了關(guān)于一個(gè)對(duì)象的所有Bean屬性信息,包括屬性的描述符(PropertyDescriptor)。
- PropertyDescriptor類:表示一個(gè)JavaBean屬性的信息,包括getter和setter方法。
以下是一個(gè)簡(jiǎn)單的示例,我們首先獲取User的JavaBean信息BeanInfo,然后找到屬性描述符PropertyDescriptor列表。
使用屬性的讀方法獲取數(shù)據(jù),使用寫方法賦值。
public static void main(String[] args)
throws IntrospectionException, InvocationTargetException, IllegalAccessException {
final BeanInfo beanInfo = Introspector.getBeanInfo(User.class);
final User user = new User();
// 遍歷所有屬性描述符
for (PropertyDescriptor prop : beanInfo.getPropertyDescriptors()) {
System.out.println("Property Name: " + prop.getName());
// 獲取getter方法
final Method readMethod = prop.getReadMethod();
if (readMethod != null) {
Object value = readMethod.invoke(user);
System.out.println("Property Value: " + value);
}
if ("username".equals(prop.getName())) {
// 獲取setter方法
final Method writeMethod = prop.getWriteMethod();
if (writeMethod != null) {
Object value = writeMethod.invoke(user, "看山");
System.out.println("Property Value: " + value);
}
}
}
System.out.println(user);
}
@Data
publicstaticclass User {
private String username;
}
在JavaBean的操作方面,內(nèi)省確實(shí)比反射更方便。
文末總結(jié)
今天通過(guò)示例介紹了Java的反射和內(nèi)省,下次我們看看在Bean賦值方面,兩者的性能差異有多少。