静态代理:需要代理对象和目标对象实现一样的接口。
同一个接口,一个目标类实现,一个代理类实现,代理类除了目标类的方法还有别的增强方法
优点:可以在不修改目标对象的前提下扩展目标对象的功能。
缺点:1冗余。由于代理对象要实现与目标对象一致的接口,会产生过多的代理类。
2不易维护。一旦接口增加方法,目标对象与代理对象都要进行修改。
JDK动态代理:使用JDK的API动态地在内存中构建代理对象,从而实现对目标对象的代理功能.
一个代理类能代理多个类型(实现多个类)
静态代理编译时就已实现,是实际的class文件,而动态代理编译完没有实际的class文件,而是在运行时动态生产类字节码,并加载到JVM中
动态代理对象不需要实现接口,但是要求目标对象必须实现接口,否则不能使用动态代理。
JDK动态代理必须实现InvocationHandler接口,增强内容写invoke就行了
cglib动态代理:使用第三方代码生成类库,运行时在内存中动态生成一个子类对象从而实现对目标对象功能的扩展
代理没有接口的类的时候使用,使用cglib代理的对象则无需实现接口,达到代理类无侵入
应用的是底层的字节码增强的技术 生成当前类的子类对象
性能:
1静态代理在编译时产生class字节码文件,可以直接使用,效率高。
2动态代理必须实现InvocationHandler接口,通过反射代理方法,比较消耗系统性能,但可以减少代理类的数量,使用更灵活。
3cglib代理无需实现接口,通过生成类字节码实现代理,比反射稍快,不存在性能问题,但cglib会继承目标对象,需要重写方法,所以目标对象不能为final类。
动态代理和静态代理的比较
动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。而且动态代理的应用使我们的类职责更加单一,复用性更强
设计模式:
1单例设计模式:懒汉式,饿汉式
懒单例模式时注意线程安全问题
饿单例模式和懒单例模式构造方法都是私有的,因而是不能被继承的
2工厂模式:
普通工厂:输入类型,获得类型的实例
多个工厂方法模式:提供多个工厂方法,创建对象调用不同的方法
静态工厂方法:将上面工厂方法模式里面的方法置为静态,不用创建实例,直接调用即可
抽象工厂方法:之前几种依赖工厂类,如果修改要改源码,违反闭包的原则,解决方法:创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。
3建造者模式:
工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性
4适配器设计模式
适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式
通过引入一个适配器类来重用现有的适配者类,无需修改原有结构
5装饰模式
装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例
(代理有对象控制权,装饰只能加强功能)
6策略模式
策略模式的决定权在用户,系统本身提供不同算法的实现,新增或者删除算法,对各种算法做封装。因此,策略模式多用在算法决策系统中,外部用户只需要决定用哪个算法即可
7观察者模式
当一个对象变化时,其它依赖该对象的对象都会收到通知,并且随着变化!对象之间是一种一对多的关系
JVM垃圾回收机制:主要进行回收的内存是 JVM 中的方法区和堆
搜索算法:根搜索算法“GC Roots”对象作为起点,从这些节点开始往下搜索,搜索通过的路径成为引用链
(Reference Chain),当一个对象没有被 GC Roots 的引用链连接的时候,说明这个对象是不可用的
而在实际开发中,可能会存在无用但可达的对象,这些对象不能被 GC 回收,因此也会导致内存泄
露的发生。
回收算法:
1标记—清除算法。标记—清除算法是基础的收集算法,标记和清除阶段的效率不高,
而且清除后回产生大量的不连续空间,这样当程序需要分配大内存对象时,可能无法找到足够的连续空间。
2复制算法.是把内存分成大小相等的两块,每次使用其中一块,当垃圾回收的时候,把存活的对象复制到另一块上,然后把这块内存整个清理掉。复制算法实现简单,运行效率高,但是由于每次只能使用其中的一半,造成内存的利用率不高。现在的 JVM 用复制方法收集新生代,由于新生代中大部分对象(98%)都是朝生夕死的,所以两块内存的比例不是 1:1(大概是 8:1)
3标记整理算法.标记—整理算法和标记—清除算法一样,但是标记—整理算法不是把存活对象复制到另一块内存,而是把存活对象往内存的一端移动,然后直接回收边界以外的内存。标记—整理算法提高了内存的利用率,并且它适合在收集对象存活时间较长的老年代
4分代收集.是根据对象的存活时间把内存分为新生代和老年代,根据各个代对象的存活特点,每个代采用不同的垃圾回收算法。新生代采用复制算法,老年代采用标记—整理算法。垃圾算法的实现涉及大量的程序细节,而且不同的虚拟机平台实现的方法也各不相同
引用类型:
1强引用
引用存在就不会回收
2软引用
如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。
3弱引用
弱引用主要用于监控对象是否已经被垃圾回收器标记为即将回收的垃圾
那该类就是可有可无的对象,因为只要该对象被 gc 扫描到了随时都会把它干掉
4虚引用
虚引用主要用于检测对象是否已经从内存中删除。在任何时候都可能被垃圾回收。虚引用主要用来跟踪对象被垃圾回收的活动。
堆,栈
stack:栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在 WINDOWS 下,栈的大小是 2M(也有的说是 1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示 overflow。因此,能从栈获得的空间较小。
heap:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,
自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,
堆获得的空间比较灵活,也比较大
java虚拟机的内存结构
堆区:
1.存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令)
2.jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身
栈区:
1.每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中
2.每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。
3.栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。
方法区:
1.又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。
2.方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。
类加载器:
1、根类加载器(Bootstrap) --C++写的 ,看不到源码
2、扩展类加载器(Extension) 加载位置 :jre\lib\ext
3、系统(应用)类加载器(AppClassLoader:System\App) --加载位置 :classpath 中
4、自定义加载器(CustomClassLoader:必须继承 ClassLoader)
双亲委托机制:
4委托到3委托到2委托到1,1没办法加载,再2加载,2没办法加载再3加载...
在该机制中,JVM 自带的 Bootstrap 是根加载器,其他的加载器都有且仅有一个父类加载器。当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,每一个层次类加载都是如此,因此所有的加载请求都应该传送到启动类加载器中,只有当父类加载器反馈自己无法完成这个请求的时候(在它的加载路径里找不到这个所需要加载的类),子类加载器才会尝试自己去加载