目录
1. 六大设计原则
一句话总结个原则特点
- 单一职责原则 :一个类只负责一项职责
- 里式替换原则:所有应用父类的地方都能够使用子类替代,子类可进行功能扩展而不是重写、覆盖父类方法
- 依赖倒置原则:高层模块不应该依赖低层模块,二者都应该依赖抽象,抽象不应该依赖细节,细节应该依赖抽象,Java中,抽象是指接口或者抽象类,细节就是具体的实现类,使用接口或者抽象类的目的是制定好规范,而不设计具体的操作,把具体的细节交给实现类去完成。
- 接口隔离原则:建立单一的接口,不要尽力庞大臃肿的接口,尽量细化接口,接口中的方法尽量的少,即为各个类分别建立专用的接口
- 迪米特原则:尽量降低类与类直接的耦合度
- 开闭原则:一个类、模块或者函数应该对外扩展开发,对修改关闭,用抽象构建框架,有实现扩展细节
2. 什么是单例模式?
在程序的生命周期中仅有一个实例即单例模式
3. 单例模式的特点?
- 只有一个实例
- 构造方法是私有的
- 单例必须自己创建自己的唯一实例
- 单例类必须向其他对象提供这一实例
4. 单例模式VS静态类
- 单例可以继承和被继承,解决方法可以被重写,静态方法不可以
- 静态方法中产生的对象在执行完后被释放,进而被GC清理,不会一直在内存中
- 静态类会在第一次运行的时候被初始化,单例模式可以进行延迟加载
- 单例模式一般是一些重量级的任务,如果经常初始化和释放会占用大量资源,使用到单例模式可以将其常驻内存节约资源,比如Sqlite数据库的连接等等
- 静态方法访问效率更高
5. 单例模式的几种实现方式
1. 懒汉式
public class LazyInstance {
private static LazyInstance instance;
private LazyInstance() {
}
public static LazyInstance getInstance() {
if (instance == null) {
instance = new LazyInstance();
}
return instance;
}
}
根据代码可以看出是具有延迟加载的特性,但是非线程安全
2. 饿汉式
public class HungryInstance {
private static HungryInstance instance = new HungryInstance();
private HungryInstance(){}
public static HungryInstance getInstance() {
return instance;
}
}
线程安全,但是不具有延迟加载的特性,造成资源浪费
3. DCL(double-check-locking)双检锁
public class DoubleCheckLockInstance {
private DoubleCheckLockInstance() {
}
private volatile static DoubleCheckLockInstance instance;
public static DoubleCheckLockInstance getInstance() {
if (instance == null) {
synchronized (DoubleCheckLockInstance.class) {
if (instance == null) {
return new DoubleCheckLockInstance();
}
}
}
return instance;
}
}
将饿汉式与懒汉式的优点相结合,synchronized内外都加判断,一是为了不必要的同步导致效率问题,二是保证了线程安全
4. 静态内部类(推荐)
public class Singleton {
private Singleton() { }
public static Singleton getInstance() {
return SingletonHolder.instance;
}
private static class SingletonHolder {
private static final Singleton instance = new Singleton();
}
}
第一次加载Singleton时并不会初始化instance,而是在调用getInstance()的时候虚拟机会去加载SingletonHolder类,不仅保证了线程安全,也保证了单例对象的唯一性,同时也延迟了单例的实例化,所以推荐使用
5. 枚举单例
public enum SingletonEnum {
INSTANCE;
}
简单,而且反序列化后也是单例,其他方式反序列化后会生成多个对象
6. DCL原理
第一层判断是为了避免不必要的同步,第二层判断就比较复杂了:假设线程A执行了instance=DoubleSyncCheckSingleDemo()语句,这个操作其实不非一个原子操作,最终会被编译成多条汇编指令,大致做了3件事情:
- 给 DoubleSyncCheckSingleDemo的实例分配内存
- 调用 DoubleSyncCheckSingleDemo()的构造函数,初始化成员字段
- 将instance对象指向分配的内存空间(此时instance就不是null了)
但是由于Java编译器允许处理器乱序执行,JDK1.5之前JMM(Java Memory Model 即java内存模型)中Cache、寄存器到主内存回写顺序的规定,2、3的执行顺序无法保障,如果先执行了3,2还未执行完毕,这个时候切换了线程B,这个时候instance因为已经在线程A执行了3,instance!=null,所以线程B直接取走了instance,再使用时就会报错了,这就是DCL失效问题,JDK1.5以后使用volatile关键字将解决此问题。
7. 多进程中单例为什么会失效?
因为进程间,内存空间是相互独立的,会产生不同的副本。