反射

反射

一、理解反射的概念,什么是反射

​ JAVA的反射机制能够让java成为一种准动态语言,即在运行时可以改变其结构的语言。

​ 刚刚学习,暂时理解为,在代码运行期间,能够通过代码中实例化的java对象去获得实例化这个对象的类的一个类对象。从这个类对象中包含了完整的类的结构信息,并且可以对其进行操作。

image-20210312211601684

二、反射机制提供的功能

​ 在运行时判断任意一个对象所属的类

​ 在运行时可以构造任意类的对象

​ 在运行时判断任意一个类具有的成员变量和方法

​ 在运行时获取泛型信息

​ 在运行时调用任意一个对象的成员变量和方法

​ 在运行时处理注解

​ 生成动态代理。。。

1、通过反射获得类对象

  • 通过类名获得类对象

    ​ 当知道一个类名的时候,可以通过类的名字获取类对象

    Class c1 = person.class;
    

    ​ person是一个类的名字。

    • 通过一个类的实例对象获取类对象

       Class c1 = hero.getClass;
      

      hero是一个类的实例化对象

      • 通过类的全名获得类对象

        Class c1 = Class.forName("com.hengyu.User");
        

        这里的com.hengyu.User是一个类的全名。

        • 内置包装类型也能有获得它的类对象

          Class c1= Integer.Type;
          

          其他的内置包装类型同理

​ 通过反射获得多个某一个类的类对象是同一个对象。

image-20210312213202427

三、类加载过程

### 	1、加载

​ 将class文件字节码内容加载到内存中去,并将这些静态数据转换为方法区的运行时的数据结构;然后生成一个代表这个类的Class对象。

2、链接

  • 验证

    确保加载的类信息符合JVM规范,没有安全方面问题

  • 准备

    正式为类变量(static)分配内存并设置类变量的初始值,这些内存在方法区中进行分配

  • 解析

    虚拟机中常量池内的符号引用(常量名)替换为直接引用(地址)的过程

3、初始化

  • 执行类构造器(),构造器会去构造类对象,并且将类变量的赋值动作和静态代码块中的语句进行合并。
  • 初始一个类对象时,如果其父类还没有进行初始化会先初始化符类。
  • 虚拟机会保证一个类的()方法在多线程环境中被正确加锁和同步。

四、类的初始化

​ 类只有在被主动引用的时候才会进行初始化。被动引用不会进行初始化。简单的理解是在访问一个类对象时如果所访问的属性需要初始化赋值的时候就会进行类的初始化。比如使用子类访问父类的静态变量,子类不会被初始化,而父类会被初始化。在引用常量的时候也不会进行父类的初始化,因为常量在链接阶段已经被放入了常量池当中。

五、类加载器

暂时还不懂。

六、获取运行时类的完整接口

  • 通过反射可以获得的运行时类的完整接口

    Filed 字段

    Method 方法

    Constructor 构造器

    Superclass 父类

    interface 接口

    Annotation 注解

  • 获得类的名字

    通过类对象可以获得类的简单名字和全名

    getName()方法获得类的全名即包括包名

    getSimpleName()方法获得类的简单名字

  • 获得类的属性

    getFileds()方法可以获取所有public修饰的属性,返回的是存放属性的数组

    getDeclaredFileds()方法可以获取所有属性,返回的是存放属性的数组

    可以通过参数获取指定的属性

获取其他结构可参照获取类的属性需要注意构造器的获取,当需要获取指定构造器时,getConstructor()方法中参数应该是数据结构类型的class如 String.class,Int.class

七、操作获取的类的结构

1、通过构造器获取对象

​ 通过c1.getConstructor()获取一个指定类的构造器之后(c1是类对象),可以通过newInstance获取一个该类的实例化对象。

User user1 = (User)constrector.newInstance;

2、通过反射调用并操作方法

​ 调用方法getDeclaredMethod(”方法名”,“方法中的参数类型.class”)

Method me = c1.getDeclaredMethod("setName",String.class);

得到方法之后,通过invoke()方法,去操作方法。

me.invoke(User,"hengyu");

上边的例子调用了c1类对象的setName()方法,给某个该类的实例化对象设置name。

invoke()方法中的参数应为(调用者参数);

八、反射获得泛型

通过反射可以获得方法中参数的泛型的具体数据类型

public class Test02 {

    public void method1(Map<String, Integer> map, List<String> list) {
        System.out.println(111);
    };

    public static void main(String[] args) throws NoSuchMethodException {
        Method method1 = Test02.class.getMethod("method1", Map.class, List.class);
        Type[] genericParameterTypes = method1.getGenericParameterTypes();
        for (Type genericParameterType : genericParameterTypes) {
            System.out.println("#"+genericParameterType);
            if (genericParameterType instanceof ParameterizedType){
                Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println("$"+actualTypeArgument);
                }
            }
        }
    };
};

通过方法的getGenericParameterTypes()方法获得方法的参数类型,得到一个数组,再判断得到的这个泛型的参数类型是否是结构化参数类型,如果是就将其强转出来并获得它的真实参数类型也就是这行代码所表达的;

 Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();

image-20210313092552425

image-20210313092638624

出自B站up:遇见狂神说

九、通过反射获取注解信息

​ 可以通过自定义注解,在自定义注解上使用元注解@Target,@Retention分别声明注解生效的范围和时间。将注解放在需要注解的类或方法或者属性上边去。

先获得类对象,如果注解在类上就通过类对象的getAnnotations获得注解数组,通过遍历这个数组可以查看在这个类上的注解。如果要获得某一个注解的值则需要指定的去获得某一个注解,通过类对象的getAnnotation()方法获取指定的注解,()里边的参数是注解名字的class类型,这样通过这个获取的注解就可以获取其中的值了。

​ 方法和属性上面的注解需要通过类对象获取方法和属性,然后操作同获取类对象注解的操作相同。

image-20210313102411074

posted @ 2021-03-13 13:55  恒宇ss  阅读(50)  评论(0)    收藏  举报