类的生命周期

  • 同一个 JVM 中的所有线程、所有变量都处于同一个进程里
  • 类的加载:将类的 class 文件(二进制数据)读入内存,并为之创建一个 java.lang.Class 对象(由类加载器完成)

  • 类的连接:把类的二进制数据合并到 JRE 中包括:验证、准备(为类的类变量分配内存并设置默认初始值)、解析

  • 类的初始化:对类变量进行初始化包括:声明类变量时指定初始值、使用静态初始化块为类变量指定初始值(执行顺序与它们在源代码中的排列顺序相同)


    图 1 类初始化过程

类加载器


图 2 类加载器的层级关系

  • 启动类加载器(Bootstrap ClassLoader):该加载器是用 C 语言实现的,负责加载存放在 $JAVA_HOME\jre\lib下,或被 -Xbootclasspath 参数指定的路径中的,并且能被虚拟机识别的类库(如 rt.jar,所有的 java. 开头的类均被 Bootstrap ClassLoader 加载),启动类加载器是无法被 Java 程序直接引用的
  • 扩展类加载器(Extension ClassLoader):该加载器由 sun.misc.Launcher$ExtClassLoader 实现,它负责加载 $JAVA_HOME\jre\lib\ext 目录中,或者由 java.ext.dirs 系统变量指定的路径中的所有类库(如 javax.开头的类),开发者可以直接使用扩展类加载器
  • 应用程序类加载器(Application ClassLoader):该类加载器由 sun.misc.Launcher$AppClassLoader 来实现,它负责加载用户类路径(ClassPath)所指定的类,开发者可以直接使用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器

如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的加载器都是如此,因此所有的类加载请求都会传给顶层的启动类加载器,只有当父载器反馈自己无法完成该加载请求(该加载器的搜索范围中没有找到对应的类)时,子加载器才会尝试自己去加载

通过反射查看类信息

  • 反射:在运行时期,动态地去获取一个类中的信息(类的信息、构造器信息、方法信息、字段等信息)

