Java 初步了解反射机制
反射:框架设计的灵魂
-
框架:
半成品软件。可以在框架的基础上进行软件开发,简化编码
-
反射:
将类的各个组成部分封装为其他对象,这就是反射机制
-
好处:
可以在程序运行过程中,操作这些对象。
可以解耦,提高程序的可扩展性。
-
Java在计算机中经历的三个阶段:
获取字节码Class对象的三种方式
定义一个Person.java,用于下面的讲解。
复制package view.study.demo44; public class Person { private String name; private int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
Class.forName("类的全称,包括所在包")
Class.forName("全类名"):将字节码文件加载进内存,返回Class对象。
常用之处:多用于配置文件,将类名定义在配置文件中。读取文件,加载类。
复制package view.study.demo44; public class DemoForName { public static void main(String[] args) throws ClassNotFoundException { Class<?> aClass = Class.forName("view.study.demo44.Person"); System.out.println(aClass); } }
运行程序,控制台输出:
复制class view.study.demo44.Person
[类名].class
类名.class:通过类名的属性class获取。
常用之处:多用于参数的传递。
复制package view.study.demo44.test; import view.study.demo44.Person; public class DemoClass { public static void main(String[] args) { Class<Person> personClass = Person.class; System.out.println(personClass); } }
运行程序,控制台输出:
复制class view.study.demo44.Person
[对象].getClass()
对象.getClass():getClass()方法在Object类中定义着。
常用之处:多用于对象的获取字节码的方式。
复制package view.study.demo44; public class DemoGetClass { public static void main(String[] args) { Person person = new Person(); Class<? extends Person> personClass = person.getClass(); System.out.println(personClass); } }
运行程序,控制台输出:
复制class view.study.demo44.Person
结论
上面的三种方式,在一个程序中输出,其实是一样的。
复制package view.study.demo44.test; import view.study.demo44.Person; public class Demo01Class { public static void main(String[] args) throws ClassNotFoundException { Class<?> class1 = Class.forName("view.study.demo44.Person"); Class<Person> class2 = Person.class; Person person = new Person(); Class<? extends Person> class3 = person.getClass(); System.out.println(class1 == class2); System.out.println(class1 == class3); } }
运行程序,控制台输出:
复制true true
同一个字节码文件(如Person.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。
Class对象获取功能:获取成员变量
复制Field[] getFields() // 获取所有public修饰的成员变量 Field getField(String name) // 获取指定名称的 public修饰的成员变量 Field[] getDeclaredFields() // 获取所有的成员变量,不考虑修饰符 Field getDeclaredField(String name) // 获取指定名称的成员变量,不考虑修饰符
定义一个Person.java,用于下面方法的使用:
复制package view.study.demo45; public class Person { private String name; private int age; public int a; public int b; public int c; public Person() { } public Person(String name, int age, int a, int b, int c) { this.name = name; this.age = age; this.a = a; this.b = b; this.c = c; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + ", a=" + a + ", b=" + b + ", c=" + c + '}'; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public int getA() { return a; } public void setA(int a) { this.a = a; } public int getB() { return b; } public void setB(int b) { this.b = b; } public int getC() { return c; } public void setC(int c) { this.c = c; } }
getFields()方法
复制public class Demo01Reflection { public static void main(String[] args) throws NoSuchFieldException { // 获取Person的Class对象 Class<?> personClass = Person.class; // 获取所有public修饰的成员变量 Field[] fields = personClass.getFields(); for (Field field : fields) { System.out.println(field); } } }
运行程序,控制台输出:
复制public int view.study.demo45.Person.a public int view.study.demo45.Person.b public int view.study.demo45.Person.c
假如想要获取:成员变量的初始值(如上例中)
复制Object o = field.get(new Person());
类似的,也可以使用java.lang.Class中的set()方法,为成员变量设置值。
复制* Field:成员变量 * 操作: 1. 设置值 * void set(Object obj, Object value) 2. 获取值 * get(Object obj) 3. 忽略访问权限修饰符的安全检查 * setAccessible(true):暴力反射
getField(String name)方法
复制public class Demo01Reflection { public static void main(String[] args) throws NoSuchFieldException { // 获取Person的Class对象 Class<?> personClass = Person.class; // 获取指定名称的 public修饰的成员变量 Field a = personClass.getField("a"); System.out.println(a); } }
运行程序,控制台输出:
复制public int view.study.demo45.Person.a
getDeclaredFields()方法
复制public class Demo01Reflection { public static void main(String[] args) throws NoSuchFieldException { // 获取Person的Class对象 Class<?> personClass = Person.class; // 获取所有的成员变量,不考虑修饰符 Field[] declaredFields = personClass.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println(declaredField); } } }
运行程序,控制台输出:
复制private java.lang.String view.study.demo45.Person.name private int view.study.demo45.Person.age public int view.study.demo45.Person.a public int view.study.demo45.Person.b public int view.study.demo45.Person.c
getDeclaredField(String name)方法
复制public class Demo01Reflection { public static void main(String[] args) throws NoSuchFieldException { // 获取Person的Class对象 Class<?> personClass = Person.class; // 获取指定名称的成员变量,不考虑修饰符 Field name = personClass.getDeclaredField("name"); Field b = personClass.getDeclaredField("b"); System.out.println(name); System.out.println(b); } }
运行程序,控制台输出:
复制private java.lang.String view.study.demo45.Person.name public int view.study.demo45.Person.b
Class对象获取功能:获取构造方法
复制Constructor<?>[] getConstructors() // 返回一个构造函数对象数组,该构造函数对象数组每个元素,都反映Class对象表示的类所对应公共构造函数。 Constructor<T> getConstructor(类<?>... parameterTypes) // 返回一个构造函数对象,该构造函数对象反映此Class对象表示的类所指定公共构造函数。 Constructor<?>[] getDeclaredConstructors() // 返回一个构造函数对象数组,该构造函数对象数组每个元素,都反映Class对象(或interface对象)表示的类所对应公共构造函数。 Constructor<T> getDeclaredConstructor(类<?>... parameterTypes) // 返回一个构造函数对象,该构造函数对象反映此Class对象(或interface对象)表示的类所指定公共构造函数。
下面就介绍:getConstructors()方法和getConstructors()方法。剩下两种方法的使用如介绍中方法一致
getConstructor()方法
这里例子中,所使用的Person.java,使用的是“获取成员变量”例子中定义的。
复制import java.lang.reflect.Constructor; public class Demo02Reflection { public static void main(String[] args) throws NoSuchMethodException { // 创建Person.class对象 Person person = new Person("LeeHua", 22, 6, 6, 6); // 获取Person的Class对象 Class<?> personClass = person.getClass(); // 获取一个Person.class构造函数对象 Constructor<?> constructor = personClass.getConstructor(); System.out.println(constructor); } }
运行程序,控制台输出:
复制public view.study.demo45.Person()
getConstructors()方法
这里例子中,所使用的Person.java,使用的是“获取成员变量”例子中定义的。
复制import java.lang.reflect.Constructor; public class Demo02Reflection { public static void main(String[] args) throws NoSuchMethodException { // 创建Person.class对象 Person person = new Person("LeeHua", 22, 6, 6, 6); // 获取Person的Class对象 Class<?> personClass = person.getClass(); // 获取一个Person.class构造函数数组对象 Constructor<?>[] constructors = personClass.getConstructors(); for (Constructor<?> constructor1 : constructors) { System.out.println(constructor1); } } }
运行程序,控制台输出:
复制public view.study.demo45.Person() public view.study.demo45.Person(java.lang.String,int,int,int,int)
Class对象获取功能:获取成员方法
复制Method[] getMethods() // 返回一个包含{@code Method}对象的数组,这些对象反映此{@code Class}对象表示的类或接口的所有公共方法,包括由类或接 // 口声明的方法以及从超类和超接口继承的方法。 Method getMethod(String name, 类<?>... parameterTypes) // 返回一个指定的{@code Method}对象,该对象反映此{@code Class}对象表示的类或接口的指定公共成员方法。 Method[] getDeclaredMethods() // 返回一个包含{@code Method}对象的数组,这些对象反映了此{@code Class}对象表示的类或接口的所有已声明方法,包括公共, // 受保护,默认(程序包)访问和私有方法,但不包括继承的方法。 Method getDeclaredMethod(String name, 类<?>... parameterTypes) // 返回一个指定的{@code Method}对象,该对象反映此{@code Class}对象表示的类或接口的指定声明方法。
getMethods()方法
复制package view.study.demo45; import java.lang.reflect.Method; public class Demo03Reflection { public static void main(String[] args) throws NoSuchMethodException { // 创建Person.class对象 Person person = new Person("LeeHua", 22, 6, 6, 6); // 获取Person的Class对象 Class<?> personClass = person.getClass(); // 获取一个包含{@code Method}对象的数组 Method[] methods = personClass.getMethods(); for (Method method : methods) { System.out.println(method.getName()); } } }
运行程序,控制台输出:
复制toString getName setName getAge setAge getB setB getC setC getA setA wait wait wait equals hashCode getClass notify notifyAll
其中红色部分是其父类或其继承的接口的方法。
getMethod(String name, 类<?>... parameterTypes)方法
复制package view.study.demo45; import java.lang.reflect.Method; public class Demo03Reflection { public static void main(String[] args) throws NoSuchMethodException { // 创建Person.class对象 Person person = new Person("LeeHua", 22, 6, 6, 6); // 获取Person的Class对象 Class<?> personClass = person.getClass(); // 返回一个指定的{@code Method}对象 Method getAge = personClass.getMethod("getAge"); System.out.println(getAge); } }
运行程序,控制台输出:
复制public int view.study.demo45.Person.getAge()
getDeclaredMethods()方法
复制package view.study.demo45; import java.lang.reflect.Method; public class Demo03Reflection { public static void main(String[] args) throws NoSuchMethodException { // 创建Person.class对象 Person person = new Person("LeeHua", 22, 6, 6, 6); // 获取Person的Class对象 Class<?> personClass = person.getClass(); Method[] declaredMethods = personClass.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.println(declaredMethod); } } }
运行程序,控制台输出:
复制public java.lang.String view.study.demo45.Person.toString() public java.lang.String view.study.demo45.Person.getName() public void view.study.demo45.Person.setName(java.lang.String) public int view.study.demo45.Person.getAge() public void view.study.demo45.Person.setAge(int) public int view.study.demo45.Person.getB() public void view.study.demo45.Person.setB(int) public int view.study.demo45.Person.getC() public void view.study.demo45.Person.setC(int) public int view.study.demo45.Person.getA() public void view.study.demo45.Person.setA(int)
getDeclaredMethod(String name, 类<?>... parameterTypes)方法
复制package view.study.demo45; import java.lang.reflect.Method; public class Demo03Reflection { public static void main(String[] args) throws NoSuchMethodException { // 创建Person.class对象 Person person = new Person("LeeHua", 22, 6, 6, 6); // 获取Person的Class对象 Class<?> personClass = person.getClass(); Method getName = personClass.getDeclaredMethod("getName"); System.out.println(getName); } }
运行程序,控制台输出:
复制public java.lang.String view.study.demo45.Person.getName()
案例
需求
写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法
实现之前,定义Person.java、Student.java:
复制package view.study.demo46; public class Person { private String name; private int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } public void personMethod() { System.out.println("我是Person中的方法!!!"); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } package view.study.demo46; public class Student { private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } public void studentMethod() { System.out.println("我是Student中的方法!!!"); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
实现步骤
- 将需要创建的对象的全类名和需要执行的方法定义在配置文件中
- 在程序中加载读取配置文件
- 使用反射技术来加载类文件进内存
- 创建对象
- 执行方法
实现
第一步:创建配置文件——pro.properties,并将需要创建的对象的全类名和需要执行的方法定义在配置文件中
配置文件内容如下:
复制className=view.study.demo46.Person methodName=personMethod
第二步:在程序中加载获取配置文件*
*
复制// 1. 在程序中加载读取配置文件 // 1.1 创建Properties对象 Properties pro = new Properties(); // 1.2 加载配置文件,转换为一个集合 // 1.2.1 获取类的类加载器 ClassLoader classLoader = DemoReflection.class.getClassLoader(); // 1.2.2 读取配置文件 // InputStream inputStream = classLoader.getResourceAsStream("pro.properties"); InputStream inputStream = new FileInputStream("/Users/liyihua/IdeaProjects/Study/src/view/study/demo46/pro.properties"); // 1.2.3 加载配置文件 pro.load(inputStream);
第三步:获取配置文件中的定义数据
复制// 2. 获取配置文件中定义的数据 // 2.1 获取类名称 String className = pro.getProperty("className"); // 2.2 获取方法名称 String methodName = pro.getProperty("methodName");
第四步:加载该类进内存
复制// 3. 加载该类进内存 Class<?> aClass = Class.forName(className);
第五步:创建对象
复制// 4. 创建对象 Object object = aClass.newInstance();
第六步:获取方法对象
复制// 5. 获取方法对象 Method method = aClass.getMethod(methodName);
第七步:执行方法
复制// 6. 执行方法 method.invoke(object);
代码总和:
复制import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Properties; public class DemoReflection { public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { // 1. 在程序中加载读取配置文件 // 1.1 创建Properties对象 Properties pro = new Properties(); // 1.2 加载配置文件,转换为一个集合 // 1.2.1 获取类的类加载器 ClassLoader classLoader = DemoReflection.class.getClassLoader(); // 1.2.2 读取配置文件 // InputStream inputStream = classLoader.getResourceAsStream("pro.properties"); InputStream inputStream = new FileInputStream("/Users/liyihua/IdeaProjects/Study/src/view/study/demo46/pro.properties"); // 1.2.3 加载配置文件 pro.load(inputStream); // 2. 获取配置文件中定义的数据 // 2.1 获取类名称 String className = pro.getProperty("className"); // 2.2 获取方法名称 String methodName = pro.getProperty("methodName"); // 3. 加载该类进内存 Class<?> aClass = Class.forName(className); // 4. 创建对象 Object object = aClass.newInstance(); // 5. 获取方法对象 Method method = aClass.getMethod(methodName); // 6. 执行方法 method.invoke(object); } }
运行程序,控制台输出:
复制我是Person中的方法!!!
修改配置文件中的内容:
复制className=view.study.demo46.Student methodName=studentMethod
运行程序,控制台输出:
复制我是Student中的方法!!!
如此,修改配置文件,不用修改代码。实现了创建任意类的对象,并且执行其中任意方法。
本文来自博客园,作者:LeeHua,转载请注明原文链接:https://www.cnblogs.com/liyihua/p/12302031.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)