设计模式———单例模式



设计模式

概念:是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结,使用设计模式是为了可重用代码、让代码更容易被他人理解并且保证代码可靠性。、

分类:设计模式可分为创建型(Creational),结构型(Structural)和行为型(Behavioral)

  • 创建型模式:用于描述如何 创建对象————五种

其中包括:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

  • 结构型模式:用于描述如何 实现类或对象的组合————七种

其中包括:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

  • 行为型模式:用于描述类或对象 怎样交互以及怎样分配职责————十一种

其中包括:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

面向对象设计思想

单一职责原则——SRP
  • 概念:一个类只负责一个功能。
    单一职责原则是实现高内聚、低耦合的指导方针
开闭原则——OCP
  • 概念:一个软件实体应当对扩展开放,对修改关闭
里氏代换原则——LSP
  • 概念:所有引用基类(父类)的地方必须能透明地使用其子类的对象。
    在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象。

例如:父类Animal————子类Dog

依赖倒转原则——DIP
  • 概念:抽象不应该依赖于细节,细节应当依赖于抽象。就是要针对接口编程,而不是针对实现编程。
    开闭原则是目标,里氏代换原则是基础,依赖倒转原则是手段
接口隔离原则——ISP
  • 概念:使用多个专门的接口,而不使用单一的总接口。
合成复用原则——CRP
  • 概念:尽量使用对象组合,而不是继承来达到复用的目的。
迪米特法则——LOD
  • 概念:一个软件实体应当尽可能少地与其他实体发生相互作用。

应该尽量减少对象之间的交互,如果两个对象之间不必彼此直接通信,那么这两个对象就不应当发生任何直接的相互作用,如果其中的一个对象需要调用另一个对象的某一个方法的话,可以通过第三者转发这个调用。简言之,就是通过引入一个合理的第三者来降低现有对象之间的耦合度。

单例模式

  • 概念:保证对某个类只存在一个对象实例,并且仅提供一个生成该类的实例的静态方法
  • 使用场景
  1. 需要频繁创建或销毁的对象。
  2. 创建对象耗费资源过多
  3. 工具类对象、数据源对象、文件对象

饿汉式————静态常量

class SingleMan{
    // 1. 私有构造器
    private SingleMan() {}
    // 2. 私有静态变量(也可以说是常量,因为仅一个,且不变)
    private static final SingleMan singleMan = new SingleMan();
    // 3. 公有静态方法
    public static SingleMan getInstance(){
        return singleMan;
    }
}
  • 优点:在类装载时完成实例化,避免线程同步问题获取多个对象实例。
  • 缺点:提前加载好,若实例未使用,浪费内存空间,没有达到懒加载的效果。

饿汉式————静态代码块

其他步骤不变,第二步,将创建对象的操作放在静态代码块中

 // 2. 私有静态变量(也可以说是常量,因为仅一个,且不变)
    private static final SingleMan singleMan ;
    
    // 2. 将创建对象的步骤放在静态代码块中
    static {
        singleMan = new SingleMan();

总结:可以使用,但是会造成资源浪费。

懒汉式————线程不安全

在使用getInstance()方法时才会创建对象实例

class LazyMan{
    // 1. 私有构造器
    private LazyMan() {}
    // 2. 私有静态变量
    private static LazyMan lazyMan ;
    // 3. 公有静态方法
    public static LazyMan getInstance(){
        if (lazyMan==null){
            lazyMan =  new LazyMan();
        }
        return lazyMan;
    }
}
  • 优点:达到了懒加载的效果。
  • 缺点:线程不安全。当出现多个线程同时进入getInance()方法时,会产生多个实例。

懒汉式————线程安全(同步方法)

其他步骤不变,第三步,在方法上加synchronized同步锁,保证线程安全。

public static synchronized LazyMan getInstance(){
        if (lazyMan==null){
            lazyMan =  new LazyMan();
        }
        return lazyMan;
    }
  • 优点:解决了线程不安全的问题。
  • 缺点:效率低。每次获取实例时都会调用getInstance()方法,每次都会进入同步锁。

双重检查

第一步:在变量上添加volatile关键字。

  • volatile目的:多线程时,一个线程改变volatile修饰的变量,其他线程能立马看到

第三步:在getInstance()方法中添加一个同步代码块,里面再次判断对象是否为空。

双重检查锁原理

条件:多线程情况下,未创建对象。

执行步骤

  1. 线程A、B均进入getInstance()方法,因为对象实例为空,均在同步代码块处等待。
  2. 此时线程A先进入同步代码块,再次判断确定对象为空,创建对象后退出。
  3. 此时线程B进入同步代码块,再次判断时,因为实例对象上有volatile修饰,B看到A已创建对象,此时对象不为空,则直接返回A创建好的对象实例。
class SingleTon{
    // 1. 私有构造器
    private SingleTon() {}

    // 2. 私有静态变量
    private static volatile SingleTon singleTon ;

    // 3. 公有静态方法————双重检查
    public static SingleTon getInstance(){
        if (singleTon==null){
            synchronized (SingleTon.class){
                if (singleTon==null){
                    singleTon =  new SingleTon();
                }
            }
        }
        return singleTon;
    }
}

总结:解决了线程不安全问题,双重判断也使程序效率提升。不用每次都进入同步锁中进行判断,减少了消耗。推荐使用!!!

静态内部类

静态内部类的特点:

  1. 主类被加载时,静态内部类不会被加载
  2. 静态内部类只会加载一次
class SingleOne{
    // 1. 私有构造器
    private SingleOne() {}

    // 2. 静态内部类
    private static class SingleManInner{
        private static final SingleOne INSTANCE = new SingleOne();
    }

    // 3. 公有静态方法————调用静态内部类
    public static SingleOne getInstance(){
        return SingleManInner.INSTANCE;
    }
}

总结:既解决了线程不安全问题,又满足了懒加载的需求。推荐使用!!!

枚举

public class EnumDemo {
    public static void main(String[] args) {
        Single instance01 = Single.INSTANCE;
        Single instance02 = Single.INSTANCE;
        System.out.println(instance01==instance02);
        System.out.println(instance01.hashCode());
        System.out.println(instance02.hashCode());
    }
}
enum Single{
    INSTANCE;
    public void say(){
        System.out.println("你好啊");
    }
}

阻绝了反射和反序列化,简单方便。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值