反射机制
使用new关键字实例化对象的过程
- 虚拟机遇到new指令之后,检查常量池中对应的符号引用的类是否被正确的加载、连接、初始化,如果没有,先进行加载
-
在堆中为对象分配内存,
- 内存分配的方法有两种,分别是指针碰撞和空闲表,具体使用哪种主要取决于JVM是否有垃圾整理功能。如果没有则使用空闲表,如果有就是指针碰撞。
-
创建过程中由于内存的原因存在线程安全的问题,为了解决线程安全的问题,主要也有两种方法,分别是CAS和TLAB。
- CAS,分配内存时候的原子操作和失败重试机制
- TLAB,在堆上事先分配好一小部分内存称之为TLAB,直到TLAB耗尽需要重新分配的时候才使用CAS。
- 对象分配的空间0值化
-
设置对象头信息
- Mark Word:对象本身运行时的数据:Hash码,偏向锁状态、GC分代年龄等,里面有一个小细节,GC进入年老代一般是15,是因为对象头中分配给记录GC分代年龄是4位。
- 类型指针:所属类的指针,指向所属类的元数据的指针。
在这里可以说一下对象的内存布局:
* 上述的对象头
* 实际数据
* 对齐填充(非必要)
设置完对象头之后,对于JVM一个对象就已经生成了,但是对于JAVA程序来说,对象的创建才刚刚开始,还需要执行<init>方法对字段进行复制。
-
将对象的引用返回
程序通过栈里的引用对堆里的对象进行操作,具体有两种方法。
- 使用句柄,句柄中包含着指向实际对象的指针和实际对象所属类的指针,这么说的好处就是,堆中内存位置更改只需要更改句柄表就可以了,而不需要栈里的引用重新指向。
- 直接指针,直接指向实际对象,不需要二次寻址。
我们通过反编译,可以看出来,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文件,在生成的过程中
- 使用Proxy类的类加载器
- 真实对象的各种接口
- 代理的功能和真实的接口对接上。生成了一个参数是InvocationHandler的构造方法,在使用反射中构造器来实例化代理对象
说白了代理和真实对象实现了一个接口,真实对象传入到代理,实际上是把这个接口信息传递给代理,代理通过反射的方式在这个对这个对象所有方法包装一边,是的这个方法具有增强的功能。
Cglib代理
传统的jdk代理的限制也是比较明显的,即其需要被代理的对象必须实现一个接口。可以说它是为接口创建代理实例
这里如果被代理对象没有实现任何接口,或者被代理的业务方法没有相应的接口,我们则可以使用另一种方式来实现,即Cglib代理(Code Generation Library)
- 创建Enhancer对象
- 将真实对象设置为父类,并设置回调类对象,这个回调类对象要实现MethodInterceptor接口
- Enhancer创建子类字节码,植入代理功能代码。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。