首页 > 编程语言 > 详细

java反射

时间:2020-07-24 15:05:48      阅读:62      评论:0      收藏:0      [点我收藏+]

写在前面

从上一篇注解的学习中发现,要想自定义的注解生效,必须添加元注解@Retention(RetentionPolicy.RUNTIME),注解会在class字节码文件中存在,jvm虚拟机在加载class文件后仍然存在,在运行时可以通过反射获取到,反射又是如何获取到想要的数据的呢?通过这个问题,就引出了下一个重要的知识点,java的高级特性:反射。在我前期泛泛而学的情况,现在越发觉得,接触到的java各种技术,各个知识点,都是环环相扣,在学习的时候循序渐进更能体会到其中的奥妙之处。

概念

Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。

应用场景

  • 逆向代码 ,例如反编译
  • 与注解相结合的框架
  • 动态生成类框架

反射的使用

这里参考知乎:bravo1988对如何理解反射的回答,部分摘抄和总结。
要想使用反射,首先要明白几个概念。

  1. 类加载
    jvm通过new或者反射创建实例,都离不开Class对象,Class对象是存在于方法区的唯一一个类对象。是通过类加载器加载字节码文件.class得到的,而类加载的过程大致分为三个步骤
  • 检查是否已经加载,有就直接返回,避免重复加载
  • 当前缓存中确实没有该类,那么遵循父优先加载机制,加载.class文件
  • 上面两步都失败了,调用findClass()方法加载
    其中,ClassLoader类是个抽象类,在前面两步都不能加载到类时,只能通过继承重写findClass()方法自定义加载过程逻辑实现类的加载。
  1. Class类
    当类加载完毕后,我们可以得到加载类的相关信息,主要包括以下内容:
  • 权限修饰符
  • 类名
  • 参数化类型(泛型信息)
  • 接口
  • 注解
  • 字段(重点)
  • 构造器(重点)
  • 方法(重点)

字段、方法、构造器对象等
技术分享图片
注解
技术分享图片
泛型
技术分享图片

Class类准备了很多字段用来表示一个.class文件的信息,对于字段、方法、构造器等,为了更详细地描述这些重要信息,还写了三个类,每个类里面都有很详细的对应。我们要搞清楚Class类与Method类、Field类等的关系。
获得类对象则有以下三种方式:

public class Clazz {
    /**
     * class获得方式
     *
     * @param args
     */
    public static void main(String[] args) {
        //1 通过类型获得
        // 语法:类名.class
        // 应用场景:确定类型 等
        Class clazz1 = Clazz.class;
        System.out.println("语法:类名.class|" + clazz1);

        //2 通过实例对象获得
        // 语法:变量.getClass()
        // 应用场景:在方法内部通过参数获得类型 等
        Clazz c = new Clazz();
        Class<? extends Clazz> aClass = c.getClass();
        System.out.println("语法:变量.getClass()|" + aClass);
        
        //3 通过字符串获得
        // 语法:Class.forName("全限定类名")
        // 应用场景:通过配置获得字符串 等
        try {
            Class<?> aClass1 = Class.forName("com.apang.clazz.Clazz");
            System.out.println("Class.forName(\"全限定类名\")|"+aClass1);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
>>>语法:类名.class|class com.apang.clazz.Clazz
>>>语法:变量.getClass()|class com.apang.clazz.Clazz
>>>Class.forName("全限定类名")|class com.apang.clazz.Clazz

在使用Class.forName创建对象的过程中,由于Class类的构造器是私有的,我们无法手动new一个Class对象,只能由JVM创建。JVM在构造Class对象时,需要传入一个类加载器,然后才有我们上面分析的一连串加载、创建过程。总而言之一句话就是要通过类加载器去完成。
而用newInstance()方式创建对象时,需要注意newInstance()底层就是调用无参构造对象的newInstance()。所以,本质上Class对象要想创建实例,其实都是通过构造器对象。如果没有空参构造对象,就无法使用clazz.newInstance(),必须要获取其他有参的构造对象然后调用构造对象的newInstance()。
3. 反射API
由于本人没具体使用过反射,对一些应用级别的东西还不太了解,不过根据大佬所述,在日常开发中,反射的目的主要有两个:一是对象的实例化,二是反射调用方法。其中有两个点需要注意:

  1. Class对象获取Method时,需要传入方法名+参数的Class类型,原因是:方法名相同的可能有多个,其次是不能传递变量名,只能传递基础数据类型的值或者是对象的引用。譬如,String.class, int.class是对象,且是Class对象。
  2. 调用method.invoke(obj, args);时需要传入一个目标对象,原因是:方法是一种行为描述,是所有该类对象共有的,被抽取出来放在了jvm中的方法区。JVM设置了一种隐性机制,每次对象调用方法时,都会隐性传递当前调用该方法的对象参数,方法可以根
    据这个对象参数知道当前调用本方法的是哪个对象,所以,在反射调用方法时,本质还是希望方法处理数据,所以必须告诉它执行哪个对象的数据。

通过以上,发现了解反射还必须清楚类加载机制和jvm内存模型,好像下一个需要学习的方向又确定了。

java反射

原文:https://www.cnblogs.com/apangne/p/13370079.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!