1、反射-基础知识

1.1、运行时类型识别[RRIT]

  1. 1、全称:Run-Time Type Identification,运行时类型识别。
  2. 2、其作用:是在运行时识别一个对象的类型和类的信息。
  3. 3、其主要有两种方式:
  4. 3.1、一种是"传统的"RTTI,它假定我们在编译时已经知道了所有的类型;
  5. 3.2、另一种是"反射"机制,它允许我们在运行时发现和使用类的信息。

1.2、Class类

1.2.1、Class类概述

  1. 1Class类存在于java.lang包中,保存类运行时类型标识信息。
  2. 2Class类封装一个对象和接口运行时的状态,当装载类时,Class类型的对象自动创建。
  3. 3Class类提取这些类的一些共同特征,比如:这些类都有类名,都有对应的hashcode...
  4. 4Class类可以定义一些方法,比如: 获取某个方法、获取类型名等等.
  5. 5、在java中,每个类都有一个相应的Class对象,即当编写一个类,编译完成后,在生成的.class文件中,就会产生一个Class对象,用于表示这个类的类型信息;Class类的每个实例则代表运行中的一个类。
  6. 6

1.2.2、Class类-简介

  1. 1Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。
  2. 2、运行程序时,Java虚拟机(JVM)首先检查是否所要加载的类对应的Class对象是否已经加载。如果没有加载,JVM就会根据类名查找.class文件,并将其Class对象载入。虚拟机只会产生一份字节码, 用这份字节码可以产生多个实例对象。
  3. 3、每个java类运行时都在JVM里表现为一个class对象,可通过类名.class、类型.getClass()、Class.forName("类名")等方法获取class对象。
  4. 4、每个通过关键字class标识的类,在内存中有且只有一个与之对应的Class对象来描述其类型信息,无论创建多少个实例对象,其依据的都是用一个Class对象。
  5. 5Class类只存私有构造函数,因此对应Class对象只能有JVM创建和加载。
  6. 5.1、源码:
  7. //私有构造函数。只有Java虚拟机创建类对象
  8. private Class(ClassLoader loader) {
  9. //初始化类加载器的最后一个字段。非null的初始化值
  10. classLoader = loader;
  11. }

1.2.2.1、获取Class对象的三种方式

  1. 1、获取Class对象三种方式为:
  2. 1.1、通过类名获取:类名.class, 通过类名的属性class获取。多用于参数的传递
  3. 1.2、通过对象获取: 对象名.getClass(), .getClass()方法是定义在Objec类中的方法。多用于对象的获取字节码的方式
  4. 1.3、通过全类名获取: Class.forName(“全类名”) 将字节码文件加载进内存,返回Class对象。多用于配置文件,将类名定义在配置文件中.
  5. 1.4、基本类型的包装类,可以调用包装类的Type属性来获得该包装类的Class对象 包装类型.TYPE
  6. 1.5、基本数据类型.class
  7. 2、代码示例
  8. public class ReflectionTest {
  9. @Test
  10. public void testClass() throws ClassNotFoundException {
  11. Class clazz = null;
  12. //1.通过类名
  13. clazz = Person.class;
  14. //2.通过对象名
  15. //这种方式是用在传进来一个对象,却不知道对象类型的时候使用
  16. Person person = new Person();
  17. clazz = person.getClass();
  18. //上面这个例子的意义不大,因为已经知道person类型是Person类,再这样写就没有必要了
  19. //如果传进来是一个Object类,这种做法就是应该的
  20. Object obj = new Person();
  21. clazz = obj.getClass();
  22. //3.通过全类名(会抛出异常)
  23. //一般框架开发中这种用的比较多,因为配置文件中一般配的都是全类名,通过这种方式可以得到Class实例
  24. String className=" com.atguigu.java.fanshe.Person";
  25. clazz = Class.forName(className);
  26. //字符串的例子
  27. clazz = String.class;
  28. clazz = "javaTest".getClass();
  29. clazz = Class.forName("java.lang.String");
  30. System.out.println(clazz);
  31. //4.基本数据类型.class
  32. Class<Character> char1=char.class;
  33. //5.基本类型的包装类.TYPE
  34. Class<Boolean> bool=Boolean.TYPE;
  35. System.out.println(char1);//char
  36. System.out.println(bool);//boolean
  37. }
  38. }

