java单例模式

博客主要介绍Java单例模式,包括饿汉式、DCL懒汉式、静态内部类等实现方式,提到volatile可避免指令重排。指出反射会破坏单例的安全性,可通过加三重锁破解。还介绍了枚举enum,其本身是class类,反射不能破坏枚举,同时提及反射获取类对象的新方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

单例模式

一日一句: 有能力不一定有机会,但是有机会,可以倒逼着你有能力。

单例模式有以下特点:
  1、单例类只能有一个实例。
  2、单例类必须自己创建自己的唯一实例。
  3、单例类必须给所有其他对象提供这一实例。

饿汉式和懒汉式的共同特点构造器私有化

  private LazyMan(){//单例模式最重要的就是构造器私有(懒汉式)

    }
   private Hungry(){//单列模式最重要就是构造器私有(饿汉式)

    }

饿汉式单例

package com.liang.single;

//饿汉式单例
public class Hungry {
//饿汉式未使用就已经加载了,浪费内存空间
    private byte[] data1=new byte[1024*1024];
    private byte[] data2=new byte[1024*1024];
    private byte[] data3=new byte[1024*1024];
    private byte[] data4=new byte[1024*1024];


    private Hungry(){//单列模式最重要就是构造器私有

    }

    private final static Hungry HUNGRY = new Hungry();//上来就直接new一个,加载对象

    public static Hungry getInstance(){
        return HUNGRY;
    }

}

DCL懒汉式单例

在这里插入图片描述

volatile避免指令重排

package com.liang.single;

//懒汉式单例
public class LazyMan {

    private LazyMan(){//单例模式最重要的就是构造器私有
        System.out.println(Thread.currentThread().getName()+"ok");
    }
    private volatile static LazyMan lazyMan;**//必须在这里加上一个volatile,这才是一个完整的双重监测锁模式
    //volatile避免指令重排**

    public static LazyMan getInstance(){
       //加锁  双重监测锁模式的  懒汉式单例  DCL懒汉式
        if (lazyMan==null){
            synchronized (LazyMan.class){
                if (lazyMan==null){
                    lazyMan = new LazyMan();//不是一个原子性操作
                   
            }
        }

        }
        return lazyMan;
    }
    /**
     * 1.分配内存空间
     * 2.执行构造方法,初始化对象
     * 3.把对象指向这个空间
     *
     *123  希望
     *132  万一 A
     * B //此时lazyman还没完成构造
     *
     */
    
    
//多线程的情况下,就出问题了
    public static void main(String[] args){
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                LazyMan.getInstance();
            }).start();

        }
    }


}
这样输出就是一个线程了

反射! 只要有反射 任何代码都不安全 任何私有关键字都是纸老虎
使用反射破坏单例
那该如何破解反射呢?
答:再在上面加一个锁,三重锁
在这里插入图片描述
在这里插入图片描述

  private LazyMan() {//单例模式最重要的就是构造器私有
        synchronized (LazyMan.class){    //再在上面加一个锁,三重锁
            if (lazyMan!=null){
                throw new RuntimeException("不要试图使用反射破坏异常!");
            }
        }
package com.liang.single;

import java.lang.reflect.Constructor;

//懒汉式单例
public class LazyMan {

    private LazyMan() {//单例模式最重要的就是构造器私有
        synchronized (LazyMan.class){    //再在上面加一个锁,三重锁
            if (lazyMan!=null){
                throw new RuntimeException("不要试图使用反射破坏异常!");
            }
        }


        System.out.println(Thread.currentThread().getName() + "ok");
    }

    private volatile static LazyMan lazyMan;//必须在这里加上一个volatile,这才是一个完整的双重监测锁模式
    //volatile避免指令重排

    public static LazyMan getInstance() {
        //加锁  双重监测锁模式的  懒汉式单例  DCL懒汉式
        if (lazyMan == null) {
            synchronized (LazyMan.class) {
                if (lazyMan == null) {
                    lazyMan = new LazyMan();//不是一个原子性操作

                }
            }

        }
        return lazyMan;
    }

    //反射!  只要有反射 任何代码都不安全 任何私有关键字都是纸老虎
    //使用反射破坏单例
    public static void main(String[] args) throws Exception {
        LazyMan instance = LazyMan.getInstance();
        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);//空参构造器
        declaredConstructor.setAccessible(true);//无视私有构造器
        LazyMan instance2 = declaredConstructor.newInstance();

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





    /**
     * 1.分配内存空间
     * 2.执行构造方法,初始化对象
     * 3.把对象指向这个空间
     *
     *123  希望
     *132  万一 A
     * B //此时lazyman还没完成构造
     *
     */


//多线程的情况下,就出问题了
//    public static void main(String[] args){
//        for (int i = 0; i < 10; i++) {
//            new Thread(()->{
//                LazyMan.getInstance();
//            }).start();
//
//        }
//    }


}

静态内部类

package com.liang.single;

//静态内部类
public class Holder {
 private  Holder(){//单例模式最重要的就是构造器私有

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


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

单例不安全,反射

枚举 enum

什么是枚举?

答:enum是一个什么?jdk1.5之后就有了 枚举本身也是一个class类 //反射不能破坏枚举

枚举类型的最终反编译源码
在这里插入图片描述
在这里插入图片描述

反射中采用newInstance方法废弃,可采用getDeclaredConstructor().newInstance()方法来得到类对象

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Java中的战斗机

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

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

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

打赏作者

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

抵扣说明:

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

余额充值