欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java中反射的20個(gè)使用技巧分享

 更新時(shí)間:2025年05月18日 08:27:28   作者:風(fēng)象南  
Java反射是一種強(qiáng)大的機(jī)制,允許程序在運(yùn)行時(shí)檢查和操作類,接口,字段和方法,本文總結(jié)了20個(gè)關(guān)于Java反射的經(jīng)驗(yàn),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

Java反射是一種強(qiáng)大的機(jī)制,允許程序在運(yùn)行時(shí)檢查和操作類、接口、字段和方法。

盡管它提供了極大的靈活性,但反射也是一把雙刃劍——使用不當(dāng)會(huì)導(dǎo)致性能下降、安全漏洞和難以調(diào)試的代碼。

本文總結(jié)了20個(gè)關(guān)于Java反射的經(jīng)驗(yàn)。

基礎(chǔ)最佳實(shí)踐

1. 緩存反射對(duì)象

反射操作獲取Class、Method、Field、Constructor等對(duì)象是昂貴的,應(yīng)當(dāng)盡可能緩存這些對(duì)象,特別是在循環(huán)或熱點(diǎn)代碼路徑中。

// 不推薦: 每次調(diào)用都獲取方法對(duì)象
public Object invoke(Object obj, String methodName, Object... args) throws Exception {
    Method method = obj.getClass().getDeclaredMethod(methodName, getParameterTypes(args));
    return method.invoke(obj, args);
}

// 推薦: 使用緩存
private static final ConcurrentHashMap<Class<?>, Map<String, Method>> METHOD_CACHE = new ConcurrentHashMap<>();

public Object invoke(Object obj, String methodName, Object... args) throws Exception {
    Class<?> clazz = obj.getClass();
    Map<String, Method> methods = METHOD_CACHE.computeIfAbsent(clazz, 
        k -> Arrays.stream(k.getDeclaredMethods())
                   .collect(Collectors.toMap(Method::getName, m -> m, (m1, m2) -> m1)));
    Method method = methods.get(methodName);
    return method.invoke(obj, args);
}

2. 區(qū)分getMethods()和getDeclaredMethods()

  • getMethods() 返回所有公共方法,包括繼承的方法
  • getDeclaredMethods() 返回所有方法(包括私有、保護(hù)、默認(rèn)和公共),但不包括繼承的方法
// 獲取所有公共方法(包括從父類繼承的)
Method[] publicMethods = MyClass.class.getMethods();

// 獲取所有聲明的方法(包括私有方法,但不包括繼承方法)
Method[] declaredMethods = MyClass.class.getDeclaredMethods();

選擇正確的方法可以提高性能并避免意外訪問不應(yīng)訪問的方法。

3. 正確處理InvocationTargetException

使用反射調(diào)用方法時(shí),原始異常會(huì)被包裝在InvocationTargetException中,應(yīng)當(dāng)提取并處理原始異常。

try {
    method.invoke(obj, args);
} catch (InvocationTargetException e) {
    // 獲取并處理目標(biāo)方法拋出的實(shí)際異常
    Throwable targetException = e.getTargetException();
    log.error("Method {} threw an exception: {}", method.getName(), targetException.getMessage());
    throw targetException; // 或者適當(dāng)處理
} catch (IllegalAccessException e) {
    // 處理訪問權(quán)限問題
    log.error("Access denied to method {}: {}", method.getName(), e.getMessage());
}

4. 合理使用setAccessible(true)

使用setAccessible(true)可以繞過(guò)訪問檢查,訪問私有成員,但應(yīng)謹(jǐn)慎使用。

Field privateField = MyClass.class.getDeclaredField("privateField");
privateField.setAccessible(true); // 允許訪問私有字段
privateField.set(instance, newValue);

在生產(chǎn)環(huán)境中,應(yīng)當(dāng)考慮這種操作的必要性和安全影響。

5. 使用泛型增強(qiáng)類型安全

泛型可以減少類型轉(zhuǎn)換,使反射代碼更安全。

// 類型不安全的反射
Object result = method.invoke(obj, args);
String strResult = (String) result; // 可能的ClassCastException

