【经典设计模式】一、单例设计模式

本文详细介绍了Java中的单例模式,包括饿汉式(静态常量、静态代码块)、懒汉式(线程不安全、线程安全的同步方法、同步代码块)、双重检查、静态内部类和枚举实现方式。分析了每种方式的创建步骤、优缺点,重点讨论了线程安全问题。最后总结了单例模式的应用场景和推荐的实现方式。
摘要由CSDN通过智能技术生成

目录

1、前言

2、设计模式类型

3、单例模式介绍

  ①  饿汉式(静态常量):用 final static 修饰类的对象引用

1、创建步骤如下:

2、说明如下:

3、 代码实现:

4、饿汉式(静态常量)优缺点

② 饿汉式(静态代码块)

1、创建步骤如下:

2、 代码实现:

3、饿汉式(静态代码块)优缺点

③ 懒汉式(线程不安全)

1、创建步骤如下:

2、 代码实现:

3、优缺点

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

1、创建步骤如下:

2、 代码实现:

3、优缺点

⑤ 懒汉式(线程安全,同步代码块)

1、创建步骤如下:

2、 代码实现:

3、优缺点

⑥ 双重检查,修饰类的时候是volatile

1、创建步骤如下:

2、 代码实现:

⑦ 静态内部类

1、创建步骤如下:

2、 代码实现:

3、结论

⑧ 枚举

1、创建步骤如下:

2、 代码实现:

3、结论

4、单例模式总结


1、前言

       设计模式是程序员在面对同类软件工程设计问题所总结出来的有用的经验,模式不是代码,而是某类问题的通用解决方案。设计模式代表了最佳的实践。这些解决方案是众多软件开发人员经过相当长的一段时间的经验和错误总结出来的。设计模式的本质就是提高软件的维护性,通用性和扩展性,并降低软件的复杂度。

       简单的说,就是前辈们在遇到一些问题和经验总结出来的,面对不同的问题,有不同的解决方案,且对症下药。使问题解决,效率提高,提高了程序的安全性、健壮性、可靠性等。

2、设计模式类型

       设计模式分为三种类型,共有23种。

       (1)创建型类型:单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式。

       (2)结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式

       (3)行为型模式:模板方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式(责任链模式)

3、单例模式介绍

      所谓类的单例模式,就是采取一定的方法,保证在整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得对象实例的方法。

      所谓饿汉式,通俗理解就是很着急很着急去创建对象。在外部还没调用的时候就自己创建好了,等着被调用。

      所谓懒汉式,就是不着急创建对象,等待外部调用的时候再创建对象。

     单例模式有8种实现方式:

     ①  饿汉式(静态常量)

     ②  饿汉式(静态代码块)

     ③  懒汉式(线程不安全)

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

     ⑤  懒汉式(线程安全,同步代码块)

     ⑥  双重检查

     ⑦  静态内部类

     ⑧  枚举

下面开始具体介绍单例设计模式:

  ①  饿汉式(静态常量):用 final static 修饰类的对象引用

   饿汉式(静态常量):就是该类很着急创建对象,即在类中创建一个私有化实例对象,并将对象的引用用static来修饰,保证了全局和唯一性

1、创建步骤如下:

   (1)构造器私有化(防止new)

   (2)类的内部创建对象

   (3)向外部暴露一个静态的公共方法,这个公共方法的作用就是向外界提供该类的实例

   (4)代码实现

2、说明如下:

         当类的构造函数被私有化的时候,外界是不能通过new的方式实例化类。这样就可以限制调用静态方法实例化对象。

3、 代码实现:

class Singleton{
    
    // 1、构造器私有化,外部不能new
    private Singleton(){}

    // 2、本类内部创建实例对象
    private final static Singleton instance = new Singleton();

    // 3、通过一个静态方法为外部提供一个获得此类对象的方法
    public static Singleton getInstance(){
        return instance;
    }
}

