Java 反射:当代码有了 “透视眼”,Private 也能 “开后门”
咱先从一个程序员的 “日常崩溃” 场景说起:
你写了个Person类,里面藏了个private的run()方法 —— 本来想 “藏着掖着”,只让类自己调用。结果产品经理突然说:“我要在外部代码里调用这个run(),还不能改Person类的代码!”
你盯着屏幕皱眉头:“private 的方法,常规操作根本调不了啊!”
这时候,Java 拍了拍你的肩膀:“兄弟,试试‘反射’呗 —— 它可是咱 Java 界的‘透视眼’,能看穿类的所有秘密,管你是 private 还是 public,都能给你‘薅’出来用!”
先认识下 “反射目标”:Person 类的小秘密
在开始 “透视” 前,咱得先搞清楚目标 ——Person类到底藏了啥。这货就像个 “半遮半掩的明星”:
有藏起来的隐私:private String name、private int age,还有个private void run()方法(相当于 “拒绝公开的才艺”);
有公开的信息:public String from、public String height(相当于 “公开的籍贯、身高”);
有三种 “出厂方式”:无参构造(默认款)、带 1 个参数(定制名字款)、带 2 个参数(定制名字 + 年龄款);
还有个toString()方法,相当于 “自我介绍脚本”,能把自己的信息打印出来。
简单说,这就是反射要 “操作” 的对象 —— 接下来,咱就用Test类当 “反射侦探”,一步步扒开它的秘密。
反射第一步:拿到 “身份证复印件”——Class 对象
要 “透视” 一个类,首先得拿到它的 “身份证复印件”——Class对象。这玩意儿可不简单,里面记录了类的所有信息:名字、成员变量、方法、构造器,相当于 “类的说明书”。
Java 给了三种拿Class对象的方式,咱结合代码里的注释唠唠:
| 方式 | 代码示例 | 通俗比喻 | 适用场景 |
|---|---|---|---|
| 1. 硬盘查档案 | Class.forName("reflect.Person") | 不用见人,直接去档案局查 “reflect.Person” 的档案 | 不知道类名,靠全类名加载(比如读配置文件) |
| 2. 直接拿户口本 | Person.class | 知道 “Person” 这个户头,直接从户口本里抽复印件 | 编译时就知道类名,想快速获取 |
| 3. 问本人要身份证 | new Person().getClass() | 先见着人(创建对象),再让他出示身份证 | 已经有对象了,想查它的类信息 |
这三种方式拿到的Class对象是同一个 —— 就像同一个人的身份证复印件,不管从哪拿,内容都一样。咱后面的操作,全靠这个 “复印件”。
反射实操 1:扒家底 —— 操作成员变量
拿到Class对象后,第一步先 “查户口”—— 看看Person有哪些成员变量。这里有两个关键方法,像 “侦探的两种搜查模式”:
1. getDeclaredFields():“地毯式搜查”
不管变量是private还是public,全给你揪出来。代码里这么写:
Field[] fields = pclass.getDeclaredFields();
for(Field item : fields) {
System.out.println(item);
}
运行结果会把name(private)、age(private)、from(public)、height(public)全列出来 —— 相当于 “把人家衣柜里的衣服(不管是内衣还是外套)全翻出来了”。
2. getFields():“只查公开区域”
只找带public修饰的变量,private 的一概不管。比如代码里查from(public):
Field field_from = pclass.getField("from"); // 能拿到
Field field_age = pclass.getField("age"); // 报错!age是private
这就像 “门卫只让你进小区的公共区域,私人住宅不让进”。
更狠的:给 private 变量 “开后门”
查到变量还不够,咱还想改它的值 —— 比如给private int age设成 20。常规操作肯定不行,但反射有 “后门钥匙”:setAccessible(true)。
代码里是这么干的:
// 1. 先拿到age这个private变量
Field field_age = pclass.getDeclaredField("age");
// 2. 开后门:跳过权限检查(相当于“撬锁”)
field_age.setAccessible(true);
// 3. 给person对象的age设值20(第一个参数是“目标对象”,第二个是“值”)
field_age.set(person, 20);
// 4. 拿值看看
System.out.println(field_age.get(person)); // 输出20
同理,给public的from设值就简单多了,不用开后门:
Field field_from = pclass.getField("from");
field_from.set(person, "中国"); // 直接设值
System.out.println(field_from.get(person)); // 输出“中国”
最后打印person,会看到Person [name=null, age=20 , from = 中国 , height = null]—— 相当于 “给原本空壳的 Person 填了籍贯和年龄”。
反射实操 2:逼才艺 —— 调用成员方法
查完家底,该让Person“表演才艺” 了 —— 调用它的方法。和变量类似,方法也分 “全查” 和 “只查公开”:
1. getDeclaredMethods():“所有方法全收录”
不管是public的getAge(),还是private的run(),全给你找出来。代码里:
Method[] methods = pclass.getDeclaredMethods();
for(Method item : methods) {
System.out.println(item);
}
运行会列出getName()、setName()、getAge()、setAge()、run()—— 连藏着的run()都跑不了。
2. getMethods():“公开方法 + 父类方法”
不仅能拿到Person的public方法,还能拿到它爹(Object类)的public方法,比如toString()、equals()。代码里打印方法名时,你会看到:
Method[] public_method = pclass.getMethods();
for(Method item : public_method) {
System.out.println(item.getName()); // 会出现toString、equals等
}
这就像 “不仅看明星的公开表演,还看它从师傅那学的基本功”。
调用方法:让run()“被迫营业”
调用public方法很简单,比如getAge():
// 拿到getAge()方法(无参数,所以后面不写参数类型)
Method method_get = pclass.getMethod("getAge");
// 调用方法:第一个参数是“目标对象”,后面是方法参数(这里没有)
method_get.invoke(person); // 输出“getAge方法执行”
但run()是private的,常规调用会报错。这时候又要用到 “后门钥匙”setAccessible(true):
// 拿到private的run()方法
Method method_run = pclass.getDeclaredMethod("run");
// 开后门
method_run.setAccessible(true);
// 调用run()
method_run.invoke(person); // 输出“run方法执行”
相当于 “本来run()只在Person内部表演,现在被反射逼着在外部跳了一段”。
反射实操 3:按配方造人 —— 调用构造器
前面都是操作已有的Person对象,反射还能 “按配方造新对象”—— 通过构造器创建实例。
1. 查构造器:getDeclaredConstructors() vs getConstructors()
和变量、方法逻辑一样:
getDeclaredConstructors():所有构造器(不管权限,虽然这里构造器都是 public);
getConstructors():只查public构造器。
代码里查 “两个参数的构造器”(String name, int age):
// 拿到带String和int参数的构造器(参数类型要写.class)
Constructor constructor2 = pclass.getDeclaredConstructor(String.class, int.class);
2. 造对象:newInstance()
拿到构造器后,用newInstance()就能 “按配方造人”:
// 无参构造造对象(默认款)
Constructor constructor = pclass.getDeclaredConstructor();
Object object = constructor.newInstance(); // 空Person对象
// 带参构造造对象(定制款:name=lili,age=20)
Person object2 = (Person) constructor2.newInstance("lili", 20);
System.out.println(object2); // 输出Person [name=lili, age=20 , from = null , height = null]
object2.getAge(); // 还能调用它的方法,输出“getAge方法执行”
这就像 “工厂造产品”:无参构造是 “默认款产品”,带参构造是 “定制款产品”,反射就是 “拿着配方指挥工厂生产”。
反射进阶:跟配置文件玩 “联动”—— 解耦的关键
前面的操作,类名(reflect.Person)、方法名(getAge)都是写死在代码里的 —— 万一要换个类操作,还得改代码,太麻烦。这时候,配置文件 + 反射就能实现 “改配置不改代码”,也就是程序员常说的 “解耦”。
代码里用了Properties类读配置文件(refconfig.properties),咱一步步看:
1. 配置文件:写 “指令”
配置文件里写了两行:
properties
reflect.className=com.qcby.reflect.Person
reflect.methodName=getAge
这里有个小坑!代码里Person的包是reflect,但配置文件写了com.qcby.reflect.Person—— 相当于 “你想去‘北京市朝阳区.reflect 小区’找 Person,结果导航到‘北京市朝阳区.com.qcby.reflect 小区’,肯定找不到人!” 得改成reflect.Person才对。
2. 读配置:拿 “指令”
用Properties读配置文件,核心是 “用类加载器找文件”(因为配置文件在src下,类加载器能找到):
// 1. 创建Properties对象(相当于“指令本”)
Properties properties = new Properties();
// 2. 用类加载器读配置文件(找到“指令本”)
InputStream in = Person.class.getClassLoader().getResourceAsStream("reflect/refconfig.properties");
// 3. 加载配置(把“指令”读进指令本)
properties.load(in);
// 4. 拿指令:类名和方法名
String className = properties.getProperty("reflect.className"); // 拿到reflect.Person
String methodName = properties.getProperty("reflect.methodName"); // 拿到getAge
3. 反射执行:按 “指令” 干活
拿到类名后,就能用反射动态加载类、操作它:
// 按配置里的类名,拿到Class对象
Class pclass = Class.forName(className);
// 后面就能像之前一样,操作成员变量、方法了
Field[] fields = pclass.getDeclaredFields();
for(Field item : fields) {
System.out.println(item); // 打印所有成员变量
}
这就牛了!以后想操作Student类,不用改Test代码,只改配置文件里的reflect.className=reflect.Student就行 —— 相当于 “换了个目标,只改指令,不用改侦探的操作流程”。
反射的 “两面性”:好用但别滥用
反射虽然强, but 它也有缺点,就像 “透视眼” 不能乱用:
破坏封装:private本来是 “隐私”,反射非要 “撬锁偷看”,违背了 Java 的封装原则;
性能略差:反射是 “动态操作”,比常规的new Person()、person.getAge()慢一点(虽然日常开发差别不大,但高并发场景要注意);
代码可读性差:别人看你代码,看到Method.invoke(),可能不知道你在调用哪个方法,不如直接调用直观。
但它的优点更突出:灵活、解耦—— 这也是 Spring、MyBatis 等框架的核心原理。比如 Spring 的 “IOC 容器”,就是用反射根据配置文件创建对象,实现 “控制反转”;MyBatis 也是用反射把数据库查询结果封装成 Java 对象。
总结:反射是 “Java 进阶的钥匙”
看完这篇,你应该明白:反射不是 “花里胡哨的技巧”,而是 “打开 Java 底层大门的钥匙”。它让代码从 “写死的硬编码” 变成 “灵活的动态操作”,也是你看懂主流框架的基础。
下次再遇到 “不知道类名要读配置”“要调用 private 方法” 的场景,别慌 —— 掏出反射这个 “透视眼”,问题迎刃而解!
最后提醒一句:用反射的时候,记得 “手下留情”,别动不动就破环封装,毕竟 “尊重隐私”(封装原则)也是程序员的基本素养~
代码总览
Person.java
package reflect;
public class Person {
private String name;
private int age;
public String from;
public String height;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
System.out.println("getAge方法执行");
return age;
}
public void setAge(int age) {
this.age = age;
}
private void run() {
System.out.println("run方法执行");
}
public Person() {
}
public Person(String name) {
super();
this.name = name;
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + " , from = "+from+" , height = "+height+"]";
}
}
refconfig.properties
reflect.className=com.qcby.reflect.Person
reflect.methodName=getAge
Test.java
package reflect;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Properties;
public class Test {
public static void main(String[] args) throws Exception{
/* //1、硬盘阶段获取类对象
Class class1 = Class.forName("reflect.Person");
//2、类阶段
Class class2 = Person.class;
//3、对象阶段
Person person = new Person();
Class class3 = person.getClass();*/
//Person person = new Person();
//获取类对象
//Class pclass = Class.forName("reflect.Person");
/*//通过类对象获取成员变量们
Field[] fields =pclass.getDeclaredFields();
for(Field item:fields) {
System.out.println(item);
}
Field field_age = pclass.getDeclaredField("age");
System.out.println(field_age);
System.out.println("---------------------");
Field[] public_fields = pclass.getFields();
for(Field item:public_fields) {
System.out.println(item);
}
Field field_from = pclass.getField("from");
System.out.println(field_from);
//获取和设置
field_from.set(person, "中国");
field_age.setAccessible(true);
field_age.set(person, 20);
System.out.println(field_age.get(person));
System.out.println(field_from.get(person));
System.out.println(person);*/
//通过类对象获取成员方法
/*Method[] methods = pclass.getDeclaredMethods();
for(Method item:methods) {
System.out.println(item);
}
Method method_run = pclass.getDeclaredMethod("run");
System.out.println(method_run);
System.out.println("------------");
Method[] public_method = pclass.getMethods();
for(Method item:public_method) {
System.out.println(item);
System.out.println(item.getName());
}
Method method_get = pclass.getMethod("getAge");
System.out.println(method_get);
method_get.invoke(person);
method_run.setAccessible(true);
method_run.invoke(person);
*/
//获取构造方法
/*Constructor[] constructors = pclass.getDeclaredConstructors();
for(Constructor item:constructors) {
System.out.println(item);
}
Constructor constructor = pclass.getDeclaredConstructor();
System.out.println(constructor);
Constructor constructor2 = pclass.getDeclaredConstructor(String.class,int.class);
System.out.println(constructor2);
System.out.println("=============");
Constructor[] public_constructors = pclass.getConstructors();
for(Constructor item:public_constructors) {
System.out.println(item);
}
Constructor public_constructor = pclass.getConstructor();
System.out.println(public_constructor);
Constructor public_constructor2 = pclass.getConstructor(String.class);
System.out.println(public_constructor2);
Object object = constructor.newInstance();
System.out.println(object);
Person object2 = (Person)constructor2.newInstance("lili",20);
System.out.println(object2);
object2.getAge();*/
//System.out.println(pclass.getName());
//System.out.println(pclass.getSimpleName());
//Properties读取配置文件的类 创建properties对象
Properties properties = new Properties();
//加载配置文件 加载配置文件必须要有类加载器
InputStream in = Person.class.getClassLoader().getResourceAsStream("reflect/refconfig.properties");
properties.load(in);
String className = properties.getProperty("reflect.className");
String methodName = properties.getProperty("reflect.methodName");
Class pclass = Class.forName(className);
System.out.println(pclass);
Field[] fields =pclass.getDeclaredFields();
for(Field item:fields) {
System.out.println(item);
}
}
}
更多推荐



所有评论(0)