一、什么是单例?
二、什么场景下需要用到单例?
三、怎么实现一个单例模式?
提供一个该类的私有静态变量用来保存这个唯一的实例
私有化构造方法,禁止外部通过new关键字调用
提供一个公共的静态方法供外部获取私有的静态变量
public class LazySingleton{private static LazySingleton instance;/*** 私有化构造方法,禁止外部调用*/private LazySingleton() {}/*** 获取对象实例* @return*/public static LazySingleton getInstance(){if (instance == null) {instance = new LazySingleton();}return instance;}}
改造1:加synchronized关键字
public synchronized static LazySingleton getInstance(){if (instance == null) {instance = new LazySingleton();}return instance;}
改造2:双重校验锁
public static LazySingleton getInstance(){if (instance == null) {synchronized (LazySingleton.class) {if (instance == null) {instance = new LazySingleton();}}}return instance;}
memory = allocate();//1.分配对象内存空间
instance(memory);//2.初始化对象
instance = memory;//3.设置instance指向刚分配的内存地址,此时 instance != null
memory=allocate();//1.分配对象内存空间
instance=memory;//3.设置instance指向刚分配的内存地址,此时instance != null,但是对象还没有初始化完成!
instance(memory);//2.初始化对象
改造3:将变量用volatile修饰
public class LazySingleton{*** 用volatile关键字禁止构造对象时候的指令重排*/private volatile static LazySingleton instance;*** 私有化构造方法,禁止外部调用*/private LazySingleton() {}*** 获取对象实例* @return*/public static LazySingleton getInstance(){if (instance == null) {synchronized (LazySingleton.class) {if (instance == null) {instance = new LazySingleton();}}}return instance;}public static void main(String[] args) {LazySingleton.getInstance();}}
public static void main(String[] args) throws Exception {//利用反射调用构造方法创建对象Class<LazySingleton> classType = LazySingleton.class;//获取无参构造Constructor<LazySingleton> constructor = classType.getDeclaredConstructor(null);accessible属性设置为true就可以调用私有的构造函数,可以说是开挂了constructor.setAccessible(true);instance = LazySingleton.getInstance();LazySingleton instance2 = constructor.newInstance();System.out.println(instance == instance2);}

改造4:在私有构造中校验对象是否已经被创建
/*** 私有化构造方法,禁止外部调用*/private LazySingleton() {if (instance != null) {throw new RuntimeException("又想开挂?");}}

public static void main(String[] args) throws Exception {//利用反射调用构造方法创建对象Class<LazySingleton> classType = LazySingleton.class;//获取无参构造Constructor<LazySingleton> constructor = classType.getDeclaredConstructor(null);accessible属性设置为true就可以调用私有的构造函数,可以说是开挂了constructor.setAccessible(true);// instance = LazySingleton.getInstance();LazySingleton instance2 = constructor.newInstance();LazySingleton instance3 = constructor.newInstance();System.out.println(instance3 == instance2);}

改造5:设置一个标志,用来标识构造方法是否被调用过
/*** 设置对象实例化的标志*/private static boolean init = false;/*** 私有化构造方法,禁止外部调用*/private LazySingleton() {synchronized (LazySingleton.class) {if (!init) {init = true;}else {throw new RuntimeException("小样,我还治不了你了");}}}

public static void main(String[] args) throws Exception {利用反射调用构造方法创建对象Class<LazySingleton> classType = LazySingleton.class;获取无参构造Constructor<LazySingleton> constructor = classType.getDeclaredConstructor(null);accessible属性设置为true就可以调用私有的构造函数,可以说是开挂了constructor.setAccessible(true);//在初始化instance前先反射调用一下LazySingleton instance2 = constructor.newInstance();System.out.println("instance2为:" + instance2);//你会发现instance永远初始化不了了instance = LazySingleton.getInstance();System.out.println(instance);}

package com.example.demo.designPatterns;import java.lang.reflect.Constructor;/*** 懒汉式单例* 只有在调用getInstance方法的时候,才会去创建*/public class LazySingleton{/*** 用volatile关键字禁止构造对象时候的指令重排*/private volatile static LazySingleton instance;/*** 设置对象实例化的标志*/private static boolean init = false;/*** 私有化构造方法,禁止外部调用*/private LazySingleton() {synchronized (LazySingleton.class) {if (!init) {init = true;}else {throw new RuntimeException("小样,我还治不了你了");}}}/*** 获取对象实例* @return*/public static LazySingleton getInstance(){if (instance == null) {synchronized (LazySingleton.class) {if (instance == null) {instance = new LazySingleton();}}}return instance;}public static void main(String[] args) throws Exception {//利用反射调用构造方法创建对象Class<LazySingleton> classType = LazySingleton.class;//获取无参构造Constructor<LazySingleton> constructor = classType.getDeclaredConstructor(null);accessible属性设置为true就可以调用私有的构造函数,可以说是开挂了constructor.setAccessible(true);LazySingleton instance2 = constructor.newInstance();System.out.println("instance2为:" + instance2);instance = LazySingleton.getInstance();System.out.println(instance);}}
package com.example.demo.designPatterns;import java.lang.reflect.Constructor;/*** 懒汉式单例模式2* 基于静态内部类实现*/public class InnerClassSingleton {*** 静态内部类*/private static class InnerClassHolder{private static InnerClassSingleton instance = new InnerClassSingleton();}/*** 私有化构造方法,禁止new关键字调用*/private InnerClassSingleton() {if (InnerClassHolder.instance != null) {throw new RuntimeException("小样,你又来了?");}}/*** 提供一个方法用来获取实例* @return*/public static InnerClassSingleton getInstance(){return InnerClassHolder.instance;}public static void main(String[] args) throws Exception {//利用反射调用构造方法创建对象Class<InnerClassSingleton> classType = InnerClassSingleton.class;//获取无参构造Constructor<InnerClassSingleton> constructor = classType.getDeclaredConstructor(null);//accessible属性设置为true就可以调用私有的构造函数,可以说是开挂了constructor.setAccessible(true);try {InnerClassSingleton instance2 = constructor.newInstance();}catch (Exception e) {e.printStackTrace();}InnerClassSingleton instance = InnerClassSingleton.getInstance();System.out.println("instance:" + instance);}}

外部类初次加载时不会加载静态内部类
类只会加载一次,不用考虑线程安全问题
加载InnerClassHolder静态内部类,由于类的加载机制保证线程安全
加载InnerClassHolder的过程中会初始化instance,所以在和null比较之前,已经初始化好了instance,调用getInstance时只是获取了初始化好的instance实例
package com.example.demo.designPatterns;import java.lang.reflect.Constructor;/*** 饿汉式单例*/public class HungrySingleton {private static HungrySingleton instance = new HungrySingleton();/*** 私有化构造禁止外部调用*/private HungrySingleton() {if (instance != null){throw new RuntimeException("你破解不了的");}}public static HungrySingleton getInstance(){return instance;}public static void main(String[] args) throws NoSuchMethodException {//利用反射调用构造方法创建对象Class<HungrySingleton> classType = HungrySingleton.class;//获取无参构造Constructor<HungrySingleton> constructor = classType.getDeclaredConstructor(null);//accessible属性设置为true就可以调用私有的构造函数,可以说是开挂了constructor.setAccessible(true);try {HungrySingleton instance2 = constructor.newInstance();System.out.println("instance2:" + instance2);}catch (Exception e) {e.printStackTrace();}HungrySingleton instance = HungrySingleton.getInstance();System.out.println("instance:" + instance);}}

package com.example.demo.designPatterns;public enum EnumSingleton {INSTANCE;public static void main(String[] args) {System.out.println(EnumSingleton.INSTANCE.hashCode());}}
第一次真正被用到的时候,才会被虚拟机加载并初始化
类加载过程是由jvm保证线程安全的
天然不支持反射
不能被继承
属性必须在创建时指定, 相当于不能延迟加载
占用的内存空间是静态变量的2倍多
文章转载自Java Miraculous,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




