单例模式是最简单的也是设计模式系列书籍开篇第一个讲到的模式,在平时的开发中也常常用它来保证获取的都是同一个实例。html
定义:确保某一个类只有一个实例,并且自行实例化并向整个系统提供这个实例。java
public class HungrySingleton { private static final HungrySingleton singleton = new HungrySingleton(); //限制外部产生HungrySingleton对象 private HungrySingleton(){ } //向外提供获取示例的静态方法 public static HungrySingleton getInstance() { return singleton; } //other methods }
饿汉模式是类加载时候就建立对象,利用了jvm特性保证了线程的安全性。linux
1 public class LazySingleton { 2 private static volatile LazySingleton singleton = null; 3 4 private LazySingleton() { } 5 6 public static LazySingleton getSingleton() { 7 if (singleton == null) { //不用每次获取对象都强制加锁,为了提高了效率 8 synchronized (LazySingleton.class) { 9 if (singleton == null) { 10 singleton = new LazySingleton(); 11 } 12 } 13 } 14 return singleton; 15 } 16 }
内部类有用static修饰和不用static修饰的内部类:git
public class Singleton2 { /** * 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例 * 没有绑定关系,并且只有被调用到时才会装载,从而实现了延迟加载。 */ private static class Singleton2Holder { /** * 静态初始化器,由JVM来保证线程安全 */ private static Singleton2 singleton = new Singleton2(); } private Singleton2() { //System.out.println("singleton2 private construct method called"); } public static Singleton2 getSingleton() { //System.out.println("singleton2 getSingleton method called"); return Singleton2Holder.singleton; } private String name; public void desc() { //System.out.println("singleton2 desc method called"); } }
是饿汉模式的变种形式,利用jvm加载保证线程安全,而且实现了懒加载,只在获取实例的时候才去建立。github
使用的是饿汉模式json
/**饿汉模式——反射建立对象*/ Class<HungrySingleton> singletonClass = HungrySingleton.class; Constructor<HungrySingleton> singletonConstructor = singletonClass.getDeclaredConstructor(); singletonConstructor.setAccessible(true); /**先反射建立*/ HungrySingleton hungrySingleton = singletonConstructor.newInstance(); /**再经过单例模式获取实例*/ HungrySingleton instance = HungrySingleton.getInstance(); System.out.println(hungrySingleton); System.out.println(instance);
经过反射修改构造函数能够被访问,经过反射构造的结果和单例模式获取的不是同一个对象。设计模式
HungrySingleton instance = HungrySingleton.getInstance(); jsonString = JSON.toJSONString(instance); HungrySingleton singleton = JSON.parseObject(jsonString, HungrySingleton.class); System.out.println(instance == singleton);
反射经过调用构造函数来建立对象,若是在构造函数里抛出异常,就能够组织反射建立对象(这种方式不适用与懒汉模式)。缓存
public class HungrySingleton { private static final HungrySingleton singleton = new HungrySingleton(); //限制外部产生Singleton对象 private HungrySingleton() { if (singleton != null) { throw new RuntimeException("不容许建立对象!"); } System.out.println("singleton private construct method called"); } //向外提供获取示例的静态方法 public static HungrySingleton getInstance() { System.out.println("create singleton instance"); return singleton; } }
再用反射建立对象会报错安全
/**饿汉模式——反射建立对象*/ Class<HungrySingleton> singletonClass = HungrySingleton.class; Constructor<HungrySingleton> singletonConstructor = singletonClass.getDeclaredConstructor(); singletonConstructor.setAccessible(true); /**先反射建立*/ HungrySingleton hungrySingleton = singletonConstructor.newInstance(); /**再经过单例模式获取实例*/ HungrySingleton instance = HungrySingleton.getInstance(); System.out.println(hungrySingleton); System.out.println(instance);
懒汉模式仍然会被破坏(当反射先于懒汉模式建立对象时,仍然会建立多个对象)jvm
/**懒汉模式——反射建立对象*/ Class<LazySingleton> lazySingletonClass = LazySingleton.class; Constructor<LazySingleton> lazySingletonConstructor = lazySingletonClass.getDeclaredConstructor(); lazySingletonConstructor.setAccessible(true); /**先经过反射获取实例*/ LazySingleton lazySingleton = lazySingletonConstructor.newInstance(); /**再经过单例模式获取实例*/ LazySingleton lazyInstance = LazySingleton.getInstance(); System.out.println(lazySingleton); System.out.println(lazyInstance);
懒汉模式仍然会建立两个对象:
singleton.LazySingleton@593634ad
singleton.LazySingleton@20fa23c1
若是是使用ObjectInputStream方式序列化,可使用readResolve方法来控制。但序列化的方法有不少种,这种方式并不可靠。
《effective java》第77条:对于实例控制,枚举类型优于readResolve。
如下是一个枚举单例的示例
public enum EnumSingleton { INSTANCE; public String getDesc() { return "desc"; } public static void process() { System.out.println("static process method"); } private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } }
使用时候直接获取 EnumSingleton.INSTANCE 便可获取当前实例,并且序列化与反序列化不会建立对象。
/**枚举获取实例*/ EnumSingleton instance = EnumSingleton.INSTANCE; instance.setName("this is a enum singleton"); instance.setAge(28); System.out.println("instance.name:"+instance.getName()+", instance.age:"+instance.getAge()); /**序列化*/ String jsonString = JSON.toJSONString(instance); /**反序列化建立对象*/ EnumSingleton serializerInstance = JSON.parseObject(jsonString, EnumSingleton.class); System.out.println(instance == serializerInstance); Class<EnumSingleton> enumSingletonClass = EnumSingleton.class; Constructor<EnumSingleton> enumSingletonConstructor = enumSingletonClass.getDeclaredConstructor(); enumSingletonConstructor.setAccessible(true); /**反射建立*/ EnumSingleton enumSingleton = enumSingletonConstructor.newInstance(); System.out.println(enumSingleton);
输出:
instance.name:this is a enum singleton, instance.age:28
true
java.lang.NoSuchMethodException: singleton.EnumSingleton.<init>()
at java.lang.Class.getConstructor0(Class.java:3082)
总结,实现单例模式的惟一推荐方法,使用枚举类来实现。
guava里有个Suppliers提供了memoize方法,用法以下:
Suppliers.memoize(new Supplier<Object>() { @Override public Object get() { return new Demo(); } });
查看其实现源码,将传入的Suppliers做为代理传给MemoizingSupperlizer,返回一个类型为MemoizingSupperlizer类型的Supperlier对象;若是不是MemoizingSupperlizer类型,建立一个MemoizingSupperlizer实例返回:
MemoizingSupperlizer内部get方法使用double-check方式实现了只执行一次建立对象方法
一样的还有 ExpiringMemoizingSupplier 方法,支持过时时间只有再次调用get方法建立对象(能够用来实现缓存)
《设计模式之禅》https://www.cnblogs.com/shangxinfeng/p/6754345.htmlhttps://www.cnblogs.com/ttylinux/p/6498822.html?utm_source=itdadao&utm_medium=referralhttps://blog.csdn.net/hintcnuie/article/details/17968261https://www.cnblogs.com/ldq2016/p/6627542.html