4、饿汉式(静态常量)优缺点

   优点:这种写法比较简单,在类加载的时候就完成了实例化,避免了线程了同步问题。

   缺点:在类装载的时候就完成了实例化。如果刚开始就没使用过这个实例就会造成内存的浪费。

   结论:这种单例模式可用,但是使用不当会造成内存的浪费。 

② 饿汉式(静态代码块)

   饿汉式(静态代码块):在类中创建对象的引用,将创建对象的事情放到static代码块里面执行。static代码块随着类的加载而加载的。

1、创建步骤如下:

   (1)构造器私有化(防止new)

   (2)类的内部创建本类的引用

   (3)在static静态代码块中创建类的实例。

   (4)向外部暴露一个静态的公共方法,这个公共方法的作用就是向外界提供该类的实例

   (5)代码实现

2、 代码实现:

class Singleton{

    // 私有化 构造器
    private Singleton(){}

    private static Singleton instance;

    // 创建类的实例对象
    static{
        instance = new Singletoon();
    }
    
    // 静态方法供外部调用获取实例
    public static Singleton getInstance(){
        return instance;
    }
}

3、饿汉式(静态代码块)优缺点

     这种方式实际上是把类的创建过程放到了static代码块中,缺点是类不创建对象的时候,会浪费空间。

③ 懒汉式(线程不安全)

  所谓懒汉式就是等待外部调用的时候,再根据需要创建对象。

1、创建步骤如下:

   (1)构造器私有化(防止new)

   (2)类的内部创建本类的引用

   (3)向外部暴露一个静态的公共方法,这个公共方法的作用就是向外界提供该类的实例,先判断该对象是否为空,为空的话就创建对象

   (4)代码实现

2、 代码实现:

class Singleton{

    // 私有化 构造器
    private Singleton(){}

    private static Singleton instance;

   
    // 首先判断是否为空,静态方法供外部调用获取实例
    public static Singleton getInstance(){
        if(instance == null){
            instance = new Singeton();
        }
        return instance;
    }
}

3、优缺点

  起到了懒加载的效果,但是只能在单线程的情况下使用。

  如果在多线程的情况下,一个线程进入if(instance == null)判断语句块,还未来得及往下执行,另一个线程也通过判断这个语句,这时会产生多个实例。所以多线程的情况下是不安全的。

  在开发中,不要使用这种模式。

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

  所谓懒汉式就是等待外部调用的时候,再根据需要创建对象。通过同步方法控制线程的执行,保证安全。

1、创建步骤如下:

   (1)构造器私有化(防止new)

   (2)类的内部创建本类的引用

   (3)向外部暴露一个静态的公共方法,在公共方法的前面添加synchronized来控制线程的执行,这个公共方法的作用就是向外界提供该类的实例,先判断该对象是否为空,为空的话就创建对象

   (4)代码实现

2、 代码实现:

class Singleton{

    // 私有化 构造器
    private Singleton(){}

    private static Singleton instance;

   
    // 首先判断是否为空,静态方法供外部调用获取实例
    public static synchronized Singleton getInstance(){
        if(instance == null){
            instance = new Singeton();
        }
        return instance;
    }
}

3、优缺点

  解决了线程不安全的问题,但是效率太低了,每个类想获得类的实例的时候,必须执行getInstance()都要执行同步,虽然new对象只需要执行一次。但是方法同步执行效率太低。

  所以在实际开发中,不推荐使用这种方式。

⑤ 懒汉式(线程安全,同步代码块)

  所谓懒汉式就是等待外部调用的时候,再根据需要创建对象。通过同步方法控制线程的执行,保证安全。

1、创建步骤如下:

   (1)构造器私有化(防止new)

   (2)类的内部创建本类的引用

   (3)向外部暴露一个静态的公共方法,在公共方法的中添加synchronized同步代码块来控制线程的执行

   (4)代码实现

2、 代码实现:

class Singleton{

    // 私有化 构造器
    private Singleton(){}

