单例模式-狂神说

目录

1.什么是单例模式

2. 单例模式的类型

一:饿汉式单例

二:懒汉式

        DCL懒汉式

三:静态内部类

 四:枚举


【狂神说Java】单例模式

1.什么是单例模式

单例模式是指在内存中只会创建且仅创建一次对象的设计模式。

2. 单例模式的类型

一:饿汉式单例

package com.shusl.single;

/*
饿汉式单例
    很饿,一上来就把对象加载了!
    问题:有可能浪费内存
        我想用的时候再去创建这个对象,解决办法:懒汉式单例

单例中最重要的思想:构造器私有
    一旦私有构造器了,别人就无法new这个对象了,保证了内存中只有一个对象
 */
public class Hungry {

    //可能会浪费空间,开辟了空间,却没有使用
    private Hungry(){

    }
    //饿汉式:先 new 对象, 可以保证唯一的
    private final static Hungry HUNGRY = new Hungry();
    //抛出一个对外的方法
    public static Hungry getInstance(){
        return HUNGRY;
    }
}

A类调用:

二:懒汉式

package com.shusl.single;
/*
懒汉式单例
    用的时候再去加载
 */
public class LazyMan01 {
    private  LazyMan01(){
        System.out.println("LazyMan01");
    };
    private  static LazyMan01 lazyMan01;

    public  static LazyMan01 getInstance(){
        if (lazyMan01 == null) {
            lazyMan01 = new LazyMan01();
        }
        return  lazyMan01;
    }

}

多线程并发测试,破坏单例

    //多线程并发
    public static void main(String[] args){
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                LazyMan01.getInstance();
            }).start();
        }
        /*
        *LazyMan01
        LazyMan01
        LazyMan01
         */
    }

多线程并发破坏单例,解决加锁

解决指令重排 :volatile

DCL懒汉式

package com.shusl.single;

public class LazyMan02 {
    private  LazyMan02(){
       
    };
    private  volatile static LazyMan02 lazyMan02;//volatile 避免指令重排

    //双重检测锁模式的懒汉式单例 DCL
    public  static LazyMan02 getInstance(){
        if(lazyMan02 == null){
            synchronized (LazyMan02.class){//锁class只有一个
                if (lazyMan02 == null) {
                    lazyMan02 = new LazyMan02();//有问题,不是原子性操作
                    /**
                     * 不是原子性操作,会经过三步操作 (底层操作)
                     * 1、分配内存空间
                     * 2、执行构造方法,初始化对象
                     * 3、把这个对象指向这个空间
                     * 这样才能保证这个对象 new 完成
                     *
                     * 会发生一个指令重排的情况:
                     * 比如:我们希望它执行123,但是它真实执行了132,这就完了
                     * 比如:第一个线程 A 进来时,执行了132(先把空间占用下来,再把内存对象放进去),
                     * 然后 A 线程没有问题,但是当B线程来的时候就有问题了
                     * 线程 B 由于它已经指向这个内存空间了,它会人为 LazyMan02 != null,会return LazyMan02,
                     * 这个时候 这个LazyMan02还没有完成构造,它的空间是虚无的,就会产生一个问题。
                     *
                     * 所有为了安全,一定要在 LazyMan02 中加 volatile,避免指令重排的
                     */
                }
            }
        }

        return  lazyMan02;
    }

}

三:静态内部类

package com.shusl.single;


//静态内部类
public class Holder {

    //构造器私有
    private Holder(){

    }
    public static Holder getInstance(){
        return InnerClass.HOLDER;
    }

    public static class InnerClass{
        private static final Holder HOLDER = new Holder();

    }
}

单例不安全,因为有反射

反射破坏单例测试一:

package com.shusl.single;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class LazyMan02 {
    private  LazyMan02(){
        System.out.println("LazyMan01");
    };
    private  volatile static LazyMan02 lazyMan02;//volatile 避免指令重排

    //双重检测锁模式的懒汉式单例 DCL
    public  static LazyMan02 getInstance(){
        if(lazyMan02 == null){
            synchronized (LazyMan02.class){//锁class只有一个
                if (lazyMan02 == null) {
                    lazyMan02 = new LazyMan02();
                   
                }
            }
        }

        return  lazyMan02;
    }

    public static void main(String[] args) throws Exception{
        LazyMan02 lazyMan02 =  LazyMan02.getInstance();
        Constructor<LazyMan02> constructor = LazyMan02.class.getDeclaredConstructor(null); //获得构造器
        constructor.setAccessible(true);//无视私有构造器
        LazyMan02 instance = constructor.newInstance();//创建对象
        System.out.println(instance);
        System.out.println(lazyMan02);
/**
 * LazyMan01
 * LazyMan01
 * com.shusl.single.LazyMan02@1b6d3586
 * com.shusl.single.LazyMan02@4554617c
 */
    }



}

解决反射加锁:

package com.shusl.single;

import java.lang.reflect.Constructor;

