首页 > 其他分享 >单例模式

单例模式

时间:2023-05-31 20:01:14浏览次数:30  
标签:EnumSingle class 模式 public LazyMan static 单例 lazyMan

单例模式(反射破坏-枚举)

饿汉式单例

package com.jan.single;

//饿汉式单例
public class Hungry {
    //一上来就会加载好,可能会浪费空间
    private byte[] data11=new byte[1024*1024];
    private byte[] data12=new byte[1024*1024];
    private byte[] data13=new byte[1024*1024];
    private byte[] data14=new byte[1024*1024];

    private Hungry(){

    }

    private final static Hungry HUNGRY = new Hungry();//保证是唯一的了

    public static Hungry getInstance(){ //一上来就加载了
        return HUNGRY;
    }
}

懒汉式单例

package com.jan.single;

//懒汉式单例
public class LazyMan {

    private LazyMan(){ ///构造器私有
        System.out.println(Thread.currentThread().getName()+"ok");
    }

    private  static  LazyMan lazyMan;//对象

    public static LazyMan getInstance(){
        if(lazyMan==null){
            lazyMan = new LazyMan();
        }
        return lazyMan;
    }

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

此时运行结果:单例有问题,偶尔会有多线程

静态内部类:

package com.jan.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.jan.single;

//懒汉式单例
public class LazyMan {

    private LazyMan(){ ///构造器私有
        System.out.println(Thread.currentThread().getName()+"ok");
    }

    //为了安全,避免 lazyMan 指令重排,必须加volatile
    private volatile static  LazyMan lazyMan;//对象

    public static LazyMan getInstance(){  //这代码安全吗?
        //双重检测锁模式的 懒汉式单例 DCL模式
        if (lazyMan==null){
            synchronized (LazyMan.class){  //保证这个类 LazyMan.class 只有一个
                if(lazyMan==null){
                    lazyMan = new LazyMan(); //不是原子性操作
                    /*
                    * 1. 分配内存空间
                    * 2. 执行构造方法,初始化对象
                    * 3. 把这个对象指向这个空间
                    * 
                    * 期望执行 123
                    *          132 A(先占内存空间)
                    * 若此时有 B线程  基于A,系统会认为 lazyMan 不为Null   直接return
                    *        //此时lazyMan没有完成构造
                    * */
                }
            }
        }
        return lazyMan; 
    }

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

单例不安全,反射

枚举

炫技时刻 :反射破坏单例

package com.jan.single;

import java.lang.reflect.Constructor;

//懒汉式单例
public class LazyMan {

    private LazyMan(){ ///构造器私有
        System.out.println(Thread.currentThread().getName()+"ok");
    }

    //为了安全,避免 lazyMan 指令重排,必须加volatile
    private volatile static  LazyMan lazyMan;//对象

    public static LazyMan getInstance(){  //这代码安全吗?
        //双重检测锁模式的 懒汉式单例 DCL模式
        if (lazyMan==null){
            synchronized (LazyMan.class){  //保证这个类 LazyMan.class 只有一个
                if(lazyMan==null){
                    lazyMan = new LazyMan(); //不是原子性操作
                 
                }

            }

        }
        return lazyMan;
    }

    //反射
    public static void main(String[] args) throws Exception {
        LazyMan instance1 = LazyMan.getInstance();
        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);//获得空参构造器
        declaredConstructor.setAccessible(true);//破坏私有权限,无视私有的构造器   通过反射来创建对象
        LazyMan instance2= declaredConstructor.newInstance();

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

结果:

继续改进:不被反射破坏(三重检测)

package com.jan.single;

import java.lang.reflect.Constructor;

//懒汉式单例
public class LazyMan {

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

        synchronized (LazyMan.class){ //这里加一把锁,第二次用就会报错
            if (lazyMan!=null){
             throw new RuntimeException("不要试图使用反射破坏异常");
            }
        }
    }

    //为了安全,避免 lazyMan 指令重排,必须加volatile
    private volatile static  LazyMan lazyMan;//对象

    public static LazyMan getInstance(){  //这代码安全吗?
        //双重检测锁模式的 懒汉式单例 DCL模式
        if (lazyMan==null){
            synchronized (LazyMan.class){  //保证这个类 LazyMan.class 只有一个
                if(lazyMan==null){
                    lazyMan = new LazyMan(); //不是原子性操作
                    /*
                    * 1. 分配内存空间
                    * 2. 执行构造方法,初始化对象
                    * 3. 把这个对象指向这个空间
                    *
                    * 期望执行 123
                    *          132 A(先占内存空间)
                    * 若此时有 B线程  基于A,系统会认为 lazyMan 不为Null   直接return
                    *        //此时lazyMan没有完成构造
                    * */
                }

            }

        }
        return lazyMan;
    }

    
    //反射
    public static void main(String[] args) throws Exception {
        LazyMan instance1 = LazyMan.getInstance();
        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);//获得空参构造器
        declaredConstructor.setAccessible(true);//破坏私有权限,无视私有的构造器   通过反射来创建对象
        LazyMan instance2= declaredConstructor.newInstance();