获得 Class 对象

  • 在 JVM 中,一个类用其全限定类名其类加载器作为其唯一标识
  • 所有的具有相同的维数和相同元素类型的数组共享同一个 Class 对象

  • 调用某个类的 class 属性来获取该类对应的 Class 对象(所有的数据类型都有 class 属性,包括数组、基本类型以及 void

  • 调用某个对象的 Class<?> getClass() 方法(可能需要强转)

从 Class 中获取信息

  • 获取 Class 对应类所包含的构造器(由 Constructor 对象表示)Constructor<T> getConstructor(Class<?>… parameterTypes):获取此 Class 对象对应类的、带指定形参列表public 构造器Constructor<T> getDeclaredConstructor(Class<?>.. parameterTypes):获取此 Class 对象对应类的、带指定形参列表的构造器,与访问权限无关Constructor<?>[] getConstructors():获取此 Class 对象对应类的所有 public 构造器Constructor<?>[] getDeclaredConstructors():获取此 Class 对象对应类的所有构造器,与访问权限无关
  • 获取 Class 对应类所包含的方法(由 Method 对象表示)Method getMethod(String name, Class<?>.. parameterTypes):获取此 Class 对象对应类的、带指定形参列表的 public 方法(包括继承的方法)Method getDeclaredMethod(String name, Class<?>.. parameterTypes):获取此 Class 对象对应类的、带指定形参列表的方法,与访问权限无关(不包括继承的方法)Method[] getMethods():获取此 Class 对象所表示的类的所有 public 方法(包括继承的方法)Method[] getDeclaredMethods():获取此 Class 对象对应类的全部方法,与访问权限无关(不包括继承的方法)
  • 访问 Class 对应类所包含的字符(由 Field 对象表示)Field getField(String name):获取此 Class 对象对应类的、指定名称的 public 成员变量(包括继承的字段)Field getDeclaredField(String name):获取此 Class 对象对应类的、指定名称的成员变量,与成员变量的访问权限无关(不包括继承的字符)Field[] getFields():返回此 Class 对象对应类的所有 public 成员变量(包括继承的字符)Field[] getDeclaredFields():获取此 Class 对象对应类的全部成员变量,与成员变量的访问权限无关(不包括继承的字符)
  • 其它实例方法ClassLoader getClassLoader():获取该类的类加载器Class<?>[] getInterfaces():获取此 Class 对象所表示的类或接口实现的接口:获取该 Class 对象对应类的超类的 Class 对象int getModifiers():获取此类或接口的所有修饰符(返回的整数应使用 Modifier 工具类的方法来解码)Package getPackage():获取此类的包String getName():以字符串形式返回此 Class 对象所表示的类的名称String getSimpleName():以字符串形式返回此 Class 对象所表示的类的简称Class<?> getComponentType():返回表示数组元素类型的 ClassisArray()isEnum()isInterface()isInstance(Object obj)isAssignableFrom(Class<?> cls)

使用反射生成并操作对象

  • Constructor、Method、Field 都在 java.lang.reflect 包下,直接父类都是 AccessibleObject

  • 如果反射的对象需要调用对应类中的 private 修饰的构造器、方法或成员变量,应调用 Constructor 对象、Method 对象或 Field 对象的如下方法:void setAccessible(boolean flag):设置此对象的 accessible 标志为指定的布尔值(值为 true 时,指示反射的对象在使用时取消访问权限检查

  • 先检查要调用的构造器或方法是否有 public 修饰,再检查是否有 private 修饰

  • 方式 1:使用 Class 对象的 T newInstance()方法来创建该 Class 对象对应类的实例,要求该 Class对象的对应类有无参数构造器

  • 方式 2:先使用 Class 对象获取指定的构造器 Constructor 对象,再调用 Constructor 对象的T newInstance(Object… initargs)方法来创建该 Class 对象对应类的实例

调用方法

  • 先使用 Class 对象获取指定的方法 Method 对象,再调用 Method 对象的 invoke() 方法Object invoke(Object obj, Object…args):该方法中的 obj 是执行该方法的对象,后面的 args 是执行该方法时传入该方法的实参

  • 如果调用是数组型参数(形参个数可变)的方法,把实际参数作为 Object[] 的元素再传递,即:Object invoke(执行该方法的对象, new Object[]{ 所有实参 })

  • 返回值的类型是 Object ,可能需要强转

访问成员变量值

  • Field 提供了如下两组方法来读取或设置成员变量值:Object get(Object obj):获取 obj 对象上此 Field 表示的字段的值xxx getXxx(Object obj):获取 obj 对象的该成员变量的值(此处的 Xxx 对应 8 种基本类型,如果该成员变量的类型是引用类型,则取消 get 后面的 Xxx)void setXxx(Object obj, Xxx val):将 obj 对象的该成员变量设置成 val 值(此处的 Xxx 对应 8 种基本类型,如果该成员变量的类型是引用类型,则取消 set 后面的 Xxx)
  • 在 java. lang. reflect 包下的 Array 类中的类方法:Object newInstance(Class<?>componentType,int…length):创建一个具有指定的元素类型、指定维度的新数组int getLength(Object array):以 int 形式返回指定数组对象的长度xxx getXxx(Object array, int index):返回 array 数组中第 index 个元素:将 array 数组中第 index 个元素的值设为 val

  • 其中 xxx 是各种基本数据类型,如果数组元素是引用类型,则方法变成get(ObjeCt array, int index)set(Object array, int index, Object val)

使用反射加载资源文件

  • Class 类中的 InputStream getResourceAsStream(String name):name 不以 '/' 开头时默认是从当前类的字节码所在的路径去加载资源,以 '/' 开头则是从 classpath 的根路径去加载资源
  • ClassLoader 类中的 InputStream getResourceAsStream(String name):从 classpath 的根路径去加载资源,返回所读取资源的输入流,name 不能以 '/' 开头

  • 获取 ClassLoader 对象的方法

    • Class 类中的 ClassLoader getClassLoader():返回该类的类加载器
    • Thread 类中的 ClassLoader getContextClassLoader():返回该线程的上下文 ClassLoader

使用反射生成 JDK 动态代理

  • 特点:
    • 代理的对象必须有实现的接口
    • 需要为每个对象创建代理对象
    • 动态代理的最小单位是类(所有类中的方法都会被处理)

Proxy 类

  • 构造器:protected Proxy(InvocationHandler h)

    • Class<?> getProxyClass(ClassLoader loader, Class<?>… interfaces):创建一个动态代理类所对应的 Class 对象,该代理类将实现 interfaces 所指定的多个接口(第一个 ClassLoader 参数指定生成动态代理类的类加载器)
    • Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h):使用指定的 InvocationHandler 创建一个动态代理对象,该代理对象的实现类实现了 interfaces 指定的系列接口,执行代理对象的每个方法时都会被替换执行 InvocationHandler 对象的 invoke 方法

InvocationHandler 接口

  • 执行动态代理对象的所有方法时,都会被替换成执行 invoke 方法 Object invoke(Object proxy, Method method, Object[] args)
    • proxy:代表动态代理对象
    • method:代表正在执行的方法
    • args:代表调用目标方法时传入的实参

cglib 动态代理

  • 原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对 final 标注的类进行代理
  1. // 自定义的 InvocationHandler 需实现 org.springframework.cglib.proxy.InvocationHandler
  2. // 获取 cglib 动态代理对象的方法
  3. public <T> T getProxyObject() {
  4. MyInvokationHandler handler = new MyInvokationHandler();
  5. handler.setTarget(target);
  6. Enhancer enhancer = new Enhancer();
  7. enhancer.setSuperclass(target.getClass()); // 设置代理对象需要继承的类
  8. enhancer.setCallback(handler); // 设置代理对象需要回调的方法
  9. return(T) enhancer.create(); // 创建一个代理对象
  10. }