设计模式之单例模式
一、23种设计模式
1. 概念
- 设计模式是前辈们对代码开发经验的总结,是解决特定问题的一系列套路,他不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案
- 1995年,GoF合作出版了《设计模式:可复用面向对象软件的基础》一书,收录了23中设计模式,从此树立了软件设计模式领域的里程碑,人称GoF设计模式
2. 意义
学习设计模式的意义
- 设计模式的本质是面向对象设计原则的实际运用,是对类的
封装性
、继承性
和多态性
以及类的关联关系和组合关系的充分理解 - 正确使用设计模式具有以下优点:
- 可以提高程序员的
思维能力
,编程能力
和设计能力
- 使程序设计更加
标准化
、代码编制更加工程化
、使软件开发效率大大提高,从而缩短软件的开发周期
- 可以提高程序员的
- 使设计的代码可重用性高、可读性强、可靠性高、灵活性好、可维护性强
二、GoF 23
- GoF23:一种思维,一种态度,一种进步
- 创建型模式:单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式
- 结构性模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式
- 行为性模式:模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式
三、OOP七大原则
- 开闭原则:对扩展开放,对修改关闭
- 里氏替换原则:继承必须确保父类所拥有的性质在子类中依然成立
- 依赖倒置原则:要面向接口编程,不要面向实现编程
- 职责原则:控制类的粒度大小,将对象解耦,提高其内聚性
- 接口隔离原则:要为各个类建立它们需要的专用接口
- 迪米特原则:只与你的直接朋友(成员变量、方法参数、方法返回值中的类)交谈,不跟“陌生人”(方法体内部的类)说话
- 合成复用原则:尽量先使用组合或者聚合等关联关系实现,其次才考虑使用继承关系来实现
四、单例模式
1. 饿汉式单例
// 饿汉式单例
public class Hungry {
// 可能会很浪费空间
private byte[] data1 = new byte[1024*1024];
private byte[] data2 = new byte[1024*1024];
private byte[] data3 = new byte[1024*1024];
private byte[] data4 = new byte[1024*1024];
private byte[] data5 = new byte[1024*1024];
// 私有化构造方法
private Hungry(){
}
private final static Hungry HUNGRY = new Hungry();
// 对外提供一个开放的接口,用于返回实例对象
public static Hungry getInstance(){
return HUNGRY;
}
}
2. 懒汉式单例
存在多线程并发模式,后面的DCL懒汉式解决并发问题
public class LazyMan {
private LazyMan(){
System.out.println(Thread.currentThread().getName()+"ok");
}
private static LazyMan lazyMan;
// 双重检测锁模式的 懒汉式单例 DCL懒汉式
public static LazyMan getInstance(){
if(lazyMan == null){
lazyMan = new LazyMan();// 不是一个原子性操作
}
return lazyMan;
}
/*
* lazyMan = new LazyMan();
* 1. 分配内存空间
* 2. 执行构造方法,初始化对象
* 3. 把这个对象指向这个空间
*
* 123
* 132 A
* B // 此时lazyMan还没有完成构造
* */
// 多线程并发
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
LazyMan instance = LazyMan.getInstance();
System.out.println(instance.hashCode());
}).start();
}
}
}
3. DCL 懒汉式单例
注意:synchronized
解决并发问题,但是因为lazyMan = new LazyMan();
不是原子性操作(可以分割,见代码注释),可能发生指令重排序的问题,通过 volatile
来解决
- Java语言提供了
volatile
和synchronized
两个关键字来保证线程之间操作的有序性,volatile
是因为其本身包含“禁止指令重排序”的语义,synchronized
是由“一个变量在同一个时刻只允许一条线程对其进行lock
操作”这条规则获得的,此规则决定了持有同一个对象锁的两个同步块只能串行执行。 - 原子性就是指该操作是不可再分的。不论是多核还是单核,具有原子性的量,同一时刻只能有一个线程来对它进行操作。简而言之,在整个操作过程中不会被线程调度器中断的操作,都可认为是原子性。比如 a = 1;
public class LazyMan {
// 私有化构造器
private LazyMan(){
System.out.println(Thread.currentThread().getName()+"ok");
}
private static volatile LazyMan lazyMan;
// Double Check Lock 双重检测锁模式的懒汉式单例
public static LazyMan getInstance(){
if( lazyMan == null ){
synchronized (LazyMan.class){ // synchronized加锁解决多线程下的问题
if(lazyMan == null){
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
// 多线程并发
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
LazyMan instance = LazyMan.getInstance();
System.out.println(instance.hashCode());
}).start();
}
}
}
4. 静态内部类单例
// 静态内部类
public class Holder {
// 构造器私有
private Holder(){
}
public static Holder getInstance(){
return InnerClass.HOLDER;
}
public static class InnerClass{
private static final Holder HOLDER = new Holder();
}
}
5. 枚举单例
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class Test01{
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
EnumSingle instance = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class, int.class);
declaredConstructor.setAccessible(true);
EnumSingle enumSingle = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(enumSingle);
}
}
注意: 枚举的唯一性,无法通过反射的方式创建枚举对象
Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects at java.lang.reflect.Constructor.newInstance(Constructor.java:417) at com.ppt.design.Test01.main(EnumSingle.java:18)
6. 单例不安全,反射破坏
public class LazyMan {
private static boolean flag = false; // 红绿灯解决通过反射创建对象(反编译可以破解该方法)
// 私有化构造器
private LazyMan(){
synchronized (LazyMan.class){
if(flag == false){
flag = true;
}else{
throw new RuntimeException("不要试图使用反射破坏单例");
}
}
System.out.println(Thread.currentThread().getName()+"ok");
}
private static volatile LazyMan lazyMan;
// Double Check Lock 双重检测锁模式的懒汉式单例
public static LazyMan getInstance(){
if (lazyMan == null) {
lazyMan = new LazyMan();
}
return lazyMan;
}
// 反射
public static void main(String[] args) throws Exception {
// LazyMan instance = LazyMan.getInstance();
Field flag = LazyMan.class.getDeclaredField("flag");
flag.setAccessible(true);
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true); // 无视私有的构造器
LazyMan lazyMan = declaredConstructor.newInstance();
flag.set(lazyMan,false);
System.out.println(lazyMan.hashCode());
LazyMan lazyMan1 = declaredConstructor.newInstance();
System.out.println(lazyMan1.hashCode());
}
}