1:单例模式。

学习设计模式的第一天,单例模式!!!

1:那么什么是单例模式呢?

        单例就是单一、独苗的意思,那什么是独一份呢?你的思维是独一份,除此之外还有什么不能山寨的呢? 我们举个比较难复制的对象:皇帝 中国的历史上很少出现两个皇帝并存的时期,是有,但不多,那我们就认为皇帝是个单例模式,在这 个场景中,有皇帝,有大臣,大臣是天天要上朝参见皇帝的,今天参拜的皇帝应该和昨天、前天的一样(过 渡期的不考虑,别找茬哦),大臣磕完头,抬头一看,嗨,还是昨天那个皇帝,单例模式,绝对的单例模式。

总的来说,不过你见几次,我都是这一个对象,好专一哦!

2:单例模式的简单实现

主要思路就是:

1:定义一个static final变量(在类加载时已经初始化)。

2:将构造函数私有化(不让随便new)。

3:暴露一个静态方法来返回该变量(每次返回都是类初始化时得变量)。

one:饿汉式

解释:

    什么是饿汉式,就是不管我用不用,我都提前把它造出来,饿的话 我就直接吃,不用出去买。

/**
 * 饿汉式
 * 类加载到内存之后,就实例化一个单例,JVM保证线程安全。
 * 推荐使用。
 * 唯一缺点:就是不管是否会用到,在类装载时就会完成实例化。
*/
public class Demo1 {
       private static final Demo1 DEMO_1 = new Demo1();
       //构造函数私有化。
       private Demo1(){}
       //给外界提供一个调用类方法的方法
       public static Demo1 getInstance(){
              return DEMO_1;
       }

       public static void main(String[] args) {
              //测试,创建两个对象,看看是否是同一个对象。
              Demo1 demo1 = Demo1.getInstance();
              Demo1 demo2 = Demo1.getInstance();
              System.out.println(demo2 == demo1); //答案为true,证明调用的是同一个对象。

       }
}

补充:类加载器子系统主要包括加载,连接(验证,准备,解析),初始化等阶段。被static修饰的变量在准备阶段就已经被赋上初始值(int 的话就是0),被static final修饰的变量直接会赋真实值,所以为什么final变量必须得赋值。也所以当类被加载时已经被初始化了。

two:饿汉式另一种表达。

采用静态代码块得方式为static final 变量赋值。

/**
 * 和demo1差不多一个意思。只是采用静态代码块的方式为其赋初始值。
 */
public class Demo2 {
       private static final Demo2 DEMO_2;
       //采用静态代码块的方式给它赋值。
       static {
              DEMO_2 = new Demo2();
       }
       public static Demo2 getInstance(){
              return DEMO_2;
       }

       public static void main(String[] args) {
              Demo2 demo2 = Demo2.getInstance();
              Demo2 demo21 = Demo2.getInstance();
              System.out.println(demo21 == demo2); // 为true,证明还是同一个对象。
       }
}

three:lazy loading(懒汉式,多线程情况下会出事情)

解释:

(懒汉式就是 我不用就没有,我用的话才创建,为什么,因为我懒!)

主要思路:

1:创建一个static变量。

2:构造函数私有化。

3:对外暴露一个方法,用来获取该变量

    进行判断

    如果变量为空,则创建一个对象并赋值给static变量。

    如果不为空,则证明前辈已经为他创建过该类实例,直接返回。 

/**
 * lazy loading
 * 也称懒汉式
 * 虽然达到了按需初始化的目的,但是却带来线程不安全的问题。
 */

public class Demo3 {
    private static Demo3 DEMO_3;

    //将构造函数私有化。
    private Demo3() {};

    public static Demo3 getInstance() {
        //思路就是如果demo_3 == null,则证明是第一次调用它,创建一个对象为其赋值,
        //          demo_3 != null,则证明不是第一次调用它,直接返回第一次赋值的结果,
        //按道理来说这样是可以保持永远拿到的是第一次创建的对象。
        if (DEMO_3 == null) {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            DEMO_3 = new Demo3();
        }
        return DEMO_3;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                System.out.println(Demo3.getInstance().hashCode());
                //在这里同时创建100个线程,打印出他们的hashcode,发现并不相同,这也就是上面所带来的问题。
                //分析问题:其实创建线程之后,他们有可能都走到了( if (DEMO_3 == null) )这一步,多个线程
                // 发现都为 DEMO_3 都 == null,所以会创建了多个对象。这样没有达到实现单例的效果。
            }).start();
            //补充:关于线程并不是说调用了start()方法就会理课执行线程,调用start()方法只会让线程处于就绪状态,
            //底层其实是调用start0()方法,他是被native修饰的,也就是说他是C++编写的,存在于本地方法栈,
            //至于什么时候开始执行,完全取决于操作系统什么时候为其进行分配资源。
        }
    }
}