        System.out.println(instance1);
        System.out.println(instance2);

    }

}

结果:

继续破坏:

如果两个对象都是LazyMan instance2= declaredConstructor.newInstance();这样获得,单例又被破坏

解决:加密(jan)

package com.jan.single;

import java.lang.reflect.Constructor;

//懒汉式单例
public class LazyMan {

    private static boolean jan = false;//定义一个变量


    private LazyMan(){ ///构造器私有
        synchronized (LazyMan.class){ //这里加一把锁,第二次用就会报错
            if (jan==false){
                jan = true; //这个肯定会变
            }else {
                throw new RuntimeException("不要试图使用反射破坏异常");
            }

        }
    }

    //为了安全,避免 lazyMan 指令重排,必须加volatile
    private volatile static  LazyMan lazyMan;//对象

    public static LazyMan getInstance(){  //这代码安全吗?
        //双重检测锁模式的 懒汉式单例 DCL模式
        if (lazyMan==null){
            synchronized (LazyMan.class){  //保证这个类 LazyMan.class 只有一个
                if(lazyMan==null){
                    lazyMan = new LazyMan(); //不是原子性操作
                }

            }

        }
        return lazyMan;
    }

    
    //反射
    public static void main(String[] args) throws Exception {
        //LazyMan instance1 = LazyMan.getInstance();
        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);//获得空参构造器
        declaredConstructor.setAccessible(true);//破坏私有权限,无视私有的构造器  通过反射来创建对象
        LazyMan instance2= declaredConstructor.newInstance();
        LazyMan instance1= declaredConstructor.newInstance();

        System.out.println(instance1);
        System.out.println(instance2);

    }

}

再破坏:

通过获取加密字段,从而破坏私有权限,再设置加密字段.instance = false
package com.jan.single;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

//懒汉式单例
//道高一尺魔高一丈
public class LazyMan {

    private static boolean jan = false;//定义一个变量


    private LazyMan(){ ///构造器私有
        synchronized (LazyMan.class){ //这里加一把锁,第二次用就会报错
            if (jan==false){
                jan = true; //这个肯定会变
            }else {
                throw new RuntimeException("不要试图使用反射破坏异常");
            }

        }
    }

    //为了安全,避免 lazyMan 指令重排,必须加volatile
    private volatile static  LazyMan lazyMan;//对象

    public static LazyMan getInstance(){  //这代码安全吗?
        //双重检测锁模式的 懒汉式单例 DCL模式
        if (lazyMan==null){
            synchronized (LazyMan.class){  //保证这个类 LazyMan.class 只有一个
                if(lazyMan==null){
                    lazyMan = new LazyMan(); //不是原子性操作
                }

            }

        }
        return lazyMan;
    }

    
    //反射
    public static void main(String[] args) throws Exception {
        //LazyMan instance1 =

        Field jan = LazyMan.class.getDeclaredField("jan");//获取字段
        jan.setAccessible(true);//破坏私有权限


        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);//获得空参构造器
        declaredConstructor.setAccessible(true);//破坏私有权限,无视私有的构造器   通过反射来创建对象
        LazyMan instance2= declaredConstructor.newInstance();

        jan.set(instance2,false);
        LazyMan instance1= declaredConstructor.newInstance();

        System.out.println(instance1);
        System.out.println(instance2);

    }

}

结果:

万恶的反射怎么解决:枚举

反编译jad:https://varaneckas.com/jad/

package com.jan.single;

//枚举 是一个什么? 本身也是class 类
public enum EnumSingle {
    
    INSTANCE;//不可能被拿走
    
    public EnumSingle getInstance(){
        return INSTANCE;
    }
    
}

class Test{  //先保证对象是唯一的,测试一下

    public static void main(String[] args) {
        EnumSingle instance1= EnumSingle.INSTANCE;
        EnumSingle instance2= EnumSingle.INSTANCE;
        System.out.println(instance1);
        System.out.println(instance2);
    }
}

结果:

看源码,有一个无参构造

在测试也使用无参构造

package com.jan.single;
import java.lang.reflect.Constructor;

//枚举 是一个什么? 本身也是class 类
public enum EnumSingle {

    INSTANCE;//不可能被拿走

    public EnumSingle getInstance(){
        return INSTANCE;
    }
}

class Test{  //试一试能不能破坏,看源代码  有一个无参构造

    public static void main(String[] args) throws Exception {
        EnumSingle instance1= EnumSingle.INSTANCE; //有一个无参构造,试一试,看看是不是真的
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        EnumSingle instance2 = declaredConstructor.newInstance();
        //com.jan.single.EnumSingle.<init>()
        //(newInstance 源码)正常报错是:throw new IllegalArgumentException("Cannot reflectively create enum objects");
       
        System.out.println(instance1);
        System.out.println(instance2);

    }
}