// 使用泛型增強(qiáng)類型安全
public <T> T invokeMethod(Object obj, Method method, Object... args) throws Exception {
    @SuppressWarnings("unchecked")
    T result = (T) method.invoke(obj, args);
    return result;
}

性能優(yōu)化技巧

6. 避免反射熱點(diǎn)路徑

在性能關(guān)鍵的代碼路徑上避免使用反射。如果必須使用,考慮以下替代方案:

  • 使用工廠模式或依賴注入
  • 預(yù)先生成訪問器代碼
  • 使用接口而非反射
// 不推薦: 頻繁調(diào)用的代碼中使用反射
for (int i = 0; i < 1000000; i++) {
    method.invoke(obj, i);
}

// 推薦: 將反射封裝到工廠中,一次性創(chuàng)建調(diào)用器
interface Processor {
    void process(int i);
}

Processor processor = createProcessor(obj, method);
for (int i = 0; i < 1000000; i++) {
    processor.process(i);
}

7. 考慮使用MethodHandle而非反射

Java 7引入的MethodHandle通常比傳統(tǒng)反射更高效,特別是在重復(fù)調(diào)用同一方法時(shí)。

// 使用MethodHandle
MethodType methodType = MethodType.methodType(String.class, int.class);
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle handle = lookup.findVirtual(String.class, "substring", methodType);

// 調(diào)用方法(性能更好的熱點(diǎn)路徑)
String result = (String) handle.invoke("Hello World", 6);

8. 利用LambdaMetafactory創(chuàng)建高效函數(shù)接口

對(duì)于簡(jiǎn)單的getter和setter方法,可以使用LambdaMetafactory創(chuàng)建函數(shù)接口,性能接近直接調(diào)用。

public static <T, R> Function<T, R> createGetter(Class<T> clazz, String propertyName) throws Exception {
    Method method = clazz.getDeclaredMethod("get" + capitalize(propertyName));
    MethodHandles.Lookup lookup = MethodHandles.lookup();
    CallSite site = LambdaMetafactory.metafactory(
            lookup,
            "apply",
            MethodType.methodType(Function.class),
            MethodType.methodType(Object.class, Object.class),
            lookup.unreflect(method),
            MethodType.methodType(method.getReturnType(), clazz));
    return (Function<T, R>) site.getTarget().invokeExact();
}

// 使用生成的函數(shù)
Function<Person, String> nameGetter = createGetter(Person.class, "name");
String name = nameGetter.apply(person); // 性能接近直接調(diào)用person.getName()

9. 使用第三方庫(kù)優(yōu)化反射

某些情況下,可以考慮使用專門針對(duì)反射優(yōu)化的庫(kù):

  • ByteBuddy
  • ReflectASM
  • CGLib
// 使用ByteBuddy優(yōu)化反射調(diào)用
Getter<Person, String> nameGetter = MethodHandles.lookup()
    .in(Person.class)
    .getter(Person.class.getDeclaredField("name"))
    .bindTo(ByteBuddy.install(MethodHandles.lookup()));
    
String name = nameGetter.get(person);

10. 謹(jǐn)慎傳遞大型數(shù)組或復(fù)雜對(duì)象

當(dāng)通過(guò)反射傳遞參數(shù)或返回值時(shí),大型數(shù)組或復(fù)雜對(duì)象可能導(dǎo)致性能問題??紤]使用更簡(jiǎn)單的數(shù)據(jù)類型或流式處理。

// 不推薦: 通過(guò)反射傳遞大數(shù)組
Object[] largeArray = new Object[10000];
method.invoke(obj, (Object) largeArray);

// 推薦: 使用更小的批次或流處理
Stream.of(largeArray)
      .collect(Collectors.groupingBy(i -> i % 100))
      .forEach((batch, items) -> {
          try {
              method.invoke(obj, (Object) items.toArray());
          } catch (Exception e) {
              // 處理異常
          }
      });

安全性考慮

11. 避免通過(guò)反射修改final字段

