专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

Java反射使用经验总结与动态调用场景实战解析

Java 反射的10个使用经验

开场先划个重点,甭管你现在是 CRUD 王者,还是刚写完个 helloWorld,只要你要写框架、搞工具类、整 ORM、搞 AOP、玩注解处理,绕不开反射这碗酒。酒是好酒,就是喝多了晕,一不小心把自己干躺下。咋喝、咋稳、咋省劲,听我一条一条给你唠。

1、私有变量?我偏改,就问你服不服

你以为加个 private 就神仙打不动?Java 说:我反射了解一下啊哥。

public class LaoWang {
    private String secret = "我藏得很深";

    public String getSecret() {
        return secret;
    }
}

上去就是一顿硬刚:

LaoWang obj = new LaoWang();
Field field = obj.getClass().getDeclaredField("secret");
field.setAccessible(true); // 直接掀桌
field.set(obj, "没藏住吧");

System.out.println(obj.getSecret()); // 输出:没藏住吧

这玩意儿不是不能用,关键看你拿来干啥。要是你在那种 BeanUtils 工具里想偷着改一下还行,要是放生产核心逻辑里天天反射搞字段,运维得拿键盘削你。

2、构造方法不给你 new?我硬给你开个门

好多第三方框架或者安全封装类,构造方法藏得死死的,外面想 new 都没门。这时候你就得使点“强扭的瓜”,甭管甜不甜,先扭出来再说。

public class LaoLi {
    private LaoLi(String name) {
        System.out.println("偷偷 new 了老李:" + name);
    }
}

反手一个反射捅进去:

Constructor<LaoLi> constructor = LaoLi.class.getDeclaredConstructor(String.class);
constructor.setAccessible(true); // 直接撬锁
LaoLi laoLi = constructor.newInstance("小李子");

说实话,这操作看着邪门,但是真有它妙用的时候,比如你想搞个对象池、Bean 工厂或者注解扫描初始化,不这么干都没法玩。别天天一来就 new,这种偷偷搞个私生子的思路,香着呢。

3、动态调用方法,别死盯那堆 if else

想做个工具类、写个统一调度、实现个通用 API?方法名、参数啥都动态的,手写 switch case?那是搬砖,不是搞技术。

public class LaoZhao {
    public void sayHello(String name) {
        System.out.println("老赵打招呼:" + name);
    }
}

改成反射调用,灵活得一批:

LaoZhao obj = new LaoZhao();
Method method = obj.getClass().getMethod("sayHello", String.class);
method.invoke(obj, "大兄弟");

当然喽,方法一多、类一杂,别傻乎乎每次都去反射一遍,Method 对象是能缓存的,内存多点都比你 CPU 卡死强。再一个,invoke() 里包异常,调试时候容易脑壳疼,记得 unwrap 异常处理清清爽爽。

4、注解 + 反射,不搞你注定写不了框架

这年头,注解到处都是,从 SpringBoot 到自定义框架,没点反射解析能力你咋往下写?

先造个注解:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ZhuYao {
    String value();
}

配个带注解的类:

public class LaoSun {
    @ZhuYao("用户名")
    private String username;

    @ZhuYao("密码")
    private String password;
}

来,解析它:

for (Field field : LaoSun.class.getDeclaredFields()) {
    if (field.isAnnotationPresent(ZhuYao.class)) {
        ZhuYao anno = field.getAnnotation(ZhuYao.class);
        System.out.println("字段:" + field.getName() + ",注解值:" + anno.value());
    }
}

别觉得写框架是啥高深活儿,其实就这些小技巧拼拼凑凑,封装封装,外面包个壳,你就能吹说自己搞了个注解驱动的自动注册处理器,听着不唬人?

5、反射慢?那真不是假的

别看反射香,但性能是真不咋地。你单次用用没事儿,上了高并发场景还反射一万次,你这不是抡着锤子敲服务器 CPU 嘛。

搞个缓存再说话:

private static final Map<String, Method> methodCache = new HashMap<>();

public static Object invokeCachedMethod(Object obj, String methodName, Class<?>[] paramTypes, Object[] args) throws Exception {
    String key = obj.getClass().getName() + "#" + methodName;
    Method method = methodCache.get(key);
    if (method == null) {
        method = obj.getClass().getMethod(methodName, paramTypes);
        methodCache.put(key, method);
    }
    return method.invoke(obj, args);
}

缓存啥意思?就跟你小卖部备点货一样,常用的先放边上,别老来回开门去仓库取,累不累啊?

阶段总结一哈

行了,今天先撂这五条,各种“反射的骚操作”你也瞄一眼了。你是不是也跟我当年似的,头铁非要动态调用、非要私有字段操作、非要搞全自动注解处理?别急,工具是把刀,用得巧才是本事,用得猛就把自己剁手了。

等你喊个“继续”,咱给你上剩下的五条,那才是硬货里的硬货,写框架的命根子,踩坑的祖传祸害。

等你开口,咱再继续嗷~

6、类型擦除下的反射,别想着泛型还能保留

