0
点赞
收藏
分享

微信扫一扫

浅谈java反射

sunflower821 2022-05-06 阅读 89

前言:说java反射之前先了解类加载。


类加载

加载.class文件大致可以分为3个步骤:

  • 检查是否已经加载,有就直接返回,避免重复加载
  • 当前缓存中确实没有该类,那么遵循父优先加载机制,加载.class文件
  • 上面两步都失败了,调用findClass()方法加载

如果我们要定义类加载器,需要继承ClassLoader类,并覆盖findClass()方法。
在这里插入图片描述



有哪几种类加载器

在这里插入图片描述

  • BootstrapClassLoader:启动类类加载器,它用来加载<JAVA_HOME>/jre/lib路径,-Xbootclasspath参数指定的路径以<JAVA_HOME>/jre/classes中的类。BootStrapClassLoader是由c++实现的。
  • ExtClassLoader:拓展类类加载器,它用来加载<JAVA_HOME>/jre/lib/ext路径以及java.ext.dirs系统变量指定的类路径下的类。
  • AppClassLoader:应用程序类类加载器,它主要加载应用程序ClassPath下的类(包含jar包中的类)。它是java应用程序默认的类加载器。
  • 用户自定义类加载器:用户根据自定义需求,自由的定制加载的逻辑,继承AppClassLoader,仅仅覆盖findClass()即将继续遵守双亲委派模型。
  • *ThreadContextClassLoader:线程上下文加载器,它不是一个新的类型,更像一个类加载器的角色,ThreadContextClassLoader可以是上述类加载器的任意一种,但往往是AppClassLoader,作用我们后面再说。

在虚拟机启动的时候会初始化BootstrapClassLoader,然后在Launcher类中去加载ExtClassLoader、AppClassLoader,并将AppClassLoader的parent设置为ExtClassLoader,并设置线程上下文类加载器。

Launcher是JRE中用于启动程序入口main()的类,让我们看下Launcher的代码:

public Launcher() {
        Launcher.ExtClassLoader var1;
        try {
            //加载扩展类类加载器
            var1 = Launcher.ExtClassLoader.getExtClassLoader();
        } catch (IOException var10) {
            throw new InternalError("Could not create extension class loader", var10);
        }

        try {
            //加载应用程序类加载器,并设置parent为extClassLoader
            this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
        } catch (IOException var9) {
            throw new InternalError("Could not create application class loader", var9);
        }
        //设置默认的线程上下文类加载器为AppClassLoader
        Thread.currentThread().setContextClassLoader(this.loader);
        //此处删除无关代码。。。
        }
  • ExtClassLoader为什么没有设置parent?
    因为BootstrapClassLoader是由c++实现的,所以并不存在一个Java的类,因此会打印出null,所以在ClassLoader中,null就代表了BootStrapClassLoader。
  • 那么双亲委派的好处是什么呢?
    答:双亲委派模型能保证基础类仅加载一次,不会让jvm中存在重名的类。比如String.class,每次加载都委托给父加载器,最终都是BootstrapClassLoader,都保证java核心类都是BootstrapClassLoader加载的,保证了java的安全与稳定性。
  • 那自己怎么去实现一个ClassLoader呢?
    自己实现ClassLoader时只需要继承ClassLoader类,然后覆盖findClass(String name)方法即可完成一个带有双亲委派模型的类加载器。
  • 为什么不继承AppClassLoader呢?
    因为它和ExtClassLoader都是Launcher的静态类,都是包访问路径权限的。


Class类

现在,.class文件已经被类加载器加载到内存中,并且JVM根据其字节数组创建了对应的Class对象。

接下来,我们来研究一下Class对象,我们将在这一小节一步步分析Class类的结构。

类至少包括以下信息(按顺序):

  • 权限修饰符
  • 类名
  • 参数化类型(泛型信息)
  • 接口
  • 注解
  • 字段(重点)
  • 构造器(重点)
  • 方法(重点)

类中所有信息,都被“解构”后保存在Class类中。针对字段、方法、构造器,因为信息量太大,JDK单独写了三个类:Field、Method、Constructor。

大概了解完Class类的字段后,我们来看看Class类的方法。

  • 构造器
    在这里插入图片描述
    可以发现,Class类的构造器是私有的,我们无法手动new一个Class对象,只能由JVM创建。JVM在构造Class对象时,需要传入一个类加载器,然后才有我们上面分析的一连串加载、创建过程。
  • Class.forName()方法
    在这里插入图片描述
    反正还是类加载器去搞。
  • newInstance()
    在这里插入图片描述
    也就是说,newInstance()底层就是调用无参构造对象的newInstance()。

所以,本质上Class对象要想创建实例,其实都是通过构造器对象。如果没有空参构造对象,就无法使用clazz.newInstance(),必须要获取其他有参的构造对象然后调用构造对象的newInstance()。



反射API

以上解释完后没啥好说的,在日常开发中反射最终目的主要两个:

  • 创建实例
  • 反射调用方法

运用的就是Field、Method、Constructor的一系列操作,在此也不再做过多叙述。

举报

相关推荐

0 条评论