反射机制

使用new关键字实例化对象的过程

  1. 虚拟机遇到new指令之后,检查常量池中对应的符号引用的类是否被正确的加载、连接、初始化,如果没有,先进行加载
  2. 在堆中为对象分配内存,

    • 内存分配的方法有两种,分别是指针碰撞和空闲表,具体使用哪种主要取决于JVM是否有垃圾整理功能。如果没有则使用空闲表,如果有就是指针碰撞。
    • 创建过程中由于内存的原因存在线程安全的问题,为了解决线程安全的问题,主要也有两种方法,分别是CAS和TLAB。

      • CAS,分配内存时候的原子操作和失败重试机制
      • TLAB,在堆上事先分配好一小部分内存称之为TLAB,直到TLAB耗尽需要重新分配的时候才使用CAS。
  3. 对象分配的空间0值化
  4. 设置对象头信息

    1. Mark Word:对象本身运行时的数据:Hash码,偏向锁状态、GC分代年龄等,里面有一个小细节,GC进入年老代一般是15,是因为对象头中分配给记录GC分代年龄是4位。
    2. 类型指针:所属类的指针,指向所属类的元数据的指针。
在这里可以说一下对象的内存布局:

*   上述的对象头
    
*   实际数据
    
*   对齐填充(非必要)
    

设置完对象头之后,对于JVM一个对象就已经生成了,但是对于JAVA程序来说,对象的创建才刚刚开始,还需要执行<init>方法对字段进行复制。
  1. 将对象的引用返回

    程序通过栈里的引用对堆里的对象进行操作,具体有两种方法。

    1. 使用句柄,句柄中包含着指向实际对象的指针和实际对象所属类的指针,这么说的好处就是,堆中内存位置更改只需要更改句柄表就可以了,而不需要栈里的引用重新指向。
    2. 直接指针,直接指向实际对象,不需要二次寻址。

我们通过反编译,可以看出来,new指令执行的之后,是使用invokespecial,在常量池中要找到对应的类的直接引用,这是在编译期间已经确定的。

而是用反射,特别地,class.forName("xxxx") ,运行期件才会ldc xxxx,将类名从常量池推送至操作数栈顶

通过反射获得类的Class对象的方法:

  • Class clz = Class.forName("java.lang.String");这个方法默认是需要初始化的。因为它的参数中的

    boolean initialize默认是进行初始化,典型的数据库加载驱动,必须要执行static块,这个时候如果不i进行初始化是会失败的,这也是为什么选择forName的原因。

  • Classloder.loaderClass:默认只加载,但是可以选择进行链接。
  • Class clz = String.class;此时不会触发类的初始化,加载阶段就已经生成class对象。
  • String str = new String("Hello");
    Class clz = str.getClass();

实例化的方法:

  • class.newInstance():只能使用无参构造器
  • clz.getConstructor()..newInstance():可以指定构造器

在使用newInstance()的时候,需要确保类已经加载了,这和new是不一样的。

代理模式

代理模式是一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能

就比如商家只管生产商品,商品代理负责提供怎么卖,等其他额外功能。

静态代理

静态代理就是是聚合的方式,三要素:同接口、真实对象、代理对象

商家和代理实现实现同一个接口,但是代理商中包含了商家,重载的方法中必然也有商家的重载的方法,不过是把这个方法在重新包装一下。这种代理模式在编译器就已经确定好了,所以是静态代理。

缺点:

  • 假如代理都是做一样的事情,比如一个代理商既卖苹果也卖猪,而实际操作都是一样的。我们需要重新为橘子写代理类,这部分代码都是重复的。注意苹果实现的水果接口,猪实现的动物接口,这就麻烦了,但实际上这种完全解耦的情况是常见的。
  • 因为代理类和真实类实现的是同一个接口,所以如果接口的功能需要修改的话,代码维护工作也会增加。

动态代理

三要素:真实对象、中间对象实现InvocationHandler接口,代理对象

static Object   newProxyInstance(ClassLoader loader, //指定当前目标对象使用类加载器

Class<?>[] interfaces,   //目标对象实现的接口的类型
InvocationHandler h     //事件处理器
)

这个代理对象是在运行时生成的class文件,在生成的过程中

  1. 使用Proxy类的类加载器
  2. 真实对象的各种接口
  3. 代理的功能和真实的接口对接上。生成了一个参数是InvocationHandler的构造方法,在使用反射中构造器来实例化代理对象

说白了代理和真实对象实现了一个接口,真实对象传入到代理,实际上是把这个接口信息传递给代理,代理通过反射的方式在这个对这个对象所有方法包装一边,是的这个方法具有增强的功能。

Cglib代理

传统的jdk代理的限制也是比较明显的,即其需要被代理的对象必须实现一个接口。可以说它是为接口创建代理实例

这里如果被代理对象没有实现任何接口,或者被代理的业务方法没有相应的接口,我们则可以使用另一种方式来实现,即Cglib代理(Code Generation Library)

  • 创建Enhancer对象
  • 将真实对象设置为父类,并设置回调类对象,这个回调类对象要实现MethodInterceptor接口
  • Enhancer创建子类字节码,植入代理功能代码。

zygan
0 声望0 粉丝