Class类
Class类是Java反射机制的核心类之一。它代表了一个类的元数据,并提供了访问该类信息的功能。通过Class类,可以在运行时动态地获取类的名称、字段、方法、构造方案、父类等信息。更重要的是,class类能够在你在程序运行的时候创建对象。
通俗来讲,Class类就是 我们编写的class 也就是类的模版,它定义了 一个类需要的信息和它的结构,由Class 我们可以知道,类的结构是怎么样的。
当虚拟机在进行了类加载的时候,会先找到类路径下的.class文件,然后根据内容创建Class对象,根据Class对象内部的信息,我们可以创建一个具体的类对象。
也可以把Class类理解成 对类的抽象,它定义了 类应该由什么组成 比如说:类名、构造方法、属性、行为、父类、接口等信息。
这里需要意一下,Class类是一个泛型类,同时每一个对象只能有一个Class,类模版与类一对一。
Field类
Field
类是 Java 反射机制的一部分。它代表类的一个成员变量(字段),并提供对这些字段的访问能力。通过 Field
类,你可以在运行时获取关于类字段的信息,并且可以通过反射机制动态地访问或修改这些字段的值。
Field
类的主要功能包括:
- 获取字段的名称、类型、修饰符等信息;
- 通过反射访问字段的值(包括私有字段);
- 修改字段的值。
通俗的理解,可以把Field 作为 类属性的抽象,可以获取属性相关的一些信息。Class类模版包含Field。
Method类
它代表 Java 类中的一个方法,并允许在运行时动态地访问、调用方法。
Method
类主要用于获取有关类方法的信息,并通过反射调用这些方法。它支持访问方法的参数、返回类型、修饰符等信息,并提供调用方法的能力。
Class类模版包含Method。
反射基本使用方法
获取Class对象的三个方法,JDK提供了三种获取类模版的方法和其他方法,下面我们逐个学习一下。
public class RefectTest {public static void main(String[] args) throws ClassNotFoundException {// 类型.class 获取Class<RefectTest> refectTestClass = RefectTest.class;System.out.println(refectTestClass.getName()); // 获取类名System.out.println(refectTestClass.getSimpleName()); // 获取简单类名System.out.println(refectTestClass.getCanonicalName()); // 获取标准类名System.out.println("---------------------------------------------------------------------");// 对象.getClass() 获取RefectTest refectTest = new RefectTest();Class<? extends RefectTest> refectTestClass1 = refectTest.getClass();System.out.println(refectTestClass1.getName());System.out.println("---------------------------------------------------------------------");// Class.forName(类全限定名) 获取Class<?> refectTestClass2 = Class.forName("com.shifei.test.springredis.RefectTest");System.out.println(refectTestClass2.getName());}
}
获取类的名称
System.out.println(refectTestClass.getName()); // 类名称System.out.println(refectTestClass.getSimpleName()); // 简单类名称 只有类名 没有包路径System.out.println(refectTestClass.getCanonicalName()); // 标准的类名称
获取类的父类
Class<? super RefectTest> superclass = refectTestClass.getSuperclass();System.out.println(superclass.getName());
获取类的接口
Class<?>[] interfaces = refectTestClass.getInterfaces();for (Class<?> anInterface : interfaces){System.out.println(anInterface.getName());}
获取类的属性
// 只获取公开的 访问修饰符 是public的Field[] fields = refectTestClass.getFields();for (Field field : fields){System.out.println(field.getName());}System.out.println("---------------------------------------------------------------------");Field[] fields1 = refectTestClass.getDeclaredFields();// 获取全部访问修饰符for (Field field : fields1){System.out.println(field.getName());}
修改类的属性
RefectTest refectTest = new RefectTest();try {// 根据名称获取 忽略访问权限修饰Field privateFiled = refectTestClass.getDeclaredField("privateFiled");// 对私有的访问 需要修改访问权限 打破封装(对私有操作必须有)privateFiled.setAccessible(true);// 给创建出来的对象的私有属性设置值privateFiled.set(refectTest,"123");System.out.println(privateFiled.get(refectTest));} catch (NoSuchFieldException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}
获取 构造器方法
try {// 获取无参构造方法 Declared 忽略访问权限Constructor<RefectTest> declaredConstructor = refectTestClass.getDeclaredConstructor();// 根据构造方法类型获取构造器Constructor<RefectTest> declaredConstructor1 = refectTestClass.getDeclaredConstructor(String.class, String.class);// 有什么用 构造器有啥用 创建对象被RefectTest refectTest = declaredConstructor.newInstance();System.out.println(refectTest);RefectTest refectTest1 = declaredConstructor1.newInstance("123", "456");System.out.println(refectTest1);} catch (NoSuchMethodException e) {e.printStackTrace();} catch (InvocationTargetException e) {throw new RuntimeException(e);} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}RefectTest{privateFiled='null', publicFiled='null'}RefectTest{privateFiled='123', publicFiled='456'}
获取全部方法
Method[] declaredMethods = refectTestClass.getDeclaredMethods();for (Method method : declaredMethods){System.out.println(method.getName());}
执行方法
RefectTest refectTest = new RefectTest();Method method = null;try {method = refectTestClass.getMethod("setPublicFiled", String.class);method.invoke(refectTest,"123");System.out.println(refectTest.getPublicFiled());} catch (NoSuchMethodException e) {e.printStackTrace();}catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}
场景实践
反射获取方法时会面临一个问题,就是多态,你传入的参数类型,不是方法中入参的类型而是它的子类,那么在获取方法动态用参数的类型去匹配方法就会找不到(坑)
解决思路 先判断参数数量,然后循环判断动态传入的参数类型是否是方法参数类型的子类
获取全部的方法
循环匹配方法名
判断每个方法参数数量与方法入参数量是否一致 不一致返回false
如果一致则判断是否是子类型
注意参数传入顺序
/*** 根据 方法名、参数类型 匹配方法* @param clazz 获取全部方法的Class类* @param methodName 需要获取的方法名称* @param inputParamTypes 需要获取方法的参数列表类型(注意顺序)* @return 返回匹配的方法对象*/@Nullablepublic static Method matchMethod(Class<?> clazz,String methodName,Class<?>[] inputParamTypes){Method[] declaredMethods = clazz.getDeclaredMethods();for (Method method : declaredMethods){if (method.getName().equals(methodName) && paramTypeMatch(method.getParameterTypes(),inputParamTypes)){return method;}}return null;}public static boolean paramTypeMatch(Class<?>[] parameterTypes,Class<?>[] inputParamTypes){if (parameterTypes.length != inputParamTypes.length){return false;}for (int i = 0; i < parameterTypes.length; i++){if (!parameterTypes[i].isAssignableFrom(inputParamTypes[i])) {return false;}}return true;}
未完待续