雖然技術(shù)上可行,但修改final字段可能導(dǎo)致線程安全問題和不可預(yù)測(cè)的行為。

// 危險(xiǎn)操作: 修改final字段
Field field = MyClass.class.getDeclaredField("CONSTANT");
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, newValue); // 可能引起嚴(yán)重問題

這種操作在Java 9后變得更加困難,并且在理論上可能導(dǎo)致JVM優(yōu)化假設(shè)失效。

12. 訪問控制檢查

在框架或API中,要實(shí)施適當(dāng)?shù)脑L問控制檢查,防止惡意使用反射。

public void invokeMethod(Object target, String methodName, Object... args) {
    // 檢查調(diào)用者是否有權(quán)限執(zhí)行此操作
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        sm.checkPermission(new ReflectPermission("suppressAccessChecks"));
    }
    
    // 檢查目標(biāo)方法是否在允許調(diào)用的白名單中
    if (!ALLOWED_METHODS.contains(methodName)) {
        throw new SecurityException("Method not in allowed list: " + methodName);
    }
    
    // 執(zhí)行反射操作
    // ...
}

13. 注意JDK版本差異

不同JDK版本對(duì)反射的限制不同,Java 9以后的模塊系統(tǒng)對(duì)反射訪問增加了更多限制。

// Java 9+ 訪問非導(dǎo)出模塊的類
try {
    // 嘗試使用反射訪問
    Class<?> clazz = Class.forName("jdk.internal.misc.Unsafe");
    // 這會(huì)拋出異常,除非使用--add-opens參數(shù)啟動(dòng)JVM
} catch (Exception e) {
    // 處理訪問限制異常
}

Java 11+中,推薦在啟動(dòng)參數(shù)中明確指定需要開放的模塊:

--add-opens java.base/jdk.internal.misc=ALL-UNNAMED

14. 處理動(dòng)態(tài)代理的安全問題

使用反射和動(dòng)態(tài)代理時(shí),確保代理類不會(huì)被濫用。

// 安全的代理創(chuàng)建
InvocationHandler handler = new MyInvocationHandler();
MyInterface proxy = (MyInterface) Proxy.newProxyInstance(
        MyInterface.class.getClassLoader(),
        new Class<?>[] { MyInterface.class },
        (proxy, method, args) -> {
            // 檢查是否允許調(diào)用此方法
            if (method.getDeclaringClass() == Object.class) {
                return method.invoke(handler, args); // 允許Object方法
            }
            
            if (!ALLOWED_METHODS.contains(method.getName())) {
                throw new SecurityException("Method not allowed: " + method.getName());
            }
            
            return method.invoke(target, args);
        });

15. 避免反射調(diào)用序列化/反序列化方法

不要使用反射調(diào)用readObject、writeObject等序列化方法,這可能導(dǎo)致嚴(yán)重安全漏洞。

// 危險(xiǎn)操作
Method readObject = targetClass.getDeclaredMethod("readObject", ObjectInputStream.class);
readObject.setAccessible(true);
readObject.invoke(instance, inputStream); // 可能導(dǎo)致反序列化漏洞

應(yīng)當(dāng)使用Java標(biāo)準(zhǔn)序列化機(jī)制或安全的序列化庫(kù)。

高級(jí)技巧與實(shí)踐

16. 結(jié)合注解實(shí)現(xiàn)聲明式編程

反射和注解結(jié)合可以實(shí)現(xiàn)強(qiáng)大的聲明式編程模型,類似Spring和JPA的實(shí)現(xiàn)方式。

// 自定義注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Transactional {
    boolean readOnly() default false;
}

// 使用反射處理注解
public void processClass(Class<?> clazz) {
    for (Method method : clazz.getDeclaredMethods()) {
        Transactional annotation = method.getAnnotation(Transactional.class);
        if (annotation != null) {
            boolean readOnly = annotation.readOnly();
            // 創(chuàng)建事務(wù)代理...
        }
    }
}

17. 獲取泛型信息

盡管Java有類型擦除,但可以通過(guò)反射API獲取泛型信息。

