一、什么是singleton
单例设计模式,即某个类在整个系统中 只能有一个实例对象 可被获取和使用的代码模式
二、要点
- 某个类只能有一个实例 —— 构造器私有化
- 它必须自行创建这个实例——含有一个该类的静态变量来保存这个唯一的实例
- 它必须自行向整个系统提供这个实例
对外提供获取该实例对象的方式,直接暴露或用静态变量的get方法获取。
三、几种常见的形式
(1)饿汉式
在类初始化时直接创造实例对象,不管你是否需要这个对象都会创建
- 直接实例化饿汉式(简洁直观)
- 枚举式(最简洁)
- 静态代码块饿汉式(适合复杂实例化)
直接实例化饿汉式
- 构造器私有化
- 自行创建,并且用静态变量保存
- 向外提供这个实例
- 强调这是个单例,我们可以用final修饰
public class Singleton01 {
public static final Singleton01 INSTANCE = new Singleton01();
private Singleton01(){
}
public static void main(String[] args) {
Singleton01 singleton01 = Singleton01.INSTANCE;
System.out.println(singleton01);
}
}
枚举式
枚举类型:表示该类型的对象是有限的几个,我们可以限定为一个,就成了单例
public enum Singleton02 {
INSTANCE;
}
public class Test {
public static void main(String[] args) {
Singleton02 instance = Singleton02.INSTANCE;
System.out.println(instance);
}
}
静态代码块饿汉式
比较复杂,适合读一堆数据才能将对象创建好
public class Singleton03 {
public static final Singleton03 INSTANCE;
private String info;
static {
try {
Properties pro = new Properties();
pro.load(Singleton03.class.getClassLoader().getResourceAsStream("single.properties")); //配置文件
INSTANCE = new Singleton03(pro.getProperty("info"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private Singleton03(String info){
this.info = info;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
@Override
public String toString() {
return "Singleton03{" +
"info='" + info + '\'' +
'}';
}
}
饿汉式中,枚举类的方式最简单
(2)懒汉式
延迟创建对象
- 线程不安全(适用于单线程)
- 线程安全(适用于多线程)
- 静态内部类形式(适用于多线程)
线程不安全
- 私有化构造器
- 用一个静态变量保存这个实例
- 提供一个静态方法,获取这个实例对象
public class Singleton04 {
private static Singleton04 instance; //若是写成public,可直接通过类名.对象名调用
private Singleton04(){
}
public static Singleton04 getInstance(){
if (instance == null){
instance = new Singleton04();
}
return instance;
}
public static void main(String[] args) {
Singleton04 instance = Singleton04.getInstance();
Singleton04 instance1 = Singleton04.getInstance();
System.out.println(instance);
System.out.println(instance == instance1); //true
}
}
线程安全
public class Singleton05 {
private static Singleton05 instance;
private Singleton05(){
}
public static Singleton05 getInstance(){
if (instance == null){ //为了效率,如果对象已经存在,不需要加锁
synchronized (Singleton05.class){ //选择一个锁对象,当前类的class当作锁对象(任意类型的对象都可以当锁对象)
if (instance == null){ //双重校验
instance = new Singleton05();
}
}
}
return instance;
}
}
静态内部类形式
在内部类被加载和初始化时,才创建INSTANCE实例对象
静态内部类不会自动随着外部类的加载和初始化而初始化,它是要单独加载和初始化的
因为实例是在内部类加载和初始化时创建的,因此是线程安全的(主动加载内部类的变量导致内部类加载,类加载机制只会加载一次(即Inner类只会加载一次),所以线程安全)
public class Singleton06 {
private Singleton06(){
}
private static class Inner{
private static final Singleton06 INSTANCE = new Singleton06();
}
private static Singleton06 getInstance(){
return Inner.INSTANCE;
}
}