一、单例模式实现三要素:
●只能有一个实例。
◆构造器私有化
●自行创建这个实例
◆含有一个该类的静态变量来保存这个唯一的实例
●必须自行向整个系统提供这个实例;
◆对外提供获取该实例对象的方式:
(1)直接暴露
(2)用静态变量的get方法获取
二、单例模式分类
根据对象创建的时机不同,单例模式可以分为两类。一种是在类初始化的时候直接创建对象,称为饿汉式;另一种是在调用类静态方法时才创建对象,称为懒汉式。
三、饿汉式
饿汉式是线程安全的。
1.直接实例化饿汉式
public class Singleton { public static final Singleton INSTANCE = new Singleton(); //final修饰,强调这是一个单例 private Singleton(){} }
2.枚举类饿汉式
public enum Singleton { INSTANCE; }
3.静态代码块方式
public class Singleton { public static final Singleton INSTANCE; //final修饰,强调这是一个单例 static { INSTANCE = new Singleton(); } private Singleton(){} }
这种方式适合于需要通过加载外部文件,来实例化单例对象属性的时候。
例如:
public class Singleton1 { public static final Singleton1 INSTANCE; private String name; static { INSTANCE = new Singleton1(); try { InputStream inputStream = Singleton1.class.getClassLoader().getResourceAsStream("singleton.properties"); Properties properties = new Properties(); properties.load(inputStream); String name = properties.getProperty("singleton1.name"); INSTANCE.name = name; } catch (IOException e) { e.printStackTrace(); } } private Singleton1(){} public String getName() { return name; } }
四、懒汉式
方式一:
public class Singleton { public static Singleton instance; private Singleton(){ } public static Singleton getInstance(){ if (instance == null){ instance = new Singleton(); } return instance; } }
注:此方式有线程安全问题,只适用于单线程环境。
方式二:同步代码块
public class Singleton { public static Singleton instance; private Singleton(){ } public static Singleton getInstance(){ synchronized (Singleton.class){ if (instance == null){ instance = new Singleton(); } } return instance; } }
注:此方式虽然解决了线程安全问题,但是效率低,不推荐使用
方式三:
public class Singleton { public static Singleton instance; private Singleton(){ } public static Singleton getInstance(){ if (instance == null){ synchronized (Singleton.class){ if (instance == null){ instance = new Singleton(); } } } return instance; } }
效率比前一种高,推荐使用
方式四:静态内部类(强烈推荐使用)
public class Singleton { private Singleton(){ } private static class Inner{ private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance(){ return Inner.INSTANCE; } }
注:
在内部类被加载和初始化时,才创建工 NSTANCE实例对象。
静态内部类不会自动随着外部类的加载和初始化而初始化,它是要单独去加载和初始化的。因为是在内部类加载和初始化时,创建的,因此是线程安全的。