four:懒汉式增强(加锁,避免多线程情况下出事情 √ )

/**
 * 针对于懒汉式,也可以解决,就是给他加锁,一个线程一个线程轮流排队去执行,这样不就可以了嘛?
 * 但是这样会影响性能。
 */
public class Demo4 {
       private static Demo4 DEMO_4;
       //构造函数私有化。
       private Demo4(){};
       //加锁(给静态方法加锁,相当于锁住的是整个类对象)
       public static synchronized Demo4 getInstance(){
              if(DEMO_4 == null){
                     try {
                            TimeUnit.SECONDS.sleep(1);
                     } catch (InterruptedException e) {
                            e.printStackTrace();
                     }
                     DEMO_4 = new Demo4();
              }
              return DEMO_4;
       }

       public static void main(String[] args) {
              for (int i = 0; i < 100; i++) {
                     new Thread(() ->{
                            System.out.println(Demo4.getInstance().hashCode());
                     }).start();
              }
       }
}

five:懒汉式增强(另一种加锁,多线程情况下出事情 × )

/**
 * 和demo4差不多,这是锁的位置不同
 */
public class Demo5 {
    private static Demo5 DEMO_5;

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

    public static Demo5 getInstance() {
        if (DEMO_5 == null) {
            //企图像通过减少同步代码块的方式来提高效率,发现并不可行。
            synchronized (Demo5.class) {
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                DEMO_5 = new Demo5();
            }
        }
        return DEMO_5;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                System.out.println(Demo5.getInstance().hashCode());
            }).start();
        }
    }
}

six:懒汉式增强(最终方案,synchronized+双重检测)

/**
 * 懒汉式增强,使用双重循环 + synchronized来实现
 */
public class Demo6 {
       private static volatile Demo6 DEMO_6;
       //构造函数私有化
       private Demo6(){};

       public static Demo6 getInstance(){
              if(DEMO_6 == null){
                     synchronized (Demo6.class){
                            //双重循环。
                            if(DEMO_6 == null){
                                   try {
                                          TimeUnit.SECONDS.sleep(1);
                                   } catch (InterruptedException e) {
                                          e.printStackTrace();
                                   }
                                   DEMO_6 = new Demo6();
                            }
                     }

              }
              return DEMO_6;
       }

       public static void main(String[] args) {
              for (int i = 0; i < 100; i++) {
                     new Thread(()->{
                            System.out.println(Demo6.getInstance().hashCode());
                     }).start();
              }
       }
}

seven:静态内部类实现

/**
 * 静态内部类的方式。
 * JVM来保证单例。
 * 加载外部类时不会加载内部类,这样可以实现懒加载。
 */
public class Demo7 {
    //构造函数私有化。
    private Demo7(){};
    //创建静态内部类
    private static class Demo7Holder{
        private static final Demo7 DEMO_7 = new Demo7();
    }
    public static Demo7 getInstance(){
        return Demo7Holder.DEMO_7;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                System.out.println(Demo6.getInstance().hashCode());
            }).start();
        }
    }
}

eight:枚举类(枚举类没有构造方法) 

/**
 * @author 小泽
 * @create 2022-10-19  10:18
 * 记得每天敲代码哦
 */
public enum Demo8 {
    INSTANCE;
    public void m(){}

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                System.out.println(Demo8.INSTANCE);
            }).start();
        }
    }
}

好了,我所了解到的就这八种单例举例了,可以看出来以上真正实现单例得也就是one,two,four,six,seven,eight。 

推荐使用得是one(饿汉式),six(synchronized+双重检测),seven(静态内部类)。

第一天得设计模式学习终于结束了,说实话真的很煎熬,但是还得加油!

生活很苦,但小泽与你相伴,加油!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小泽不会Java

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值