你以为泛型能在反射里读出来?天真了兄弟,Java 的泛型,编译完就没了,擦没了!你手贱反射个 List<String> 出来,拿到的是个啥?嗯,是 List,跟 String 一毛钱关系都没有。

看这个例子你就明白:

public class LaoMa<T> {
    public T data;
}

你反射看看 data 的类型:

Field field = LaoMa.class.getDeclaredField("data");
System.out.println(field.getType()); // class java.lang.Object

看清了没?泛型全整没了,就剩个 Object,典型“编译期看起来高大上,运行期啥都不是”。

要真想拿泛型信息?得从父类的 Type 里抠,这都属于“反射深水区”了,不是写几行代码就能拿下的,属于硬活儿,得慢慢摸、慢慢配。

7、代理对象反射,真不是你想当然那么简单

你以为拿个对象就能随便 getClass?那你可别忘了,Spring 那套 AOP、动态代理,把原对象给你包三层八层,结果你反射来反射去,压根不是你想反的那个。

Object bean = applicationContext.getBean("userService");
System.out.println(bean.getClass()); // com.sun.proxy.$Proxy123 或 CGLIB 动态生成的类

你用反射 getDeclaredMethod,直接 null,啥都拿不着。为啥?因为你反的是代理类,不是原类,方法都不在那儿!

解决办法呢,要么你用接口干活(JDK 动态代理还能对接口起作用),要么你用 AOPUtils、ReflectionUtils 之类的工具类再往里扒——这就不只是反射本身的活了,是反射 + 框架 + 黑魔法三合一,走错一步,全线炸穿。

8、class.forName() 别乱用,类加载是个深坑

很多人一写反射就来一句:

Class<?> clazz = Class.forName("com.xxx.LaoHei");

你还真以为这么一行代码只是“获取类对象”?老弟,这玩意儿顺带会触发类初始化

也就是说,你静态代码块要是写了点啥乱七八糟的操作,这句代码一来,啪的一下全执行了。

public class LaoHei {
    static {
        System.out.println("我被加载啦!");
    }
}

你反射调用:

Class<?> clazz = Class.forName("com.xxx.LaoHei");
// 控制台输出:我被加载啦!

你要是真不想触发初始化?可以用另一个构造法:

Class<?> clazz = ClassLoader.getSystemClassLoader().loadClass("com.xxx.LaoHei");

这个只是加载 class 信息,不触发 static 块执行,适合你那种“提前预热,不触发副作用”的场景。懂点这玩意儿,关键时候不踩坑,能救命。

9、数组的反射,跟普通类压根不是一路人

你要以为数组也是 Class,那你就傻了吧,数组在反射体系里是个怪胎,它的类名、类型结构、数组长度、元素类型都得另外搞。

int[] arr = new int[5];
Class<?> arrClass = arr.getClass();
System.out.println(arrClass.getName()); // [I

你要想反射设置数组值:

int[] nums = new int[3];
Array.set(nums, 0, 42);
int val = (int) Array.get(nums, 0);
System.out.println(val); // 42

这玩意儿你看着不难,其实没人告诉你压根不会想到原来还能这么搞。特别是搞那种多维数组,反射能让你头发一晚上掉光。总结:数组要反射,得用 java.lang.reflect.Array,不是用 Field、Method 那套。

10、破坏单例?反射可以,玩脱也真快

有些人脑袋一热,非要用反射破坏单例,说能绕过构造器私有,搞出两个实例。是的,的确可以,但你真要在项目里干这事儿,我劝你早点跑路,架构师要追着你削了。

拿最典型的饿汉式单例举个例子:

public class LaoDan {
    private static final LaoDan instance = new LaoDan();

    private LaoDan() {}

    public static LaoDan getInstance() {
        return instance;
    }
}

你用反射搞:

Constructor<LaoDan> cons = LaoDan.class.getDeclaredConstructor();
cons.setAccessible(true);

LaoDan one = cons.newInstance();
LaoDan two = cons.newInstance();

System.out.println(one == two); // false,直接破坏了单例

你看着是成功了,实则你破坏了别人设计的约束。现在很多类内部会检测是否已被构造过,比如通过 flag、或者直接在构造器中抛异常,防止你搞幺蛾子。也有用 Enum 来实现单例的,那更反不动了。你真非要破它,那不是技术问题,是道德问题,懂?

最后的唠叨:技术是把刀,怎么用全看人

你说反射香不香?我说它是把开山斧,能一刀斩妖除魔,也能把自己大腿劈断。你用它写框架、自动注入、注解解析,那是真厉害;你要乱来,把代码搞得别人看不懂、性能炸穿,那就真是“工具没错,人有病”。

你今天学到的这 10 招,都是我啃项目、熬通宵、和 bug 大战三百回合后总结出来的。你要真想把反射玩明白,不光得会写代码,还得会分析框架,知道 JVM 底层那些事儿。

技术不靠背语法靠干,别纸上谈兵。要是你哪天真靠反射写了个能通杀业务场景的工具,记得回来给哥留言,咱们一块喝顿酒,庆祝你“反射入门”!

未经允许不得转载:搜云库 » Java反射使用经验总结与动态调用场景实战解析

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们