单例模式:大家应该都不陌生,就是保证jvm进程里一个类只会对应一个实例对象。有很多方法可以实现单例模式,但是哪一种是最安全的,不能通过非法的手段,创建多个实例对象,比如通过反射,new的方式创建多个。我们今天就来学习一下。
一.禁止new创建多个实例
通过把构造函数的修饰符设置为private, 禁止通过new的方法实例化,只能调用提供的方法获取到实例对象,确保获取到的实例对象都是同一个,这样来保证单例。
饿汉式
Singleton 构造函数是private的,Singleton类在加载完成就会实例化,所以即使没有使用到这个类,也会进行实例化。优点是比较简单,线程安全,但是缺点也很明显如果这个类实例化比较耗时,在类加载的时间就会很长,如果占用内存,即使没有用到这个实例也会占用大量内存。
public class Singleton { //静态代码块 //私有化构造器 private Singleton() {} //内部创建对象实例 private static Singleton instance; static { // 在静态代码块中,创建单例对象 instance = new Singleton(); } //对外公有的静态方法 public static Singleton getInstance() { return instance; } }
懒汉式
饿汉式是需要的时候才创建实例对象,一般结合双重检查来实现,否则会导致线程不安全,就是多个线程调用getInstance方法时,可能会获取到不同的实例对象。需要注意的是Singleton对象还需要用volatile来修饰,这样才能避免指令重排,指令重排可能会导致获取到的Singleton对象还没有实例化完成。
public class Singleton { //双重检查 private static volatile Singleton instance; private Singleton() {} public static synchronized Singleton getInstance() { if(instance == null) { //判断是否实例化 synchronized (Singleton.class) { if(instance == null) { instance = new Singleton(); } } } return instance; //否则直接return } }
静态内部类
这个声明一个静态内部类的形式,既可以实现懒汉式的效果,只有调用了getInstance方法,SingleTonHoler才会初始化;也可以实现线程安全的效果,静态内部类把Singleton对象声明为static类型,在类加载的时候会实例化,且jvm保证了只会实例化一次,所以是线程安全的。
public class Singleton{ private Singleton(){} private static class SingleTonHoler{ private static Singleton INSTANCE = new Singleton(); } public static Singleton getInstance(){ return SingleTonHoler.INSTANCE; } }
二. 防止反射创建多个实例
上面的几种方式都可以多线程的情况下,类只能实现一次,但是都是可以通过反射的方法来创建多个实例对象的,那有没有一种既可以防止反射,又可以保证多线程安全的单例实现方法呢?有的,就是通过枚举的方式实现。
enum Singleton { INSTANCE; //属性 public void say() { System.out.println("关注java面试教程 学习更多java知识"); } }
三.总结
我们学习了怎么来创建一个安全可靠的单例模式,如果不需要防止反射的情况下,可以通过静态内部类的方法实现,否则,推荐使用枚举的形式来实现单例。