    private static Singleton instance;

   
    // 首先判断是否为空,静态方法供外部调用获取实例
    public static Singleton getInstance(){
        if(instance == null){
            synchronized (Singleton.class){
                instance = new Singeton();
            }
        }
        return instance;
    }
}

3、优缺点

 这种方式并不能起到线程同步的作用,因为当if(instance == null)的时候,一个线程已经执行了,还未来得及往下执行,另一个线程又通过这个判断语句,这时候会产生多个实例,第一个实例没有更新到内存中。实际开发中不能使用这种方式。

⑥ 双重检查,修饰类的时候是volatile

  所谓双重检查就是为了防止多次创建实例的情况,是保证线程安全和提高效率

  

1、创建步骤如下:

   (1)构造器私有化(防止new)

   (2)类的内部创建本类的引用

   (3)向外部暴露一个静态的公共方法,在公共方法的中有两层if,第一层if是判断是否有对象创建,没有的话就创建,当有AB两个线程同时进入第一层if,A先创建对象,此时singleton有对象了。B线程再进去,第二层if判断已经有对象了,就不会再去创建。假如C线程再来的话,第一层判断对象已经存在,那么就返回对象。保证了线程的安全性提高了执行的效率。

   (4)知识补充:

     volatile:当修饰类的对象应用的时候,呈现出所有线程都可以看到的共享内存的最新状态。

2、 代码实现:

class Singleton{

    // 私有化 构造器
    private Singleton(){}

    private static volatile Singleton instance;

   
    // 首先判断是否为空,静态方法供外部调用获取实例
    public static Singleton getInstance(){
        if(instance == null){
            synchronized (Singleton.class){
                if(instance == null){
                    instance = new Singeton();
                }
            }
        }
        return instance;
    }
}

⑦ 静态内部类

  所谓内部静态类,通过内部静态类创建静态常量对象。直接返回内部类对象

1、创建步骤如下:

   (1)构造器私有化(防止new)

   (2)创建内部静态类,并且创建静态常量对象引用

   (3)返回内部类对象

2、 代码实现:

class Singleton{

    // 私有化 构造器
    private Singleton(){}

    // 内部类创建外部类静态常量对象
    private static class SingletonInstance{
        private static final Singleton INSTANCE= new Singleton();
    }
    
   
    // 返回外部类对象
    public static Singleton getInstance(){
        
        return SingletonInstance.INSTANCE;
    }
}

3、结论

当外部类被加载的时候,内部类不会被加载。当执行获取对象的静态方法的时候,内部类才会被加载,只会被加载一次。

这种方式,实时的只会限制一个线程的执行。

⑧ 枚举

  所谓枚举,就是通过调用枚举类的成员来限制线程的单例。

1、创建步骤如下:

   (1)创建枚举类,添加枚举类属性,且属性唯一。

   (2)外部通过枚举类名调用枚举类的属性,获取实例对象。

2、 代码实现:

public class Singleton1 {
    public static void main(String[] args) {
        Singleton singleton = Singleton.INSTANCE;
        Singleton singleton2 = Singleton.INSTANCE;

        System.out.println(singleton == singleton2);

        System.out.println(singleton.hashCode());
        System.out.println(singleton2.hashCode());
    }
}

enum Singleton{
    INSTANCE;
    public void say(){
        System.out.println("OK");
    }
}

3、结论

不仅能避免多线程同步的问题,而且还能防止反序列化重新创建新的对象。

4、单例模式总结

     单例模式保证了系统内存中只存在一个对象,节省了系统的资源,对于一些需要频繁销毁的对象,使用单例模式可以提高系统的性能。

     当想实例化一个单例的时候,必须调用创建实例的方法。

     单例模式使用场景:

       需要频繁创建和销毁对象、创建对象的时候耗时过多或者是耗费资源过多,经常用到的工具类对象、频繁访问数据库对象。

     推荐使用:

     枚举、静态内部类、双重检查

  如果这篇文章能让你系统认识单例设计模式,求个赞!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值