public class GenericExample<T> {
    private List<String> stringList;
    private Map<Integer, T> genericMap;
    
    // 獲取字段泛型類型
    public static void main(String[] args) throws Exception {
        Field stringListField = GenericExample.class.getDeclaredField("stringList");
        ParameterizedType stringListType = (ParameterizedType) stringListField.getGenericType();
        Class<?> stringListClass = (Class<?>) stringListType.getActualTypeArguments()[0];
        System.out.println(stringListClass); // 輸出: class java.lang.String
        
        Field genericMapField = GenericExample.class.getDeclaredField("genericMap");
        ParameterizedType genericMapType = (ParameterizedType) genericMapField.getGenericType();
        Type keyType = genericMapType.getActualTypeArguments()[0]; // Integer
        Type valueType = genericMapType.getActualTypeArguments()[1]; // T (TypeVariable)
        System.out.println(keyType + ", " + valueType);
    }
}

18. 實(shí)現(xiàn)插件系統(tǒng)和SPI機(jī)制

反射是實(shí)現(xiàn)插件系統(tǒng)和服務(wù)提供者接口(SPI)的關(guān)鍵技術(shù)。

public class PluginLoader {
    public static List<Plugin> loadPlugins(String packageName) {
        List<Plugin> plugins = new ArrayList<>();
        
        // 掃描包中的類
        Reflections reflections = new Reflections(packageName);
        Set<Class<? extends Plugin>> pluginClasses = 
            reflections.getSubTypesOf(Plugin.class);
            
        // 實(shí)例化插件
        for (Class<? extends Plugin> pluginClass : pluginClasses) {
            try {
                // 查找@PluginInfo注解
                PluginInfo info = pluginClass.getAnnotation(PluginInfo.class);
                if (info != null && info.enabled()) {
                    // 使用反射創(chuàng)建插件實(shí)例
                    Plugin plugin = pluginClass.getDeclaredConstructor().newInstance();
                    plugins.add(plugin);
                }
            } catch (Exception e) {
                // 處理異常
            }
        }
        
        return plugins;
    }
}

19. 避免反射創(chuàng)建原生類型數(shù)組

創(chuàng)建原生類型數(shù)組需要特別注意,不能直接使用Array.newInstance()。

// 錯(cuò)誤: 嘗試通過(guò)反射創(chuàng)建int[]
Object array = Array.newInstance(int.class, 10); // 正確
Object array = Array.newInstance(Integer.TYPE, 10); // 也正確

// 錯(cuò)誤: 嘗試通過(guò)反射設(shè)置值
Array.set(array, 0, 42); // 拋出IllegalArgumentException
// 正確方式
Array.setInt(array, 0, 42);

20. 使用反射模擬依賴注入

可以使用反射實(shí)現(xiàn)簡(jiǎn)單的依賴注入框架,類似Spring的核心功能。

public class SimpleDI {
    private Map<Class<?>, Object> container = new HashMap<>();
    
    public void register(Class<?> type, Object instance) {
        container.put(type, instance);
    }
    
    public <T> T getInstance(Class<T> type) throws Exception {
        // 檢查容器中是否已有實(shí)例
        if (container.containsKey(type)) {
            return type.cast(container.get(type));
        }
        
        // 創(chuàng)建新實(shí)例
        Constructor<T> constructor = type.getDeclaredConstructor();
        T instance = constructor.newInstance();
        
        // 注入字段
        for (Field field : type.getDeclaredFields()) {
            if (field.isAnnotationPresent(Inject.class)) {
                field.setAccessible(true);
                Class<?> fieldType = field.getType();
                Object dependency = getInstance(fieldType); // 遞歸解析依賴
                field.set(instance, dependency);
            }
        }
        
        container.put(type, instance);
        return instance;
    }
}

總結(jié)

在實(shí)際應(yīng)用中,應(yīng)當(dāng)權(quán)衡反射帶來(lái)的靈活性與潛在的性能、安全和可維護(hù)性問題。

大多數(shù)情況下,如果有不使用反射的替代方案,應(yīng)優(yōu)先考慮。