1.2.2.2、Class类的常用方法

  1. 1static Class forName(String name) 返回指定类名 name Class 对象
  2. 2Object newInstance() 调用缺省构造函数,返回该Class对象的一个实例
  3. 2.1、代码示:
  4. //1.获取Class对象
  5. String className="com.atguigu.java.fanshe.Person";
  6. Class clazz = Class.forName(className);
  7. //利用Class对象的newInstance方法创建一个类的实例
  8. Object obj = clazz.newInstance();
  9. System.out.println(obj);
  10. 3Object newInstance(Object []args) 调用当前格式构造函数,返回该Class对象的一个实例
  11. 4getName() 返回此Class对象所表示的实体(类、接口、数组类、基本类型或void)名称
  12. 5Class getSuperClass() 返回当前Class对象的父类的Class对象
  13. 6Class [] getInterfaces() 获取当前Class对象的接口
  14. 7ClassLoader getClassLoader() 返回该类的类加载器
  15. 7.1、类加载器:https://www.yuque.com/moercheng/xgug88/akdpqt
  16. 8Class getSuperclass() 返回表示此Class所表示的实体(类、接口、基本类型或 void)的超类/父类的Class
  17. 8.1、由于编译擦除,没有显示泛型参数,即:此 Class 表示 Object 类、一个接口、一个基本类型或 void,则返回 null;如果此对象表示一个数组类,则返回表示该 Object 类的 Class 对象。
  18. 9Type getGenericSuperclass() 返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的直接超类的 Type
  19. 9.1、包含泛型参数:如果超类是参数化类型,则返回的 Type 对象必须准确反映源代码中所使用的实际类型参数。
  20. 9.2Type作为顶级接口,Type下还有几种类型,比如TypeVariableParameterizedTypeWildCardTypeGenericArrayType、以及Class。通过这些接口我们就可以在运行时获取[泛型]相关的信息。

