目录
前言
一、概述
二、获取Class对象
三、反射构造方法
1. 获取构造方法
2. 获取修饰符、名称和形参
3. 创建对象
四、反射成员变量
1. 获取成员变量
2. 获取修饰符、名称和类型
3. 赋值/获取值
五、 反射成员方法
1. 获取成员方法
2. 获取修饰符、形参、返回值和异常
3. 运行成员方法
总结
前言
在当今的软件开发领域,灵活性和可扩展性成为了软件设计的重要考量因素。Java反射机制作为实现这一目标的强大工具之一,为开发人员提供了在运行时动态获取类的信息或动态调用对象的方法的能力。通过使用反射,开发人员可以在不知道具体实现细节的情况下,操作类、构造函数、方法及字段等。
一、概述
Java反射机制是指在运行时动态获取类的信息或动态调用对象的方法、修改属性等操作。主要核心就是Class类、Constructor类、Field类、Method类等API。 反射机制主要应用于框架开发、动态代理、ORM框架、JDBC驱动等方面。通过反射机制,程序员能够获得在编译期间不被知晓的类、属性、方法等信息。但是反射机制的性能较低,常常被认为是一种牺牲性能换取灵活性的实现方式。
二、获取Class对象
在Java中,我们有三种方式获取Class对象,下面是这三种方式:
方式 | 示例 | 使用时机 |
---|---|---|
方式一:使用Class类的forName()方法 | Class clazz = Class.forName("java.lang.Object"); | 源代码阶段 |
方式二:使用“类.class”语法 | Class clazz = Object.class; | 加载阶段 |
方式三:调用Object类的getClass()方法 | Object obj = new Object(); Class clazz = obj.getClass(); | 运行阶段 |
这三种方式其实就对应Java里面三种不同的阶段,如果我想创建一个类的对象,是分以下三个阶段的:第一个阶段,要先编写Java文件,然后把他编译成.class字节码文件,这个阶段还没有把代码加载到内存的,只是在硬盘里面进行的操作,所以这个阶段我们也叫做源代码阶段,这个阶段我们会用方式一去获取字节码文件对象。接下来要运行代码了,是不是首先要把这个类的字节码文件加载到内存?这个阶段我们叫做加载阶段,在这个阶段我们会用方式二。接下来去内存当中创建这个类的对象,比如A a = new A(),此时就叫做运行阶段,这个阶段用方式三。下图是对这三个方式的进一步使用的示例:
三、反射构造方法
1. 获取构造方法
Class类中有四种用于获取构造方法的方法,这四种方法的简单描述如下所示:
方法 | 描述 |
---|---|
Constructor<?>[] getConstructors(); | 返回所有“公共”构造方法对象的数组 |
Constructor<?>[] getDeclaredConstructors(); | 返回所有构造方法对象的数组 |
Constructor getConstructor(Class<?>…parameterTypes); | 返回单个“公共”构造方法对象 |
Constructor getDeclaredConstructor(Class<?>…parameterTypes); | 返回单个构造方法对象 |
下面,我们可以编写一个Student对象,该类中的属性和相关方法如下所示,后面的反射示例均会使用到这个类:
public class Student {private String name;private int age;public String gender;public Student() {}public Student(String name, int age) {this.name = name;this.age = age;}protected Student(int age) {this.age = age;}private Student(String name) {this.name = name;}public String getName() {return this.name;}public void setName(String name) {this.name = name;}public int getAge() {return this.age;}public void setAge(int age) {this.age = age;}void sayHello() {System.out.println("学生说你好!");}private String sayHello(String str) throws RuntimeException {System.out.println("你好你好!");return str;}}
下面我们编写Java代码来测试上面提到的四种获取构造方法的代码:
public static void main(String[] args) throws NoSuchMethodException {Class<Student> studentClass = Student.class;// 方式一Constructor<?>[] constructors = studentClass.getConstructors();for (Constructor<?> constructor : constructors) {System.out.println(constructor); // 仅输出public修饰的构造方法}System.out.println("==================================================================");// 方式二Constructor<?>[] declaredConstructors = studentClass.getDeclaredConstructors();for (Constructor<?> constructor : declaredConstructors) {System.out.println(constructor);}System.out.println("==================================================================");// 方式三Constructor<Student> constructor = studentClass.getConstructor(String.class, int.class);System.out.println(constructor); // 仅能获取public修饰的构造方法,如果不是,则会报错System.out.println("==================================================================");// 方式四Constructor<Student> declaredConstructor = studentClass.getDeclaredConstructor(int.class);System.out.println(declaredConstructor);
}
测试结果如下图所示:
2. 获取修饰符、名称和形参
如何获取构造方法上的修饰符、名称和形参呢?下面是我们的测试代码:
public static void main(String[] args) throws NoSuchMethodException {Class<Student> studentClass = Student.class;Constructor<Student> constructor = studentClass.getConstructor(String.class, int.class);int modifiers = constructor.getModifiers();System.out.println("修饰符整数标识:" + modifiers);String stringModifiers = Modifier.toString(modifiers);System.out.println(stringModifiers);System.out.println("===========================================");String name = constructor.getName();System.out.println(name); // 打印构造方法名称System.out.println("===========================================");Class<?>[] parameterTypes = constructor.getParameterTypes();for (Class<?> parameterType : parameterTypes) {System.out.println(parameterType); // 打印参数类型}System.out.println("===========================================");Parameter[] parameters = constructor.getParameters();for (Parameter parameter : parameters) {System.out.println(parameter); // 打印参数名称}
}
测试结果如下图所示:
3. 创建对象
Constructor类中用于创建对象的方法有:
方法 | 描述 |
---|---|
T newInstance(Object… initargs) | 根据指定构造方法创建对象 |
setAccessible(boolean flag) | 设置为true,表示取消访问检查 |
下面是一个示例代码:
public static void main(String[] args) throws Exception {Class<Student> studentClass = Student.class;Constructor<Student> constructor = studentClass.getDeclaredConstructor(String.class);constructor.setAccessible(true); // 取消访问检查Student student = constructor.newInstance("张三");System.out.println(student);
}
运行结果如下图所示:
四、反射成员变量
1. 获取成员变量
Class类中用于获取成员变量的方法有四种,这四类方法如下:
方法 | 描述 |
---|---|
Field[] getFields(); | 返回所有"公共"成员变量对象的数组 |
Field[] getDeclaredFields(); | 返回所有成员变量对象的数组 |
Field getField(String name); | 返回单个公共成员变量对象 |
Field getDeclaredField(String name); | 返回单个成员变量对象 |
下面我们编写Java代码对这四个方法进行测试:
public static void main(String[] args) throws NoSuchFieldException {Class<Student> studentClass = Student.class;// 方式一Field[] fields = studentClass.getFields();for (Field field : fields) {System.out.println(field);}System.out.println("===========================================================================");// 方式二Field[] declaredFields = studentClass.getDeclaredFields();for (Field field : declaredFields) {System.out.println(field);}System.out.println("===========================================================================");// 方式三Field gender = studentClass.getField("gender");System.out.println(gender);System.out.println("===========================================================================");// 方式四Field name = studentClass.getDeclaredField("name");System.out.println(name);
}
代码运行结果如下图所示:
2. 获取修饰符、名称和类型
如何获取成员变量上的修饰符、名称和形参呢?下面是我们的测试代码:
public static void main(String[] args) throws NoSuchFieldException {Class<Student> studentClass = Student.class;Field gender = studentClass.getField("gender");System.out.println(Modifier.toString(gender.getModifiers())); // 修饰符System.out.println(gender.getName()); // 名称System.out.println(gender.getType()); // 类型
}
测试结果如下图所示:
3. 赋值/获取值
Field类中用于赋值和获取值的方法:
方法 | 描述 |
---|---|
void set(Object obj, Object value) | 赋值 |
Object get(Object obj) | 获取值 |
下面是一个示例代码:
public static void main(String[] args) throws Exception {Student student = new Student("张三", 18);Class<? extends Student> studentClass = student.getClass();Field name = studentClass.getDeclaredField("name");name.setAccessible(true);Object oldValue = name.get(student);System.out.println(oldValue); // 获取值name.set(student, "李四"); // 设置值System.out.println(student);
}
运行结果如下:
五、 反射成员方法
1. 获取成员方法
Class类中用于获取成员方法的方法有四个,这四个方法如下所示:
方法 | 描述 |
---|---|
Method[] getMethods(); | 返回所有公共成员方法对象的数组,包括继承的 |
Method[] getDeclaredMethods(); | 返回所有成员方法对象的数组,不包括继承的 |
Method getMethod(String name, Class<?>… parameterTypes); | 返回单个公共成员方法对象 |
Method getDeclaredMethod(String name, Class<?> … parameterTypes); | 返回单个成员方法对象 |
下面是这四个方法的使用示例代码:
public static void main(String[] args) throws NoSuchMethodException {// 方式一Class<Student> studentClass = Student.class;Method[] methods = studentClass.getMethods();for (Method method : methods) {System.out.println(method);}System.out.println("=======================================================================");// 方式二Method[] declaredMethods = studentClass.getDeclaredMethods();for (Method method : declaredMethods) {System.out.println(method);}System.out.println("=======================================================================");// 方式三Method setName = studentClass.getMethod("setName", String.class);System.out.println(setName);System.out.println("=======================================================================");// 方式四Method sayHello = studentClass.getDeclaredMethod("sayHello");System.out.println(sayHello);
}
运行结果如下图所示:
2. 获取修饰符、形参、返回值和异常
如何获取成员变量上的修饰符、形参、返回值和异常呢?下面是我们的测试代码:
public static void main(String[] args) throws NoSuchMethodException {Class<Student> studentClass = Student.class;Method sayHello = studentClass.getDeclaredMethod("sayHello", String.class);System.out.println(Modifier.toString(sayHello.getModifiers())); // 修饰符System.out.println("=============================================");Class<?>[] parameterTypes = sayHello.getParameterTypes();for (Class<?> parameterType : parameterTypes) {System.out.println(parameterType); // 形参类型}System.out.println("=============================================");Parameter[] parameters = sayHello.getParameters();for (Parameter parameter : parameters) {System.out.println(parameter); // 形参}System.out.println("=============================================");Class<?> returnType = sayHello.getReturnType();System.out.println(returnType); // 返回值类型System.out.println("=============================================");Class<?>[] exceptionTypes = sayHello.getExceptionTypes();for (Class<?> exceptionType : exceptionTypes) {System.out.println(exceptionType);} // 异常
}
测试结果如下图所示:
补充:我们还可以反射方法上的注解,进一步提高程序的扩展性,下面是一篇参考博文:
Java Reflect - 利用反射获取类上的注解
3. 运行成员方法
Method类中用于创建对象的方法如下所示:
方法 | 描述 |
---|---|
Object invoke(Object obj, Object… args); | 运行方法 参数一:用obj对象调用该方法 参数二:调用方法的传递的参数(如果没有就不写) 返回值:方法的返回值(如果没有就不写) |
下面是我们的测试代码:
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {Student student = new Student();Class<Student> studentClass = Student.class;Method sayHello = studentClass.getDeclaredMethod("sayHello", String.class);sayHello.setAccessible(true);Object res = sayHello.invoke(student, "人生入戏");System.out.println(res);
}
运行结果如下:
总结
本文探讨了Java反射机制的基础知识,包括获取Class对象的不同方式、反射构造方法、获取成员方法和成员变量等。通过一系列的实际代码示例,我们展示了如何利用反射机制来动态地获取类的信息、创建对象、获取构造方法和成员方法的细节、以及如何对成员变量进行操作。尽管反射机制为Java开发带来了极大的灵活性,但也应注意到它可能会带来的性能影响。因此,在实际项目中,应当合理使用反射,确保在灵活性与性能之间取得良好的平衡。掌握反射机制不仅有助于深入理解Java语言的内部工作原理,而且还能帮助开发者更有效地使用现有的框架和技术栈,提高软件的可维护性和扩展性。