public class LazyMan03 {
    private LazyMan03() {
        synchronized (LazyMan03.class) {
            if (lazyMan03 != null) {
                throw new RuntimeException("不要试图反射破坏异常");
            }
        }
    }

    private volatile static LazyMan03 lazyMan03;//volatile 避免指令重排

    //双重检测锁模式的懒汉式单例 DCL
    public static LazyMan03 getInstance() {
        if (lazyMan03 == null) {
            synchronized (LazyMan03.class) {//锁class只有一个
                if (lazyMan03 == null) {
                    lazyMan03 = new LazyMan03();

                }
            }
        }

        return lazyMan03;
    }

    public static void main(String[] args) throws Exception {
        LazyMan03 lazyMan03 = LazyMan03.getInstance();
        Constructor<LazyMan03> constructor = LazyMan03.class.getDeclaredConstructor(null); //获得构造器
        constructor.setAccessible(true);//无视私有构造器
        LazyMan03 instance = constructor.newInstance();//创建对象

        System.out.println(instance);
        System.out.println(lazyMan03);
    }
}

问题:两个对象都是反射来定义,破坏单列?

   public static void main(String[] args) throws Exception {
        //LazyMan03 lazyMan03 = LazyMan03.getInstance();
        Constructor<LazyMan03> constructor = LazyMan03.class.getDeclaredConstructor(null); //获得构造器
        constructor.setAccessible(true);//无视私有构造器
        LazyMan03 instance = constructor.newInstance();//创建对象
        LazyMan03 instance2 = constructor.newInstance();//创建对象

        System.out.println(instance);
        System.out.println(instance2);
        /**
         * com.shusl.single.LazyMan03@2a33fae0
         * com.shusl.single.LazyMan03@707f7052
         */
    }

解决:红绿灯:定义标志位

package com.shusl.single;

import java.lang.reflect.Constructor;

public class LazyMan04 {
    //定义标志位
    private static boolean shu = false;
    private LazyMan04() {
        synchronized (LazyMan04.class) {
            if (shu == false){
                shu = true;
            } else {
                throw  new RuntimeException("不要试图使用反射破坏异常");
            }
        }
    }

    private volatile static LazyMan04 lazyMan04;//volatile 避免指令重排

    //双重检测锁模式的懒汉式单例 DCL
    public static LazyMan04 getInstance() {
        if (lazyMan04 == null) {
            synchronized (LazyMan03.class) {//锁class只有一个
                if (lazyMan04 == null) {
                    lazyMan04 = new LazyMan04();

                }
            }
        }

        return lazyMan04;
    }

    public static void main(String[] args) throws Exception {
        Constructor<LazyMan04> constructor = LazyMan04.class.getDeclaredConstructor(null); //获得构造器
        constructor.setAccessible(true);//无视私有构造器
        LazyMan04 instance = constructor.newInstance();//创建对象
        LazyMan04 instance2 = constructor.newInstance();//创建对象

        System.out.println(instance);
        System.out.println(instance2);
    }
}

反射获取字段来破坏单例:



    public static void main(String[] args) throws Exception {
        Field shu = LazyMan04.class.getDeclaredField("shu");
        shu.setAccessible(true);//把这个私有权限也破坏
        Constructor<LazyMan04> constructor = LazyMan04.class.getDeclaredConstructor(null); //获得构造器
        constructor.setAccessible(true);//无视私有构造器
        LazyMan04 instance = constructor.newInstance();//创建对象
        shu.set(instance,false);
        LazyMan04 instance2 = constructor.newInstance();//创建对象

        System.out.println(instance);
        System.out.println(instance2);
        /**
         * com.shusl.single.LazyMan04@4554617c
         * com.shusl.single.LazyMan04@74a14482
         */
    }
}

源码来分析:点入newInstance知道enum反射不能破坏

 四:枚举

public enum EnumSingle {
    INSTANCE;
    public EnumSingle getInstance(){
        return INSTANCE;
    }
}

为什么枚举可以,探寻???

打开查看枚举构造方法测试下是不是无参构造:报错没有无参构造

class Test{
    public static void main(String[] args) throws Exception {
        EnumSingle instance = EnumSingle.INSTANCE;
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        EnumSingle instance2 = declaredConstructor.newInstance();

        //java.lang.NoSuchMethodException: com.ph.single.EnumSingle.<init>()
        System.out.println(instance);
        System.out.println(instance2);

    }
}

 反编译这个类,

 反编译软件编译之后

知道有参的测试

package com.shusl.single;


import java.lang.reflect.Constructor;

//enmu是什么?本身也是一个class类
public enum EnumSingle {
    INSTANCE;
    public EnumSingle getInstance(){
        return INSTANCE;
    }
}

class Test{
    public static void main(String[] args) throws Exception {
        EnumSingle instance = EnumSingle.INSTANCE;
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
        declaredConstructor.setAccessible(true);
        EnumSingle instance2 = declaredConstructor.newInstance();

        System.out.println(instance);
        System.out.println(instance2);
/*Cannot reflectively create enum objects*/

    }
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值