为什么叫做饿汉式,意思是很饥饿,那么就会一开始就准备好,以防以后吃不饱,名字由此而来。代码以下java
class HUNGRYMAN{ //这里实例化方法要设置成私有的,以防外部直接new对象,破坏单例 private HUNGRYMAN(){ } //这里即为一开始就建立好对象,须要调用的时候,直接返回,不须要新建立 private static HUNGRYMAN INSTANCE = new HUNGRYMAN(); //调用方法返回实例对象 public static HUNGRYMAN getInstance(){ return INSTANCE; } } class Main{ public static void main(String[] args) { //咱们在这里采用哈希值的方式来判断是否相等 HUNGRYMAN instance1 = HUNGRYMAN.getInstance(); HUNGRYMAN instance2 = HUNGRYMAN.getInstance(); System.out.println("instance1的哈希值为" + instance1.hashCode() + " " + "instance2的哈希值为" + instance2.hashCode()); System.out.println("二者是否相等:" + (instance1 == instance2)); } } //输出:instance1的哈希值为366712642 instance2的哈希值为366712642 //二者是否相等:true
因为上述的状况,诞生了懒汉式:数组
与饿汉式相对,懒汉式只有在须要使用的时候才会建立,也就是说能够实现“延时建立”,代码以下:安全
class LAZYMAN{ //一样,这里的实例方法也须要设置成私有的,防止其余类破坏单例 private LAZYMAN(){ } //这里定义一个LAZYMAN类型的变量instance,可是并无建立对象 private static LAZYMAN INSTANCE; //在执行getInstance方法的时候才会建立对象 public static LAZYMAN getInstance(){ //判空时初始化,不然直接返回对象 if(INSTANCE == null){ INSTANCE = new LAZYMAN(); } return INSTANCE; } } class Main{ public static void main(String[] args) { LAZYMAN instance1 = LAZYMAN.getInstance(); LAZYMAN instance2 = LAZYMAN.getInstance(); System.out.println("instance1的哈希值为" + instance1.hashCode() + " " + "instance2的哈希值为" + instance2.hashCode()); System.out.println("二者是否相等:" + (instance1 == instance2)); } } //输出:instance1的哈希值为366712642 instance2的哈希值为366712642 //输出:二者是否相等:true
由此产生了双重校验锁(DCL懒汉式)的单例模式实现方式并发
线程安全的懒汉模式,直接来看代码:线程
class LAZYMAN{ private LAZYMAN(){ } //这里加入volatile,是为了防止实例对象建立的时候出现指令重排 /* 对象建立过程(非原子性):1.分配内存空间 2.执行构造方法,初始化对象 3.将对象指向这个内存空间。这里若是没有使用volatile修饰的话,会致使 执行顺序不是123,多是132,致使对象建立尚未彻底建立完毕,可是外部 执行到if(instance == null)这里的时候,已经不为空了,会直接返回,致使 返回版初始化状态的实例,发生错误。 */ private static volatile LAZYMAN INSTANCE; //在执行getInstance方法的时候才会建立对象 public static LAZYMAN getInstance(){ //若是对象没有建立过,先把整个class锁住,等到第一个拿到锁的线程释放以后其余的线程在继续执行 if(INSTANCE == null){ synchronized(LAZYMAN.class){ if(INSTANCE == null){ INSTANCE = new LAZYMAN(); } } } return INSTANCE; } } class Main{ public static void main(String[] args) { LAZYMAN instance1 = LAZYMAN.getInstance(); LAZYMAN instance2 = LAZYMAN.getInstance(); System.out.println("instance1的哈希值为" + instance1.hashCode() + " " + "instance2的哈希值为" + instance2.hashCode()); System.out.println("二者是否相等:" + (instance1 == instance2)); } }
关于 volatile 和 synchronized ,在另外的文章中会探讨。
这里说一下如何经过反射来破坏:code
class Main{ public static void main(String[] args) throws Exception { LAZYMAN instance1 = LAZYMAN.getInstance(); Constructor<LAZYMAN> constructor = LAZYMAN.class.getDeclaredConstructor(); //无视私有构造器 constructor.setAccessible(true); //直接建立 LAZYMAN instance2 = constructor.newInstance(); System.out.println("instance1的哈希值为" + instance1.hashCode() + " " + "instance2的哈希值为" + instance2.hashCode()); System.out.println("二者是否相等:" + (instance1 == instance2)); } } //输出:instance1的哈希值为366712642 instance2的哈希值为1829164700 //二者是否相等:false
class INNERSINGLE{ private INNERSINGLE(){ } //内部类,调用的时候才进行加载 private static class SINGLEINNNER{ private static INNERSINGLE instance = new INNERSINGLE(); } //调用方法:getInstance() public static INNERSINGLE getInstance(){ return SINGLEINNNER.instance; } } class Main{ public static void main(String[] args) throws Exception { INNERSINGLE instance1 = INNERSINGLE.getInstance(); INNERSINGLE instance2 = INNERSINGLE.getInstance(); System.out.println("instance1的哈希值为" + instance1.hashCode() + " " + "instance2的哈希值为" + instance2.hashCode()); System.out.println("二者是否相等:" + (instance1 == instance2)); } } //输出:instance1的哈希值为366712642 instance2的哈希值为366712642 //二者是否相等:true
枚举类默认是单例模式的对象
public enum ENUMSINGLE{ INSTANCE; public ENUMSINGLE getInstance(){ return INSTANCE; } } class Main{ public static void main(String[] args) throws Exception { ENUMSINGLE instance1 = ENUMSINGLE.INSTANCE; ENUMSINGLE instance2 = ENUMSINGLE.INSTANCE; System.out.println("instance1的哈希值为" + instance1.hashCode() + " " + "instance2的哈希值为" + instance2.hashCode()); System.out.println("二者是否相等:" + (instance1 == instance2)); }