单例模式(singleton)有一下三个要点
为了保证单例类的实例只能由单例类自己创建,需要将单例类的构造方法设置成private私有,防止构造方法被其他类调用
单例模式有饿汉式、懒汉式等实现方式,下面一一做介绍
饿汉式在类加载的时候就会创建static的实例
- public class Eager {
- private static final Eager instance = new Eager();
-
- private Eager() {
-
- }
-
- public static Eager getInstance() {
- return instance;
- }
- }
懒汉式将实例的创建延后到第一次使用的时候,这时候instance是空的,就创建出来,后面使用就不用再创建
这里需要注意的是getInstance()方法中使用了synchronized关键字,保证该方法只能同步调用,防止在多线程环境下可能导致的创建出多个实例的情况
- public class Lazy {
- private static Lazy instance;
-
- private Lazy() {
-
- }
-
- public synchronized static Lazy getInstance() {
- if (instance == null) {
- instance = new Lazy();
- }
- return instance;
- }
- }
懒汉式还有如下双重校验锁的写法
至于为什么要用volatile关键字,说来话长,以后单独开一篇聊
- public class Lazy2 {
- private volatile static Lazy2 instance;
- private Lazy2 (){}
- public static Lazy2 getInstance() {
- if (instance == null) {
- synchronized (Lazy2.class) {
- if (instance == null) {
- instance = new Lazy2();
- }
- }
- }
- return instance;
- }
- }
这种方式其实也可以归为懒汉式,因为是等到第一次使用的时候才创建对象
- public class Lazy3 {
- private static class InstanceHolder {
- private static Lazy3 instance = new Lazy3();
- }
-
- private Lazy3() {
-
- }
-
- public static Lazy3 getInstance() {
- return InstanceHolder.instance;
- }
- }
由于Lazy3是InstanceHolder的类成员变量,因此在JVM调用InstanceHolder类的类构造器对其进行初始化时,虚拟机会保证一个类的类构造器在多线程环境中被正确的加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的类构造器,其他线程都需要阻塞等待,直到活动线程执行方法完毕。
JVM会保障多线程情况下类的正确初始化,所以借助这一点,我们创建一个内部类,然后让内部类初始化的时候创建唯一个实例作为成员变量
直接上代码
父类的代码
- package com.himmy.gof.singleton;
-
- import java.util.HashMap;
- import java.util.Map;
-
- public class RegSingleton {
- private static Map
mRegister = new HashMap<>(); -
- static {
- RegSingleton reg = new RegSingleton();
- mRegister.put(reg.getClass().getName(), reg);
- }
-
- protected RegSingleton() {
-
- }
-
- public static RegSingleton getInstance(String name) {
- if (name == null) {
- name = "com.himmy.gof.singleton.RegSingleton";
- }
- if (mRegister.get(name) == null) {
- try {
- mRegister.put(name, (RegSingleton) Class.forName(name).newInstance());
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- return mRegister.get(name);
- }
-
- public String about() {
- return "我是你爸爸";
- }
- }
子类的代码
- package com.himmy.gof.singleton;
-
- public class RegSingletonChild extends RegSingleton {
-
- public RegSingletonChild() {
-
- }
-
- public static RegSingletonChild getInstance() {
- return (RegSingletonChild) RegSingleton.getInstance("com.himmy.gof.singleton.RegSingletonChild");
- }
-
- @Override
- public String about() {
- return "我是我爸爸的儿子";
- }
- }
登记式单例比较少见,在GoF的《设计模式》一种中有提到该实现方式
但是我个人不是很理解这种实现方式,不管是父类RegSingleton还是子类RegSingletonChild其构造方法都不是private,所以都不能保证在整个系统中只有一个唯一的实例,而这也就违反了单例模式的第一条要求“某个类只能有一个实例”
有深入了解的朋友可以在评论里讨论下