[笔记] Java基础(12)-反射与注解

164 阅读4分钟

1. 类加载机制

  • 类初始化步骤:加载->连接->初始化
    • 加载:将类的class文件读入内存,并创建一个class对象,任何类使用时都会创建一个class对象
    • 连接:又分为验证、准备、解析三步
      • 验证:验证内部结构是否正确,并与其它类协调一致
      • 准备:为类的静态成员分配内存,并设置默认初始化值
      • 解析:将类的二进制数据中的符号引用替换为直接引用
    • 初始化:默认初始化与现时初始化
  • 类加载机制:全盘委托与双亲委派
    • 全盘委托:类加载器加载类时也会同时加载类中引用的其它类
    • 双亲委派:如果子类加载器没有加载过这个类,就会委托给父类加载器加载,如果父类加载不到,则从自己的路径下加载
  • 类加载时机:创建类的实例、访问类的静态变量、调用类的静态方法、使用反射方式强制创建类的class对象、初始化类的子类、使用java命令运行某个主类
  • 类加载器:分为根类加载器、扩展类加载器、系统类加载器
    • 根类加载器Bootstrap ClassLoader:也称为引导类加载器,负责java核心类的加载,如:SystemString类;位于/lib目录下的rt.jar文件中
    • 扩展类加载器Extension ClassLoader:负责JRE扩展目录中jar包的加载;位于/lib目录下的/ext目录
    • 系统类加载器System ClassLoader:负责在JVM启动时加载来自java命令的class文件以及classpath环境变量所指定的jar包和类路径,即第三方jar包或类

2. 反射机制

  • 反射:通过Class对象动态获取信息以及动态调用方法的功能
  • 实现方式
    • Class.forName("FullClassName"):源文件阶段;典型应用:读取配置文件获取类
    • ClassName.class:字节码阶段;典型应用:当作锁对象
    • obj.getClass():对象阶段,即运行阶段;典型应用:判断是否为同一个字节码对象

3. Class类

  • Class类:java.lang.Class
  • static Class<?> forName(String className):根据全类名获取Class对象
  • Construct<T> getConstructor(Class<?> ...parameterTypes):获取制定的公共构造方法
  • Constract<T>[] getConstructors():获取所有公共构造方法的数组
  • Field getField(String name):获取指定的公共类成员变量
  • Field[] getFields():获取所有公共类成员变量的数组
  • Method getMethod(String name, Class<?> ...parameterTypes):获取指定的公共成员方法
  • Method[] getMethod():获取所有公共成员方法的数组
  • T newInstance():使用无参构造创建类的实例对象
  • boolean isEnum():类声明为Enum时返回true
  • Constructor<T> getDeclaredConstructor(Class<?> ...parameterTypes):获取指定的构造方法,可获取私有的构造方法
  • Constructor<T> getDeclaredConstructors():获取所有构造方法的数组
  • Field getDeclaredField(String name):获取指定的成员变量,可获取私有成员变量
  • Field[] getDeclaredFields():获取所有成员变量的数组
  • Method getDeclaredMethod(String name, Class<?> ...parameterTypes):获取指定的成员方法,可以获取私有的成员方法
  • Method[] getDeclaredMethods():获取所有成员方法的数组
  • MethodFieldConstructor对象都可以通过setAccessible(boolean flag)方法设置可访问性
  • Method
    • invoke(Object obj, Object ...args):调用obj对象的方法
  • Field
    • get(Object obj):获取obj对象的字段值
    • set(Object obj, Object value):设置obj对象的字段值为value
  • Constructor
    • newInstance(Object ... initargs):创建对象
  • 泛型检查只在编译期有效,运行期会被擦除,故可以通过反射越过泛型检查

4. 动态代理

  • 动态代理:在程序运行中产生代理对象;基于反射机制;Java内置的代理只能针对接口

  • JDK代理:使用java.lang.reflect.Proxy类与InvacationHandler接口实现

    /* 创建被代理对象 */
    final Object obj = new Object();
    /* 创建代理对象 */
    Object proxy = Proxy.newProxyInstance(obj.getClass(), obj.getClass.getInterfaces(), new InvocationHandler() {
        /* 通过代理对象调用时最终会执行invoke方法 */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            /* 调用被代理对象方法前执行语句 */
            System.out.println("check...");
            /* 调用被代理对象的方法 */
            Object invoke = method.invoke(obj, args);
            /* 调用被代理对象方法后执行语句 */
            System.out.println("log...");
            /* 返回执行结果 */
            return invoke;
        }
    });
    /* 通过代理对象调用指定的方法 */
    proxy.equals();
    
  • Cglib代理:使用net.sf.cglib.proxy.Enhancer类与MethodInterceptor接口实现

    /* 创建被代理对象 */
    final Object obj = new Object();
    /* 创建Enhancer对象 */
    Enhancer enhancer = new Enhancer();
    /* 设置被代理的类 */
    enhancer.setSuperclass(obj.getClass());
    /* 设置回调方法 */
    enhancer.setCallback(new MethodInterceptor() {
        /* 通过代理对象调用时最终会执行intercept方法 */
        @Override
        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            /* 调用被代理对象方法前执行语句 */
            System.out.println("check...");
            /* 调用被代理对象的方法 */
            Object invoke = method.invoke(obj, args);
            /* 调用被代理对象方法后执行语句 */
            System.out.println("log...");
            /* 返回执行结果 */
            return invoke;
        }
    });
    /* 创建代理对象 */
    Object proxy = (Object) enhancer.create();
    /* 通过代理对象调用指定的方法 */
    proxy.equals();
    

5. 泛型反射

  • 泛型反射:从参数化数组中获取实际类型参数

    • ArrayList<Integer>:整体为参数化类型,Integer为实际类型参数
    • ArrayList<E>E为类型通用参数变量,<>表示typeof
  • public Type[] getGenericInterfaces():获取带有泛型的接口类型

  • public Type getGenericSuperclass():获取带有泛型的父类类型

  • 获取父类泛型示例;该代码位于父类的构造方法中

    /* 获取子类字节码对象 */
    Class c = this.getClass();
    /* 获取带有泛型的父类 */
    ParameterizedType type = (Parameterized) c.getGenericSuperclass();
    /* 获取泛型的Class对象 */
    Class clazz = (Class) type.getActualTypeArguments()[0];