Java 的反射、內(nèi)省,你會用嗎?
你好,我是看山。
Java的反射和內(nèi)省是兩個在運(yùn)行時操作類和對象的強(qiáng)大機(jī)制,它們之間存在關(guān)聯(lián)和區(qū)別。很多時候我們用錯了,就會有性能上的損失。
概念
反射(Reflection)
反射是Java語言的一個特性,允許程序在運(yùn)行時檢查和操作類、方法、字段等。反射可以動態(tài)地獲取類的所有屬性和方法,并且可以動態(tài)調(diào)用這些方法。
反射強(qiáng)調(diào)的是運(yùn)行狀態(tài),即在程序運(yùn)行時能夠訪問和修改類的狀態(tài)或行為。反射提供了更底層的類結(jié)構(gòu)和行為訪問機(jī)制。
反射的核心類都在java.reflect包下,主要類如下:
圖片
圖片
內(nèi)?。↖ntrospection)
內(nèi)省是基于反射實現(xiàn)的,主要用于操作符合JavaBean規(guī)范的類。
JavaBean是一種特殊的Java類,通常用于封裝多個屬性為一個單一的對象。
內(nèi)省機(jī)制通過反射獲取屬性描述器(PropertyDescriptor),然后可以方便地獲取和設(shè)置屬性值。
內(nèi)省操作只針對JavaBean,只有符合JavaBean規(guī)則的類的成員才可以采用內(nèi)省API進(jìn)行操作。
內(nèi)省的核心類在java.beans包下,主要類如下:
圖片
關(guān)系與區(qū)別
- 適用范圍:
 
反射可以操作任何類的所有成員,包括私有成員。
內(nèi)省主要針對JavaBean,只能操作符合JavaBean規(guī)范的類的成員。
- 操作方式:
 - 反射是先得到類的字節(jié)碼(Class)后再進(jìn)行各種操作。
 - 內(nèi)省是先得到屬性描述器(PropertyDescriptor)后再進(jìn)行各種操作。
 - 復(fù)雜度:
 - 反射提供了更底層的訪問機(jī)制,使用起來相對復(fù)雜。
 - 內(nèi)省提供了一套比較簡單和有層次的API,更專注于JavaBean的屬性操作。
 - 應(yīng)用場景:
 - 反射適用于需要動態(tài)獲取和調(diào)用類信息的場景,如框架開發(fā)。
 - 內(nèi)省適用于需要操作JavaBean屬性的場景。
 
來點小栗子
使用反射創(chuàng)建對象并調(diào)用方法
我們通過反射創(chuàng)建對象并調(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對象,有三種方式:
- 通過類名直接獲?。篊lass<?> clazz = MyClass.class;
 - 通過實例獲?。篊lass<?> clazz = new MyClass().getClass();
 - 通過加載類:Class<?> clazz = Class.forName("cn.howardliu.tutorials.core.reflect.ReflectDemo.MyClass"); 。
 
然后獲取構(gòu)造器對象,使用Class對象的getConstructor()或getDeclaredConstructor()方法獲取構(gòu)造器對象。
- 無參構(gòu)造函數(shù):Constructor<?> constructor = clazz.getConstructor();
 - 有參構(gòu)造函數(shù):Constructor<?> constructor = clazz.getConstructor(String.class); 。
 
使用newInstance()創(chuàng)建實體對象:final Object o = constructor.newInstance();。
示例中我們對name屬性賦值,首先得獲取Field對象,可以用getDeclaredField()或者getField()。
因為name屬性是非public的,調(diào)用setAccessible設(shè)置可訪問,然后賦值。
最后就是獲取Method對象,使用invoke方法實現(xiàn)對象的動作。
以上就是反射常用的邏輯了。反射還提供了基于枚舉的API,有需要的時候可以用一用。
通過內(nèi)省給對象賦值
內(nèi)省就是轉(zhuǎn)為JavaBean準(zhǔn)備的,主要包括下面幾個類:
- Introspector類:這是內(nèi)省API的核心類,提供了獲取BeanInfo對象的方法,例如Introspector.getBeanInfo()方法。
 - BeanInfo類:這個類包含了關(guān)于一個對象的所有Bean屬性信息,包括屬性的描述符(PropertyDescriptor)。
 - PropertyDescriptor類:表示一個JavaBean屬性的信息,包括getter和setter方法。
 
以下是一個簡單的示例,我們首先獲取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)省確實比反射更方便。
文末總結(jié)
今天通過示例介紹了Java的反射和內(nèi)省,下次我們看看在Bean賦值方面,兩者的性能差異有多少。
















 
 
 











 
 
 
 