Java反射机制详解:动态访问和操作对象

目录

  1. 先看个例子
  2. 基本概念
  3. Class 2.1 获取Class 2.2 通过Class获取类修饰符和类型
  4. Member 3.1 Field 3.1.1 获取Field 3.1.2 获取变量类型、修饰符、注解 3.1.3 获取、设置变量值 3.2 Method 3.2.1 获取Method 3.2.2 获取方法返回类型 3.2.3 获取方法参数类型 3.2.4 获取方法声明抛出的异常类型 3.2.5 获取方法参数名称 3.2.6 通过反射调用方法 3.3 Constructor 3.3.1 获取构造方法 3.3.2 创建对象
  5. 数组和枚举 4.1 数组 4.2 枚举
  6. 反射的缺点 参考

1. 先看个例子

public class People {
    private People(int age) {
    }
    public void show() {
        System.out.println("test reflect");
    }
}

//test code
Class<?> clazz = Class.forName("com.xfhy.ref.People");
Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(int.class);
declaredConstructor.setAccessible(true);
People person = (People) declaredConstructor.newInstance(12);
person.show();
复制代码
通过这个简单的例子,我们可以看到Java反射的强大之处:即使People的构造方法是私有的,我们仍然可以通过反射机制来创建其实例。

2. 基本概念

Java的反射机制能够:

  • 对于任意一个类,都能够知道这个类的所有属性和方法。
  • 对于任意一个对象,都能够调用它的任意一个方法和属性。

这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。反射机制主要涉及两个类:ClassMember

3. Class

Class是反射能够实现的基础,Class是JDK提供的一个类,完整路径是java.lang.Class。本质上这个类和MathString或者自己定义的各种类没有区别。

3.1 获取Class

获取Class对象的几种方法:

  • Object.getClass():对象实例调用getClass方法。
  • The.class:类调用.class,例如String.class
  • Class.forName(""):传入类的全路径。
  • The.TYPE:例如Double.TYPE
  • Class.getSuperclass():某个实例调用getClass().getSuperclass()

3.2 通过Class获取类修饰符和类型

HashMap为例,我们可以通过反射获取类的修饰符、泛型信息、实现的接口、父类和注解等信息。

Class<?> clazz = HashMap.class;
System.out.println("Class : " + clazz.getCanonicalName());
System.out.println(Modifier.toString(clazz.getModifiers()));
TypeVariable<? extends Class<?>>[] typeParameters = clazz.getTypeParameters();
StringBuilder stringBuilder = new StringBuilder("Parameters : ");
for (TypeVariable<? extends Class<?>> typeParameter : typeParameters) {
    stringBuilder.append(typeParameter.getName()).append(" ");
}
System.out.println(stringBuilder.toString());

Type[] genericInterfaces = clazz.getGenericInterfaces();
StringBuilder interfaces = new StringBuilder("Implemented Interfaces : ");
for (Type intf : genericInterfaces) {
    interfaces.append(intf.toString()).append(" ");
}
System.out.println(interfaces.toString());

Class<?> superclass = clazz.getSuperclass();
if (superclass != null) {
    System.out.println(superclass.getCanonicalName());
}

Annotation[] annotations = clazz.getAnnotations();
StringBuilder annotation = new StringBuilder("Annotations : ");
for (Annotation a : annotations) {
    annotation.append(a.toString()).append(" ");
}
System.out.println(annotation.toString());

4. Member

Member有三个实现类:

  • java.lang.reflect.Field:类变量
  • java.lang.reflect.Method:类方法
  • java.lang.reflect.Constructor:类构造方法

4.1 Field

通过Field可以访问给定类对象的类变量,包括获取变量的类型、修饰符、注解、变量名、变量值等,即使是private的也是OK的。

4.1.1 获取Field

Class提供了四种方法获得给定类的Field

  • getDeclaredField(String name):获取指定的变量,包括private的。
  • getField(String name):获取指定的变量,只能是public的。
  • getDeclaredFields():获取所有变量,包括private的。
  • getFields():获取所有变量,只能是public的。