1.2.2.3、Class类基本方法

  1. ClassLoader getClassLoader() 获取该类的类装载器。
  2. Class<?> getComponentType() 如果当前类表示一个数组,则返回表示该数组组件的 Class 对象,否则返回 null
  3. Constructor<T> getConstructor(Class[]) 返回当前 Class 对象表示的类的指定的公有构造子对象。
  4. Constructor<?>[] getConstructors() 返回当前 Class 对象表示的类的所有公有构造子对象数组。
  5. Constructor<T> getDeclaredConstructor(Class[]) 返回当前 Class 对象表示的类的指定已说明的一个构造子对象。
  6. Constructor<?>[] getDeclaredConstructors() 返回当前 Class 对象表示的类的所有已说明的构造子对象数组。
  7. Field getDeclaredField(String) 返回当前 Class 对象表示的类或接口的指定已说明的一个域对象。
  8. Field[] getDeclaredFields() 返回当前 Class 对象表示的类或接口的所有已说明的域对象[数组]。
  9. Method getDeclaredMethod(String, Class[]) 返回当前 Class 对象表示的类或接口的指定已说明的一个方法对象。
  10. Method[] getDeclaredMethods() 返回 Class 对象表示的类或接口的所有已说明的方法[数组]。 【获取不到父类的方法】
  11. Field getField(String) 返回当前 Class 对象表示的类或接口的指定的公有成员域对象。
  12. Field[] getFields() 返回当前 Class 对象表示的类或接口的所有可访问的公有域对象数组。
  13. Class<?>[] getInterfaces() 返回当前对象表示的类或接口实现的接口。 【由于编译擦除,没有显示泛型参数】
  14. Type[] getGenericInterface() 返回表示某些接口的 Type,这些接口由此对象所表示的类或接口直接实现。【包含泛型参数】
  15. Method getMethod(String, Class[]) 返回当前 Class 对象表示的类或接口的指定的[公有成员方法]对象。 【获取不到私有方法】
  16. Method[] getMethods() 返回当前 Class 对象表示的类或接口的所有公有成员方法对象数组,包括已声明的和从父类继承的方法。
  17. int getModifiers() 返回该类或接口的 Java 语言修改器代码: 以整数表示。修饰符由Java虚拟机的的常量
  18. String getName() 返回 Class 对象表示的类型(类、接口、数组或基类型)的完整路径名字符串。
  19. InputStream getResource(String) 按指定名查找资源。
  20. InputStream getResourceAsStream(String) 用给定名查找资源。
  21. Object[] getSigners() 获取类的签名者:如果没有签名者,则返回null
  22. Class<? super T> getSuperclass() 如果此对象表示除 Object 外的任一类, 那么返回此对象的父类对象。
  23. boolean isArray() 如果 Class 对象表示一个数组则返回 true, 否则返回 false
  24. boolean isAssignableFrom(Class) 判定 Class 对象表示的类或接口是否同参数指定的 Class 表示的类或接口相同,或是其父类。
  25. boolean isInstance(Object) 此方法是 Java 语言 instanceof 操作的动态等价方法。
  26. boolean isInterface() 判定指定的 Class 对象是否表示一个接口类型。
  27. boolean isPrimitive() 判定指定的 Class 对象是否表示一个 Java 的基类型。
  28. T newInstance() 创建类的新实例。
  29. String toString() 将对象转换为字符串。

1.2.2.4、获取父类的私有方法

  1. ###
  2. Method[] getDeclaredMethods() 返回 Class 对象表示的类或接口的所有已说明的方法数组。 【获取不到父类的方法】
  3. Method getMethod(String, Class[]) 返回当前 Class 对象表示的类或接口的指定的[公有成员方法]对象。 【获取不到私有方法】
  4. ###
  5. ###定义一个方法,不但能访问当前类的私有方法,还要能父类的私有方法

1.3、类加载

1.3.1、类加载机制

https://www.yuque.com/moercheng/xgug88/hxm42n

1.3.2、类字节码

https://www.yuque.com/moercheng/xgug88/wpcw9x

2、反射-概述

2.1、反射的定义

  1. 1、反射[Reflection],允许程序在执行期借助于Reflection API取得任何类的內部信息,并能直接操作任意对象的内部属性及方法。【动态获取动态调用对象方法】

2.2、反射机制功能

  1. 1、在运行时构造任意一个类的对象。 -->Class类.xxxConstructor()
  2. 2、在运行时获取任意一个类所具有的成员变量和方法。-->.xxxFiled()
  3. 3、在运行时调用任意一个对象的方法(属性)。-->.xxxMethod()
  4. 4、生成动态代理。

2.3、反射机制API简介

  1. 1、位于java.lang包中
  2. 1.1Class类:代表一个类
  3. 2、位于java.lang.reflect包中
  4. 2.1Field类:代表类的成员变量(成员变量也称为类的属性)
  5. 2.2Method类:代表类的方法
  6. 2.3Constructor类:代表类的构造方法
  7. 2.4Array类:提供了动态创建数组,以及访问数组的元素的静态方法

3、反射机制-简介

3.1、反射机制执行流程

3.1.1、示例代码

  1. public class ReflectDemo {
  2. public static void main(String[] args) {
  3. try {
  4. ReflectDemo demo = (ReflectDemo)Class.forName("com.alipay.antcloud.bootstrap.util.ReflectDemo").newInstance();
  5. demo.sayHello("call directly");
  6. // 2. 根据配置的函数名,进行方法调用(不需要通用的接口抽象)
  7. ReflectDemo t2 = new ReflectDemo();
  8. Method method = t2.getClass().getDeclaredMethod("sayHello", String.class);
  9. method.invoke(demo, "method invoke");
  10. } catch (Exception e) {
  11. System.out.println(e.getMessage());
  12. }
  13. }
  14. private void sayHello(String sayStr) {
  15. System.out.println("sayStr: "+sayStr);
  16. }
  17. }

