只要是单例模式,构造器一定私有化,即用private修饰。

一.饿汉式单例

package 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() {}

    //实例化私有并且用final修饰
    private final static Hungry HUNGRY = new Hungry();

    public static Hungry getInstance(){
        return HUNGRY;
    }
}

二.懒汉式单例

package single;

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

//懒汉式单例
public class LazyMan {

    //红绿灯标志位,不让反射获取对象,反射通过构造器来获取对象
    private static boolean blog = false;

    //构造器私有
    private LazyMan() {
        synchronized (LazyMan.class) {
            if (blog == false) {
                blog = true;
            }else {
                throw new RuntimeException("不要试图破坏单例");
            }
        }
    }

    //实例化私有
    private volatile static LazyMan lazyMan;

    //双重检验锁模式 懒汉式单例 简称DCL懒汉式
    public static LazyMan getInstance() {
        if (lazyMan == null) {
            synchronized (LazyMan.class) {
                if (lazyMan == null) {
                    lazyMan = new LazyMan(); // 实例化对象不能够保证原子性,所以加上了volatile关键字,不会产生指令重排
                    /**
                     * 对象的基本创建过程
                     * 1.分配内存空间
                     * 2.执行构造方法,初始化对象
                     *
                     * 123
                     * 132 A线程
                     *     B线程指向A的地址空间,产生了虚无操作,但是lazyMan还没有完成构造
                     */
                }
            }
        }
        return lazyMan;
    }

    //反射
    public static void main(String[] args) throws Exception {
//        LazyMan instance = LazyMan.getInstance();
        Field blog1 = LazyMan.class.getDeclaredField("blog");
        blog1.setAccessible(true);
        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        LazyMan instance = declaredConstructor.newInstance();
        blog1.set(instance,false);
        LazyMan instance1 = declaredConstructor.newInstance();
        blog1.set(instance1,false);
        System.out.println(instance);
        System.out.println(instance1);
    }
}

三.静态内部类单例

package single;

//静态内部类
public class Holder {

    private Holder() {

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

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

}

四.枚举单例

package single;

import java.lang.reflect.Constructor;

/*
    枚举的class文件中确实有无参构造器,但是通过jad反编译成java文件,
    发现里面有String和int的有参构造,通过无参无法抛出"Cannot reflectively create enum objects"
 */

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> constructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
        constructor.setAccessible(true);
        EnumSingle instance2 = constructor.newInstance();
        System.out.println(instance1);
        System.out.println(instance2);
    }
}

  • 因为反射不能破解枚举单例
  • 并且尝试通过反射来破坏枚举单例,抛出异常却不是上述图片
    Constructor<EnumSingle> constructor = EnumSingle.class.getDeclaredConstructor(null);参数为null是因为,在.class文件里,我们发现枚举有无参构造
  • 然后我们得到了不一样的异常
    它提示没有这个空参的构造方法
  • 我们进入.class文件,通过命令:javap -p EnumSingle.class 通过反编译得到.java文件的表现形式
  • 既然反编译解决不了这个问题,我们需要通过更专业的工具jad.exe得到他的.java文件,一探究竟
    输入cmd,在dos窗口输入:jad -sjava EnumSingle.class 得到EnumSingle.java文件,打开.java文件发现枚举有一个是有参构造,而不是无参构造
  • 一个类型为String,另外一个为int,然后通过反射来试图破解枚举单例

  • 这次的报错,跟之前通过反射的信息是一致的了