4.1.2 获取变量类型、修饰符、注解
Class clazz = People.class;
Field[] declaredFields = clazz.getDeclaredFields();
for (Field field : declaredFields) {
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.append("变量名: ").append(field.getName()).append("\n");
    stringBuilder.append("变量类型: ").append(field.getType()).append("\n");
    stringBuilder.append("变量修饰符: ").append(Modifier.toString(field.getModifiers())).append("\n");
    Annotation[] annotations = field.getAnnotations();
    if (annotations.length != 0) {
        stringBuilder.append(" 变量注解 : ");
        for (Annotation a : annotations) {
            stringBuilder.append(a.toString()).append(" ");
        }
    }
    System.out.println(stringBuilder.toString());
}
4.1.3 获取、设置变量值
People people = new People("张三", 17);
Class<? extends People> peopleClass = people.getClass();
try {
    Field nameField = peopleClass.getDeclaredField("name");
    nameField.setAccessible(true);
    Field ageField = peopleClass.getField("age");
    String name = (String) nameField.get(people);
    int age = ageField.getInt(people);
    System.out.println("name = " + name + ", age = " + age);
    nameField.set(people, "李四");
    ageField.set(people, 18);
    System.out.println(people.toString());
} catch (NoSuchFieldException | IllegalAccessException e) {
    e.printStackTrace();
}

4.2 Method

4.2.1 获取Method

Class提供了四种方法获取Method

  • getDeclaredMethod(String name, Class<?>... parameterTypes)
  • getMethod(String name, Class<?>... parameterTypes)
  • getDeclaredMethods()
  • getMethods()
4.2.2 获取方法返回类型
  • getReturnType():获取目标方法返回类型对应的Class对象。
  • getGenericReturnType():获取目标方法返回类型对应的Type对象。
4.2.3 获取方法参数类型
  • getParameterTypes():获取目标方法各参数类型对应的Class对象。
  • getGenericParameterTypes():获取目标方法各参数类型对应的Type对象。
4.2.4 获取方法声明抛出的异常类型
  • getExceptionTypes():获取目标方法抛出的异常类型对应的Class对象。
  • getGenericExceptionTypes():获取目标方法抛出的异常类型对应的Type对象。
4.2.5 获取方法参数名称

.class文件中默认不存储方法参数名称,如果想要获取方法参数名称,需要在编译的时候加上-parameters参数。

4.2.6 通过反射调用方法

通过Methodinvoke()方法来反射调用目标方法,第一个参数为需要调用的目标类对象。如果方法是static的,则该参数为null,后面的参数是目标方法的参数值,顺序与目标方法声明中的参数顺序一致。

4.3 Constructor

4.3.1 获取构造方法

Method类似。

4.3.2 创建对象

一般使用java.lang.reflect.Constructor.newInstance()来构建对象,另一种Class.newInstance()已经废弃。

5. 数组和枚举

5.1 数组

数组类型:数组本质上是一个对象,所以它也有自己的类型。例如对于int[] intArray,数组类型为class [I。数组类中的[个数代表数组的维度,例如[代表一维数组,[[代表二维数组,[后面的字母代表数组元素类型,I代表int,一般为类型的首字母大写(long类型例外,为J)。

创建和初始化数组:

Object array = Array.newInstance(int.class, 2);
Array.set(array, 0, 1);
Array.set(array, 1, 2);
System.out.println(Array.get(array, 1));

多维数组:Java反射没有提供能够直接访问多维数组元素的API,但你可以把多维数组当成数组的数组处理。

5.2 枚举

枚举隐式继承自java.lang.EnumEnum继承自Object

结论

Java反射机制是Java语言的一个重要特性,它提供了一种动态访问和操作对象的能力。虽然它有性能和安全上的考量,但在需要动态性和灵活性的场景下,反射是一个不可或缺的工具。理解和掌握反射机制,可以帮助我们编写出更加灵活和强大的Java程序。

相关推荐
转世成为计算机大神1 分钟前
易考八股文之Java中的设计模式?
java·开发语言·设计模式
宅小海20 分钟前
scala String
大数据·开发语言·scala
qq_3273427322 分钟前
Java实现离线身份证号码OCR识别
java·开发语言
锅包肉的九珍23 分钟前
Scala的Array数组
开发语言·后端·scala
心仪悦悦27 分钟前
Scala的Array(2)
开发语言·后端·scala
yqcoder1 小时前
reactflow 中 useNodesState 模块作用
开发语言·前端·javascript
baivfhpwxf20231 小时前
C# 5000 转16进制 字节(激光器串口通讯生成指定格式命令)
开发语言·c#
许嵩661 小时前
IC脚本之perl
开发语言·perl
长亭外的少年1 小时前
Kotlin 编译失败问题及解决方案:从守护进程到 Gradle 配置
android·开发语言·kotlin
直裾1 小时前
Scala全文单词统计
开发语言·c#·scala