3.1.2、示例代码分析

  1. 1、通过反射获取类示例:Class.forName("")
  2. @CallerSensitive
  3. public static Class<?> forName(String className)
  4. throws ClassNotFoundException {
  5. // 先通过反射,获取调用进来的类信息,从而获取当前的 classLoader
  6. Class<?> caller = Reflection.getCallerClass();
  7. // 调用native方法进行获取class信息
  8. return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
  9. }
  10. 2、调用.newInstance()方法获取对应实例
  11. 2.1T newInstance()主要作用:
  12. 2.1.1. 权限检测,如果不通过直接抛出异常;
  13. 2.1.2. 查找无参构造器,并将其缓存起来;
  14. 2.1.3. 调用具体方法的无参构造方法,生成实例并返回;
  15. 3、反射获取实例的方法:Xxx.class.getDeclaredMethod();
  16. // java.lang.Class
  17. @CallerSensitive
  18. public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
  19. throws NoSuchMethodException, SecurityException {
  20. //权限检查
  21. checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
  22. //根据方法名称和方法列表,选出符合要求的方法
  23. Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
  24. //如果没有找到相应方法,抛出异常,否则返回对应方法
  25. if (method == null) {
  26. throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
  27. }
  28. return method;
  29. }
  30. 4、调用method.invoke()方法
  31. 4.1、是通过 MethodAccessor 进行调用的,而 MethodAccessor 是个接口,在第一次时调用 acquireMethodAccessor() 进行新创建。

3.1.2.1、源码详细分析

https://www.cnblogs.com/yougewe/p/10125073.html

3.1.3、执行流程图

Java-反射[Reflection]机制 - 图1

3.1.4、反射调用流程小结

  1. 1、反射类及反射方法的获取,都是通过从列表中搜寻查找匹配的方法,所以查找性能会随类的大小方法多少而变化;
  2. 1.1Method method = searchMethods(privateGetDeclaredMethods(false), nameParam, parameterTypes);
  3. 2、每个类都会有一个与之对应的Class实例,从而每个类都可以获取method反射方法,并作用到其他实例身上;
  4. 3、反射也是考虑了线程安全的,放心使用;
  5. 3.1、加载之前进行了加锁:synchronized (getClassLoadingLock(name)) {...}
  6. 4、反射使用软引用relectionData缓存class信息,避免每次重新从jvm获取带来的开销;
  7. 4.1ReflectionData<T> rd = reflectionData();
  8. 5、反射调用多次生成新代理Accessor, 而通过字节码生存的则考虑了卸载功能,所以会使用独立的类加载器;
  9. 5.1invoke时,是通过 MethodAccessor 进行调用的,而 MethodAccessor 是个接口,在第一次时调用 acquireMethodAccessor() 进行新创建。
  10. 5.2NativeMethodAccessorImpl / DelegatingMethodAccessorImpl
  11. class NativeMethodAccessorImpl extends MethodAccessorImpl {}
  12. class DelegatingMethodAccessorImpl extends MethodAccessorImpl {}
  13. 6、当找到需要的方法,都会copy一份出来,而不是使用原来的实例,从而保证数据隔离;
  14. 6.1getReflectionFactory().copyConstructor(constructor)
  15. 7、调度反射方法,最终是由jvm执行invoke0()执行;
  16. 7.1、调用时,调用 DelegatingMethodAccessorImpl.invoke();最后被委托到 NativeMethodAccessorImpl.invoke()。
  1. https://www.pdai.tech/md/java/basic/java-basic-x-reflection.html