最后,優(yōu)秀的框架設(shè)計(jì)應(yīng)該盡量將反射細(xì)節(jié)封裝起來(lái),為最終用戶提供清晰、類型安全的API,只在必要的內(nèi)部實(shí)現(xiàn)中使用反射。

以上就是Java中反射的20個(gè)使用技巧分享的詳細(xì)內(nèi)容,更多關(guān)于Java反射技巧的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java ScheduledExecutorService定時(shí)任務(wù)案例講解

    Java ScheduledExecutorService定時(shí)任務(wù)案例講解

    這篇文章主要介紹了Java ScheduledExecutorService定時(shí)任務(wù)案例講解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • spring-boot-thin-launcher插件分離jar包的依賴和配置方式

    spring-boot-thin-launcher插件分離jar包的依賴和配置方式

    這篇文章主要介紹了spring-boot-thin-launcher插件分離jar包的依賴和配置方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • Java多線程并發(fā)之線程池任務(wù)請(qǐng)求攔截測(cè)試實(shí)例

    Java多線程并發(fā)之線程池任務(wù)請(qǐng)求攔截測(cè)試實(shí)例

    這篇文章主要介紹了Java多線程并發(fā)之線程池任務(wù)請(qǐng)求攔截測(cè)試實(shí)例,隊(duì)列中永遠(yuǎn)沒有線程被加入,即使線程池已滿,也不會(huì)導(dǎo)致被加入排隊(duì)隊(duì)列,實(shí)現(xiàn)了只有線程池存在空閑線程的時(shí)候才會(huì)接受新任務(wù)的需求,需要的朋友可以參考下
    2023-12-12
  • Mybatis3中方法返回生成的主鍵:XML,@SelectKey,@Options詳解

    Mybatis3中方法返回生成的主鍵:XML,@SelectKey,@Options詳解

    這篇文章主要介紹了Mybatis3中方法返回生成的主鍵:XML,@SelectKey,@Options,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • Redis緩存及熱點(diǎn)key問題解決方案

    Redis緩存及熱點(diǎn)key問題解決方案

    這篇文章主要介紹了Redis緩存及熱點(diǎn)key問題解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-04-04
  • Java虛擬機(jī)運(yùn)行時(shí)棧的棧幀

    Java虛擬機(jī)運(yùn)行時(shí)棧的棧幀

    本節(jié)將會(huì)介紹一下Java虛擬機(jī)棧中的棧幀,會(huì)對(duì)棧幀的組成部分(局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接、方法出口)分別進(jìn)行介紹,最后還會(huì)通過(guò)javap命令反解析編譯后的.class文件,進(jìn)行分析方法執(zhí)行時(shí)的局部變量表、操作數(shù)棧等
    2021-09-09
  • Mybatis省略@Param注解原理分析

    Mybatis省略@Param注解原理分析

    這篇文章主要介紹了Mybatis省略@Param注解原理分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • Java抽獎(jiǎng)?chuàng)屬?gòu)算法

    Java抽獎(jiǎng)?chuàng)屬?gòu)算法

    這篇文章主要為大家詳細(xì)介紹了Java抽獎(jiǎng)?chuàng)屬?gòu)算法,ava實(shí)現(xiàn)的抽獎(jiǎng)?chuàng)屬?gòu)算法,用數(shù)據(jù)庫(kù)行鎖實(shí)現(xiàn),支持集群,感興趣的小伙伴們可以參考一下
    2016-08-08
  • Java中Scanner使用方式:單行/多行輸入

    Java中Scanner使用方式:單行/多行輸入

    這篇文章主要介紹了Java中Scanner使用方式:單行/多行輸入,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-05-05
  • 使用jib插件為Java應(yīng)用構(gòu)建鏡像的方法

    使用jib插件為Java應(yīng)用構(gòu)建鏡像的方法

    這篇文章主要介紹了使用jib插件為Java應(yīng)用構(gòu)建鏡像,要是用戶本地沒安裝docker,可以使用jib制作出帶有鏡像的tar文件,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-08-08

最新評(píng)論