结果:被欺骗,这源码不是真的

因为newinstance的源码:

两个输出的结果不同!!!

反编译

在IDEA的targer中找到 EnumSingle.class,打开文件的文件的位置,再从文件的卫视输入 cmd

也有空参,也是骗我们的

更专业的反编译

jad

这里是有参构造

修改代码,这里只展示不同的

Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);

结果:这个反射的确不能破坏枚举的单例

枚举最终反编译源码

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   EnumSingle.java

package com.jan.single;


public final class EnumSingle extends Enum
{

    public static EnumSingle[] values()
    {
        return (EnumSingle[])$VALUES.clone();
    }

    public static EnumSingle valueOf(String name)
    {
        return (EnumSingle)Enum.valueOf(com/jan/single/EnumSingle, name);
    }

    private EnumSingle(String s, int i)
    {
        super(s, i);
    }

    public EnumSingle getInstance()
    {
        return INSTANCE;
    }

    public static final EnumSingle INSTANCE;
    private static final EnumSingle $VALUES[];

    static 
    {
        INSTANCE = new EnumSingle("INSTANCE", 0);
        $VALUES = (new EnumSingle[] {
            INSTANCE
        });
    }
}

标签:EnumSingle,class,模式,public,LazyMan,static,单例,lazyMan
From: https://www.cnblogs.com/zhongjianYuan/p/17447178.html

相关文章

  • Java 微服务中的聚合器设计模式示例
    微服务架构中的聚合器设计模式是一种设计模式,用于通过聚合多个独立的微服务的响应来组成一个复杂的服务。它也是与SAGA、CQRS和EventSourcing一起的基本微服务设计模式之一。当客户端请求需要跨多个微服务分布的数据或功能时,此模式是合适的。可以提高系统的性能和可扩展性通过允许......
  • 什么是微服务中的断路器设计模式?如何实施?
    大家好,微服务设计模式是Java开发人员需要学习的非常重要的概念,不仅是为了创建一个健壮的、可扩展的、高性能的微服务架构,也是为了在Java开发人员面试中取得好成绩。过去,我分享了几种微服务设计模式,如eEventSourcing、SAGA、DatabasePerMicroservices、CQRS、APIGateway......
  • minio server启动模式 小结
     官方文档:https://docs.min.io/docs/minio支持多种server启动模式 1.分布式文件系统应用:1.1、Minlo介绍:Minlo是一个基于ApacheLicensev2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数......
  • 单例bean与类加载过程
    构造单例bean的方式有很多种,我们来看一下其中一种,饿汉式publicclassSingleton1implementsSerializable{//1、构造函数私有privateSingleton1(){if(INSTANCE!=null){thrownewRuntimeException("单例对象不能重复创建");}......
  • mac bash 下使用vi 快捷方式——因为没有alt键 所以没有办法 用vi模式也非常方便的
     set-oemacs##切到emacs模式set-ovi##切到vi模式set-o##查看当前选项的设置状态 所以你只需要在.bashrc下加入set-ovi然后,使用ESC进行行首尾,单词之间的快速跳转。......
  • golang实现设计模式之抽象工厂模式总结-代码、优缺点、适用场景
    抽象工厂模式也是一种创建型的设计模式,其是在工厂模式的基础上实现更高程度的内聚。我们知道在工厂模式中,一种产品类就需要新建个对应的工厂类生成产品的实例,这会有什么问题呢?虽然工厂模式解决了简单工厂模式不好扩展的问题,实现了OCP,但一种产品就需要新建一个工厂类,比如有10000种......
  • Java中枚举类的特殊用法-使用枚举实现单例模式和策略模式
    场景设计模式-单例模式-饿汉式单例模式、懒汉式单例模式、静态内部类在Java中的使用示例:https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/127555096设计模式-单例模式-注册式单例模式-枚举式单例模式和容器式单例模式在Java中的使用示例:https://blog.csdn.net/BAD......
  • golang实现设计模式之工厂模式总结-代码、优缺点、适用场景
    工厂模式也是一种创建型模式,它与简单工厂不同的是将实例的创建推迟到具体的工厂类方法中实现,每一种产品生成一个对应的工厂,从而替换掉简单工厂方法模式中那个静态工厂方法。所以在工厂模式中,不同产品就由不同的工厂生产,每次增加产品时,我们就不需要在类似在简单工厂中,在统一的工厂......
  • 03.外观模式
    外观式定义为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。界面在这里提到的界面,主要指的是从一个组件外部来看这个组件,能够看到什么,这就是这个组件的界面,也就是所说的外观。接口在这里提到的接口,主要指的是外部......
  • 享元模式
    一、定义运用共享技术有效地支持大量细粒度的对象。二、适用场景系统会用到大量相同或相似的对象。对象创建比较耗时。三、目的减少创建对象的数量。对象全局共享。四、UML类图FlyweightFactory:享元工厂,用来创建并管理Flyweight对象Flyweight:享元类的基类或接口C......