单例模式

只要是单例模式一定要构造器私有

1.饿汉模式

package com.mahui.single;


//饿汉式单例  可能会浪费资源
public class HungryDemo {

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

    }
    //饿汉式一上来先把这个对象加载
    private final static  HungryDemo HUNGRY_DEMO =new HungryDemo();

    public static HungryDemo getInstance(){
        return HUNGRY_DEMO;
    }


}

2.懒汉模式:

1.需要双重检测锁

2.需要用volatile进行修饰对象

package com.mahui.single;


//懒汉式单例
public class LazyManDemo {
    //构造器私有
    private LazyManDemo(){
        System.out.println(Thread.currentThread().getName()+"ok");
    }
    //避免进行指令重排 用volatile进行修饰
    private volatile static LazyManDemo lazyManDemo;

    //这是有问题的在单线程下是可以得在多线程中会出现问题 需要加锁
    //双重检测锁模式的懒汉式单例  DCL懒汉式
    public static LazyManDemo getInstance(){
        if(lazyManDemo==null){
            //加锁 在锁中在判断一次即可,锁的是class只有一个
            synchronized (LazyManDemo.class){
                if(lazyManDemo==null){
                    //这个过程在极端情况下是有一定的问题,它不是原子性操作
                    /*底层是三层操作
                    * 1.分配内存空间
                    * 2.执行构造方法,初始化对象
                    * 3.把这个对象指向这个空间
                    * 期望进行123执行顺序
                    * 也可能是132执行顺序
                    * 若线程A先进来执行的是13还没执行2完成构造
                    *这时线程B进来了,以为已经完成构造直接返回lazyManDemo但是其仍为null
                    *底层会进行指令重排
                    * 为了避免指令重拍必须将lazyManDemo用volatile进行修饰
                    *
                    * */
                    lazyManDemo=new LazyManDemo();
                }
            }

        }
        return lazyManDemo;
    }
}

第二个问题:

通过反射可以破坏构造器,会创建两个不同的对象

package com.mahui.single;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class LazyManDemo02{
    //构造器私有
    private LazyManDemo02(){
        System.out.println(Thread.currentThread().getName()+"ok");
    }
    //避免进行指令重排 用volatile进行修饰
    private volatile static LazyManDemo02 lazyManDemo02;

    //这是有问题的在单线程下是可以得在多线程中会出现问题 需要加锁
    //双重检测锁模式的懒汉式单例  DCL懒汉式
    public static LazyManDemo02 getInstance(){
        if(lazyManDemo02==null){
            //加锁 在锁中在判断一次即可,锁的是class只有一个
            synchronized (LazyManDemo.class){
                if(lazyManDemo02==null){
                    //这个过程在极端情况下是有一定的问题,它不是原子性操作
               
                    lazyManDemo02=new LazyManDemo02();
                }
            }
        }
        return lazyManDemo02;
    }
    //反射
    public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
        LazyManDemo02 lazyManDemo02 = LazyManDemo02.getInstance();
        Constructor<LazyManDemo02> declaredConstructor =    LazyManDemo02.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        LazyManDemo02 instance = declaredConstructor.newInstance();
        //如果被破坏了这两个对象是不一样的
        System.out.println(lazyManDemo02);
        System.out.println(instance);
    }
}

运行结果:

在这里插入图片描述

解决方案:

反射是破坏了构造器,在构造器中添加判断可以解决

private LazyManDemo02(){
    //在构造器上加一把锁
    synchronized (LazyManDemo02.class){
        if(lazyManDemo02!=null){
            //如果不为空就抛出异常
            throw new RuntimeException("不要试图使用反射破坏异常");
        }

    }

但是两个对象都是通过反射创建,同样会出现两个对象

解决方案:

设置第三方变量来判断在构造器中判断

private static boolean flag=false;
//构造器私有
private LazyManDemo02(){
    //在构造器上加一把锁
    synchronized (LazyManDemo02.class){
        if(flag == false){
            flag=true;
        }else {
            //如果不为空就抛出异常
            throw new RuntimeException("不要试图使用反射破坏异常");
        }

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

但是可以通过反射来该变字段的值:所以这是一个很大的问题

如何解决这问题,其实可以使用枚举类型。

package com.mahui.single;

import java.lang.reflect.Constructor;

public enum  EnumDemo {
    INSTACE;

    public EnumDemo getInstace(){
        return INSTACE;
    }
}
class Test{
    public static void main(String[] args) throws Exception {

        EnumDemo instance1 = EnumDemo.INSTACE;
        //通过反射去获取对象
        Constructor<EnumDemo> declaredConstructor = EnumDemo.class.getDeclaredConstructor(null);

        declaredConstructor.setAccessible(true);
        EnumDemo instance2 = declaredConstructor.newInstance();
        System.out.println(instance1);
        System.out.println(instance2);
    }
}

在源码中看到该类有一个无参构造

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.mahui.single;

public enum EnumDemo {
    INSTACE;
	//源码中是有一个无参构造的
    private EnumDemo() {
    }

    public EnumDemo getInstace() {
        return INSTACE;
    }
}

但是运行结果显示该类没有无参构造器

java.lang.NoSuchMethodException: com.mahui.single.EnumDemo.()

在这里插入图片描述

但是在反射的newInstance()方法中枚举会抛出异常"Cannot reflectively create enum objects"但是并没有抛出异常"Cannot reflectively create enum objects"输出了异常没有空参构造,没有达到预期的效果

在这里插入图片描述

分析出现这样的原因:

反编译EnumDemo.class文件

在这里插入图片描述

使用jad反编译器将class文件编译为java文件

在这里插入图片描述

反编译的java文件

在这里插入图片描述

查看其java文件:枚举类型的最终反编译源码

在这里插入图片描述

通过有参构造器创建对象传入参数(String s, int i)

package com.mahui.single;

import java.lang.reflect.Constructor;

public enum  EnumDemo {
    INSTACE;

    public EnumDemo getInstace(){
        return INSTACE;
    }
}
class Test{
    public static void main(String[] args) throws Exception {

        EnumDemo instance1 = EnumDemo.INSTACE;
        Constructor<EnumDemo> declaredConstructor = EnumDemo.class.getDeclaredConstructor(String.class,int.class);

        declaredConstructor.setAccessible(true);
        EnumDemo instance2 = declaredConstructor.newInstance();
        System.out.println(instance1);
        System.out.println(instance2);
    }
}

运行结果正常抛出异常:不能通过反射创建枚举对象

java.lang.IllegalArgumentException: Cannot reflectively create enum objects

在这里插入图片描述

3.静态内部类:



//静态内部类
//不安全的
public class Holder {
    //构造器私有
    private Holder(){

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

    }    public static class InnerClass{

        private static final Holder HOLDER=new Holder();
    }
}

总结:

单例不安全,原因是有反射,引入枚举来创建单例

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值