Hello 各位后端搬砖人!今天咱们聊个 Java 里超有意思的 “黑科技”—— 反射。要是把 Java 类比作一个包装严实的快递盒,那反射就是能隔着盒子看清里面装了啥、甚至不用拆封就能摆弄里面东西的 “透视挂”。别觉得这玩意儿离咱远,你天天用的 Spring、MyBatis,背后都有它的身影!
一、先搞懂:反射到底是个啥?
官方定义说得玄乎:“反射是 Java 程序在运行时获取类信息、操作类成员的能力”。咱翻译成人话就是:平时咱写代码都是 “正着来”—— 知道类名,new 个对象调用方法;而反射是 “反着来”—— 哪怕不知道类叫啥,只要拿到它的 “身份证”(Class 对象),就能扒出它的构造方法、成员变量、成员方法,还能随便调用、修改。
打个比方:平时你去奶茶店,得知道 “珍珠奶茶” 这个名字才能点(正着来);反射就像你拿着奶茶店的配方表(Class 对象),不管这奶茶叫啥,都能自己凑材料做出来,甚至还能偷偷多加两勺糖(修改私有变量)。
二、反射这么秀,到底能干嘛?(应用场景大揭秘)
别以为反射是 “花架子”,后端开发没它真不行,这几个场景你绝对眼熟:
1. 框架的 “核心发动机”
咱天天用的 Spring IOC,为啥能 “自动帮你 new 对象”?其实就是 Spring 读取配置文件(比如 xml 里的),拿到类名后,用反射找到构造方法,帮你创建对象;MyBatis 为啥能把数据库结果直接转成 Java 对象?也是反射在背后匹配字段名、调用 set 方法赋值。
2. 动态代理 “好搭档”
写 AOP 的时候,JDK 动态代理就是靠反射实现的!代理类在运行时才通过反射拿到目标类的方法,然后在方法前后加日志、事务这些 “增强逻辑”,不用手动写一堆代理类,简直是懒癌福音。
3. 通用工具类 “神器”
比如你想写个工具类,能给任意对象的任意字段赋值,总不能每个类都写一遍 set 方法吧?用反射就能搞定:传入对象和字段名,直接通过反射找到字段并修改,一行代码适配所有类,效率直接拉满。
三、第一步:拿到 “身份证”—— 获取 Class 对象
要玩反射,首先得拿到类的 “身份证”——Class 对象。Java 给了三种常用方式,各有各的用法,记牢别搞混!
方式 1:对象.getClass ()——“从已有对象查身份证”
适合已经创建了对象的场景,比如你手里有个User对象,想看看它的类信息:
User user = new User();
Class<?> clazz = user.getClass(); // 拿到User类的Class对象
System.out.println(clazz.getName()); // 输出:com.example.User(全类名)
方式 2:类名.class——“直接查类的身份证”
不用创建对象,直接通过类名获取,适合知道具体类名的场景:
Class<?> clazz = User.class; // 直接拿User类的Class对象
这种方式还有个小妙用:判断对象类型时,比instanceof更精准(比如user.getClass() == User.class,不会包含子类)。
方式 3:Class.forName ("全类名")——“按名字查身份证”
最灵活的方式!不用导入类,只要知道全类名(比如从配置文件读的 “com.example.User”),就能动态加载类并获取 Class 对象,框架最爱用这个:
// 注意:全类名要写对,不然会抛ClassNotFoundException
Class<?> clazz = Class.forName("com.example.User");
小提醒:一个类在 JVM 里只有一个 Class 对象,不管用哪种方式拿,拿到的都是同一个,别担心重复创建~
四、玩起来:反射操作三大核心成员
拿到 Class 对象后,咱就能开始 “透视” 操作了 —— 构造方法、成员方法、成员变量,一个都跑不了!
1. 操作构造方法:帮你 “new 对象”
平时 new 对象靠new 构造方法(),反射靠Constructor类,哪怕是私有构造方法也能调用(但要注意安全哦)。
示例:用反射创建 User 对象
假设 User 类长这样:
public class User {
private String name;
private int age;
// 无参构造(默认有,这里显式写出来)
public User() {}
// 有参构造(私有)
private User(String name, int age) {
this.name = name;
this.age = age;
}
// getter/setter省略...
}
步骤 1:获取构造方法
- 无参构造:getConstructor()(public 修饰)
- 有参构造:getDeclaredConstructor(参数类型.class)(支持私有)
Class<?> userClass = Class.forName("com.example.User");
// 1. 获取无参构造
Constructor<?> noArgConstructor = userClass.getConstructor();
// 2. 获取私有有参构造(String, int)
Constructor<?> argConstructor = userClass.getDeclaredConstructor(String.class, int.class);
步骤 2:创建对象
- 调用 public 构造:直接newInstance()
- 调用私有构造:先设setAccessible(true)(打破封装)
// 1. 用无参构造创建对象
User user1 = (User) noArgConstructor.newInstance();
// 2. 用私有有参构造创建对象(关键:打开访问权限)
argConstructor.setAccessible(true); // 告诉JVM:别拦着,我要访问私有方法!
User user2 = (User) argConstructor.newInstance("张三", 25);
System.out.println(user2.getName()); // 输出:张三(成功创建!)
2. 操作成员方法:帮你 “调用方法”
不管是 public 方法还是私有方法,反射都能帮你调用,哪怕是父类的方法也能拿到(用getMethod()会找父类,getDeclaredMethod()只找当前类)。
示例:用反射调用 User 的方法
给 User 加个方法:
public class User {
// 公开方法
public void sayHello() {
System.out.println("Hello, 我是" + name);
}
// 私有方法(带参数)
private void setInfo(String name, int age) {
this.name = name;
this.age = age;
}
}
步骤 1:获取成员方法
Class<?> userClass = User.class;
User user = new User();
// 1. 获取公开方法sayHello(无参)
Method sayHelloMethod = userClass.getMethod("sayHello");
// 2. 获取私有方法setInfo(参数:String, int)
Method setInfoMethod = userClass.getDeclaredMethod("setInfo", String.class, int.class);
步骤 2:调用方法
// 1. 调用公开方法:invoke(对象, 方法参数)
sayHelloMethod.invoke(user); // 输出:Hello, 我是null(此时name还没设)
// 2. 调用私有方法:先开权限,再invoke
setInfoMethod.setAccessible(true);
setInfoMethod.invoke(user, "李四", 30); // 给user设置name和age
// 再调用sayHello,看看效果
sayHelloMethod.invoke(user); // 输出:Hello, 我是李四(成功!)
3. 操作成员变量:帮你 “读 / 写变量”
平时要改私有变量得靠 setter,反射直接跳过 setter,直接修改变量值,简直是 “直达核心”。
示例:用反射修改 User 的私有变量
步骤 1:获取成员变量
Class<?> userClass = User.class;
User user = new User();
// 获取私有变量name(getDeclaredField()支持私有)
Field nameField = userClass.getDeclaredField("name");
步骤 2:读 / 写变量值
// 写变量:先开权限,再set
nameField.setAccessible(true);
nameField.set(user, "王五"); // 直接给user的name赋值
// 读变量:get
String userName = (String) nameField.get(user);
System.out.println(userName); // 输出:王五(成功修改并读取!)
五、总结:反射是把 “双刃剑”
反射的优点很明显:灵活、强大,是框架和通用工具的核心;但缺点也不能忽视:
- 破坏封装:能访问私有成员,可能导致代码不安全;
- 性能略差:反射是运行时操作,比直接调用慢一点(但一般场景下影响不大,别过度焦虑);
- 代码可读性低:别人看你用反射的代码,可能会懵:“这咋没 new 对象就调用方法了?”
所以咱用反射的原则是:能不用则不用,该用的时候别犹豫—— 比如写框架、通用工具时,反射是最佳选择;但平时写业务代码,还是老老实实用 new 和直接调用更清晰。
好了,今天的反射 “透视挂” 教程就到这!各位后端小伙伴要是在项目里用反射踩过坑,或者有更骚的用法,评论区聊聊~ 觉得有用的话,点赞收藏走一波,下次想看啥知识点,评论区告诉我!