咱先从一个程序员的 “日常崩溃” 场景说起:

你写了个Person类,里面藏了个privaterun()方法 —— 本来想 “藏着掖着”,只让类自己调用。结果产品经理突然说:“我要在外部代码里调用这个run(),还不能改Person类的代码!”

你盯着屏幕皱眉头:“private 的方法,常规操作根本调不了啊!”

这时候,Java 拍了拍你的肩膀:“兄弟,试试‘反射’呗 —— 它可是咱 Java 界的‘透视眼’,能看穿类的所有秘密,管你是 private 还是 public,都能给你‘薅’出来用!”

先认识下 “反射目标”:Person 类的小秘密

在开始 “透视” 前,咱得先搞清楚目标 ——Person类到底藏了啥。这货就像个 “半遮半掩的明星”:

藏起来的隐私private String nameprivate int age,还有个private void run()方法(相当于 “拒绝公开的才艺”);

公开的信息public String frompublic 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

同理,给publicfrom设值就简单多了,不用开后门:

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():“所有方法全收录”

不管是publicgetAge(),还是privaterun(),全给你找出来。代码里:

Method[] methods = pclass.getDeclaredMethods();
for(Method item : methods) {
    System.out.println(item);
}

运行会列出getName()setName()getAge()setAge()run()—— 连藏着的run()都跑不了。

2. getMethods():“公开方法 + 父类方法”

不仅能拿到Personpublic方法,还能拿到它爹(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);
		}
		
		
	}
}

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