类加载

Class类和Class对象

加载类完成后,堆内存的方法区产生Class类型的对象,Class类:描述类的类。

  1. Class.forName已知一个类的全类名,且该类在类路径下。

    1. Class<?> clazz = Class.forName("com.yanjing.reflection.User");
  2. 通过class属性获取

    1. Class clazz = Person.class
  3. 调用实例的getClass()方法

    1. Class clazz = person.getClass();
  4. 获取父类属性

    1. Class clazz = son.getSuperclass();
  5. 基本内置类型的的包装类

    1. Class clazz = Integer.TYPE;

    各种类型的Class对象

    image.png

    类加载过程

    类加载过程:加载——链接(验证、准备、解析)——初始化——使用——卸载
    反射和动态代理 - 图2
    image.png

    Object obj = new Object()发生了什么: 首先JVM会启动,你的代码会编译成一个.class文件,然后被类加载器加载进jvm的内存中,你的类Object加载到方法区中,创建了Object类的class对象到堆中,注意这个不是new出来的对象,而是类的类型对象,每个类只有一个class对象,作为方法区类的数据结构的接口。jvm创建对象前,会先检查类是否加载,寻找类对应的class对象,若加载好,则为你的对象分配内存,初始化也就是代码:new Object()。 作者:Goosy
    链接:https://www.zhihu.com/question/24304289/answer/147529485

更详细深入的类加载过程
image.png

反射

动态语言VS静态语言

运行时是否可改变程序结构(如变量类型)。

反射概念

Reflection(反射)允许程序在运行期间借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

API

  • getName
  • getSimpleName
  • getFields
  • getDeclareFields
  • getMethods
  • getDeclareMethods
  • getMethod
  • getDeclaredConstructor
  • getDeclaredConstructors
  • ……

    newInstance

    1. aClass.getDeclaredConstructor().newInstance()
    2. aClass.getDeclaredConstructor(Class<?>... parameterType).newInstance(Object ... initargs)

    getDeclareMethod+invoke (setAccessible)

    1. aClass.getDeclareMethod(String name, Class<?>...parameterTypes);
    2. // method.setAccessible(true);
    3. method.invoke(Object obj, Object[] args)

    field.get/set (setAccessible)

    1. this.getClass().getDeclaredField(String name);
    2. // filed.setAccessible(true);
    3. field.get(this);
    4. field.set(this, 'abc')

    获取泛型信息

    image.png

    获取注解信息

  • getAnnotation

  • getAnnotations

    应用

    对所有成员变量进行处理(判断)。

    1. private boolean hasValue() {
    2. Field[] fields = this.getClass().getDeclaredFields();
    3. return Arrays.stream(fields).anyMatch(field -> {
    4. field.setAccessible(true);
    5. try {
    6. return Objects.nonNull(field.get(this));
    7. } catch (IllegalAccessException ignore) {
    8. return false;
    9. }
    10. });
    11. }

    注:反射调用方式比普通方式慢几十倍,setAccessible可提高一些速度
    点击查看【bilibili】

    动态代理

  • [ ] Java 动态代理作用是什么?

    代理

  1. 控制访问
  2. 功能增强(日志AOP)

    静态代理

  • 定义接口类,目标类和代理类都实现
  • 原则:用组合,而不是继承
  • 建议Setter注入目标对象,而不是构造函数

    动态代理是什么和作用

    代理类的对象在程序运行时由JVM根据反射机制动态生成的。
  1. 减少代理类数量(
  • 一个动态代理 , 一般代理某一类业务
  • 一个动态代理可以代理多个类,只要这些类实现的是同一接口
  1. 修改接口中的方法不会影响实现类

    两种实现

    JDK动态代理

    使用java反射包中的类和接口实现动态代理的功能。
    反射包java.lang.reflect
    **InvocationHandler**, **Method**, **Proxy**

    CGLIB动态代理(了解)

    glib是第三方的工具库,创建代理对象

    1. cglib的原理是继承,cglib通过继承目标类,创建它的子类,在子类中重写父类中同名的方法,实现功能的修改。
    2. 因为cglib是继承,重写方法,所以要求目标类不能是fina1的,方法也不能是final的。cglib的要求目标类比较宽松,只要能继承就可以了。cglib在很多的框架中使用,比如mybatis,spring框架中都有使用。

实现过程

步骤和demo

  1. 创建接口,定义目标类要完成的功能
  2. 创建目标类实现接口
  3. 创建InvocationHandler接口的实现类,在invoke方法中完成代理类的功能
  • 调用目标方法
  • 增强功能
  1. 使用Proxy类的静态方法,创建代理对象,并把返回值转换成接口类型,然后通过代理执行方法。 ```java /**

    • InvocationHandler is the interface implemented by the invocation handler of a
    • proxy instance.Each proxy instance has an associated invocation handler.
    • When a method is invoked(float price = proxy.sell(1)) on a proxy instance,
    • the method invocation is encoded and dispatched to the invoke method of its
    • invocation handler. */ public class MySellHandler implements InvocationHandler {

      private final Object target;

      // 传入是谁的对象,就给谁创建代理 public MySellHandler(Object target) {

      this.target = target; }

    /**

    • 参数都是jdk直接传入
    • @param proxy 来自newProxyInstance创建的
    • @param method 来自proxy.method调用时的method
    • @param args 来自proxy.method调用时传入的参数
    • @return
    • @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

      // 调用目标方法 method.invoke(target, args);

      // 增强功能 // … } }

public class MainShop {

  1. public static void main(String[] args) {
  2. // 1. 创建目标对象
  3. UsbSell factory = new UsbKingFactory();
  4. // 2. 创建InvocationHandler对象
  5. InvocationHandler handler = new MySellHandler(factory);
  6. // 3. 创建代理对象
  7. UsbSell proxy = (UsbSell) Proxy.newProxyInstance(factory.getClass().getClassLoader(),
  8. factory.getClass().getInterfaces(),
  9. handler);
  10. // 4. 通过代理执行方法
  11. float price = proxy.sell(1);
  12. }

}

```

总结

  1. 为什么动态?是在运行期间创建代理对象。目标对象是活动的,根据需求实现一个具有增强功能的InvocationHandler,传入是谁的对象,就给谁创建代理。
  2. 代理对象通过Proxy.newProxyInstance创建,需要用到类加载器(任意一个类对象的getClassLoader()获取)和接口(target.getClass().getInterfaces())),这个前提就是要用到JDK反射(invoke方法内的method.invoke也用到反射),以及目标对象和代理对象都实现同一个接口。
  3. 运行期怎么动态代理可以通过debug方式
  4. 动态代理的是接口,注意类型要用接口,这样才是代理的实例,如果类型直接用实现,就是目标实例

image.png

点击查看【bilibili】