ClassLoader与反射

ClassLoader

什么是类加载器

类加载器完成的是类加载机制中的类的加载的部分,完成从静态的 .class 文件生成 java.lang.class 对象(可以参照上一篇对 Java 类加载机制的学习

类加载器的种类

在 Java 中内置的 ClassLoader 有三种:Bootstrap ClassLoader, Extension ClassLoader, Application ClassLoader,另外还可以通过自定义 ClassLoader 来决定类的加载方式

  1. Bootstrap ClassLoader

    这是 Java 最基础的类加载器,负责加载 JVM 核心类,通常是我们常常看到的 java. 开头的内置库。这些类通常位于 $JAVA_HOME/lib/rt.jar 中, Bootstrap ClassLoader 为根加载器

  2. Extension ClassLoader

    Extension ClassLoader 加载的对象是 $JAVA_HOME/lib/ext/ 目录下的 jar 包中的类,加载得到的类通常是我们看到的 javax. 开头的扩展类,如 javax.swing.* 等,Extension ClassLoader 继承 Bootstrap ClassLoader,是 Bootstrap ClassLoader 的子类

  3. Application ClassLoader

    Application ClassLoader 是直接面向用户的加载器,Application ClassLoader 加载的对象是环境变量里面的路径的 jar 包或者路径中的 jar 包,还有我们自己编写的 java 文件以及第三方 jar 包。Application ClassLoader 继承 Extension ClassLoader,是Extension ClassLoader 的子类 。Application ClassLoader 也被成为系统加载器,可以通过 getSystemClassLoader 获取。

以上三个加载器都是在本地文件系统加载类,在 Java 中还有一种类加载器 Url ClassLoader,使用Url ClassLoader 可以加载网络上的 jar 包中的类。

  1. 自定义加载器

    定义一个加载器时,需要注意定义这几个方法:loadClass, findClass, defineClass。其中 loadClass 的作用是在当前类加载器中寻找这个类看这个类是否已被加载,如果未被加载的话,就调用父加载器进行加载,如果双亲都加载不了的话,就调用 findClass 由自定义加载器自己进行加载。这里需要注意 findClass 常常由自定义类加载器的子类自己定义。如果调用了 findClass 还是加载不了这个类的话,就会抛出异常。defineClass 的作用就是当类加载器完成 loadClass 和 findClass 的步骤时,将字节码对象转换成 class 对象,完成类的加载过程。

类加载器的运作

双亲委派

双亲委派是 Java 类加载器运作的重要原理。上面已经提到 Bootstrap ClassLoader 是根加载器,Extension ClassLoader 和 Application ClassLoader 都是 Bootstrap ClassLoader 子类,同时 Application ClassLoader 是 Extension ClassLoader 的子类,三者之间的父子关系为: Bootstrap ClassLoader -> Extension ClassLoader -> Application ClassLoader。

先以 Application ClassLoader 的运作过程作为例子:当 Application ClassLoader 去加载一个类的时候(由于含有 main() 的类会首先被 Application ClassLoader 初始化,所以很多时候 Java 文件遇到陌生的类时,会先用 Application ClassLoader 去加载,这是类加载器的传递性),会首先将加载类的任务交给 Extension ClassLoader,让 Extension ClassLoader去加载类,这时 Extension ClassLoader 同样的将加载的任务交给它的父亲 Bootstrap ClassLoader,Bootstrap ClassLoader 拿到任务就会去加载这个类,如果没找到这个类的话, Extension ClassLoader 就会自己去执行加载任务,同样的,如果 Extension ClassLoader 没找到这个类的话,Application ClassLoader 就会自己去执行类的加载任务。可以看到,类的加载的过程就像一个递归的过程。

p1

反射

类对象的获取

  1. class.forName(“xxx.xxx”)

    通过这种方式,我们可以通过类的全限定名直接获取类的 class 对象,利用起来比较容易

  2. className.class

    通过这种方式来获取类对象的话,需要当前文件引用了 className 这个类的库,依赖性较强

  3. new className().getClass()

    同样的这种方式只能在引用这个类的前提条件下使用,依赖性也比较强

    以上方法返回值均为类对象

反射的相关方法和使用

通过反射,我们可以获取到类对象中的属性对象和方法

  1. 获取属性对象

    1
    2
    3
    4
    getField(String name)	// 获取类对象的 name 属性(可以是父类的)
    getFields() // 获取类对象的所有属性(包括父类的)
    getDeclaredField(String name) // 获取当前类的 name 属性(不可以是父类的)
    getDeclaredFields() // 获取当前类的所有属性(不包括父类的)
  2. 获取方法

    1
    2
    3
    4
    getMethod(String name, parameters)		// 获取类对象中的 name 方法(可以是父类的)
    getMethods() // 获取类对象的所有方法(包括父类的)
    getDeclaredMethod(String name, parameters) // 获取类对象的 name 方法(不可以是父类的)
    getDeclaredMethods() // 获取类对象的所有方法(不包括父类的)

    需要注意的是使用 getMethod 和 getDeclaredMethod 来获取单个方法时,我们需要在第二个参数传入需要获取的方法的参数的 class 对象,比如:getMethod(add, int.class, int.class) 或是 getMethod(add, new Class[]{int.class, int.class}),到这里,我们还只可以获取方法对象(getMethod 返回值为方法对象),接下来我们可以通过 method 对象的 invoke 方法来执行方法。

    先来看看 invoke 函数的原型:

    1
    2
    invoke(Object obj,Object args[])
    两个参数均为对象,第一个参数为目标方法所在的类的对象(常常用newInstance来初始化),第二个参数为对象数组,为当前方法接受到的参数

    一个例子:

    1
    method.invoke(className.class.newInstance(), new Object[]{new Interger(11), new Interger(22)})

综合以上,得到一个完整的例子:

1
class.forName("xxx.className").getMethod("add", new Class[]{int.class, int.class}).invoke(class.forName("xxx.className").newInstance(), new Object[]{new Interger(11), new Interger(22)})
Contents
  1. 1. ClassLoader
    1. 1.1. 什么是类加载器
    2. 1.2. 类加载器的种类
    3. 1.3. 类加载器的运作
      1. 1.3.1. 双亲委派
  2. 2. 反射
    1. 2.1. 类对象的获取
    2. 2.2. 反射的相关方法和使用
|