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程序。

相关推荐
用余生去守护3 分钟前
python报错系列(16)--pyinstaller ????????
开发语言·python
yuanbenshidiaos6 分钟前
c++---------数据类型
java·jvm·c++
数据小爬虫@8 分钟前
利用Python爬虫快速获取商品历史价格信息
开发语言·爬虫·python
向宇it10 分钟前
【从零开始入门unity游戏开发之——C#篇25】C#面向对象动态多态——virtual、override 和 base 关键字、抽象类和抽象方法
java·开发语言·unity·c#·游戏引擎
Lojarro23 分钟前
【Spring】Spring框架之-AOP
java·mysql·spring
莫名其妙小饼干26 分钟前
网上球鞋竞拍系统|Java|SSM|VUE| 前后端分离
java·开发语言·maven·mssql
十年一梦实验室36 分钟前
【C++】sophus : sim_details.hpp 实现了矩阵函数 W、其导数,以及其逆 (十七)
开发语言·c++·线性代数·矩阵
isolusion38 分钟前
Springboot的创建方式
java·spring boot·后端
最爱番茄味1 小时前
Python实例之函数基础打卡篇
开发语言·python
zjw_rp1 小时前
Spring-AOP
java·后端·spring·spring-aop