写一个Singleton示例
Singlton:在Java中即值单例设计模式,它是软件开发最常用的设计模式之一
- 单: 唯一
- 例: 示例
- 单例设计模式,即某个类在整个系统中只能有一个示例对象可以被获取和使用的代码模式;
- 例如: 代表JVM运行环境的Runtime类
要点: - 一丶 某个类只能有一个实例;
- 构造器私有化 - 二丶 它必须自行创建这个实例;
- 含有一个该类的静态变量来保存这个唯一的实例 - 三丶 它必须自行向整个系统提供这个实例;
-对外提供获取该实例对象的方式:
(1) 直接暴露 (2) 用静态变量的get方法获取
常见形式
饿汉式: 直接创建对象,不存在线程安全问题
直接实例化饿汉式(简洁直观)
/**
* Created by The丶Best丶K
*
* 饿汉式 1
* 在类初始化时直接创建实例对象,不管你是否需要这个对象 都会创建
* 1丶构造器私有化
* 2丶 自行创建 并且用静态变量保存
* 3丶 向外提供这个实例
* 4丶强调这是一个单例,可以用final修饰
*/
public class Singleton1 {
public static final Singleton1 INSTANCE=new Singleton1();
private Singleton1(){ }
}
枚举式(最简洁)
/**
* Created by The丶Best丶K
*
* 枚举类型: 表示该类型的对象是有限的几个
* 可以限定为一个 ,就成了单例
*/
public enum Singleton2 {
INSTANCE
}
静态代码块饿汉式(适合复杂实例化)
/**
* Created by The丶Best丶K
* 静态代码块
* 初始化 info属性
*/
public class Singleton3 {
public static final Singleton3 INSTANCE;
private String info;
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
static {
Properties pro=new Properties();
try {
//配置文件在 src目录下 最终编译是在类路径下的 所以可以直接用类加载器
// 当前这个类也需要类加载器 利用类加载器加载类路径下的文件
pro.load(Singleton3.class.getClassLoader().getResourceAsStream("singleton.properties"));
//从配置文件中获取 value
INSTANCE=new Singleton3(pro.getProperty("info"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private Singleton3(String info){
this.info=info;
}
}
饿汉式是不存在线程安全问题的,因为java类加器的加载机制就是可以避免线程安全问题
静态代码块和初始化方法最终会合成一个类初始化方法 clinit方法 一口气完成 并且是线程安全的
懒汉式:延迟创建对象
线程不安全(适用于单线程)
/**
* Created by The丶Best丶K
* 懒汉式:
* 延迟创建这个实例对象
* 1丶构造器私有化
* 2丶用一个静态变量保存这个唯一的实例
* 3丶提供一个静态方法 获取这个实例对象
*/
public class Singleton4 {
private static Singleton4 instance;
private Singleton4() {
}
public static Singleton4 getInstance() {
if (instance == null) {
instance = new Singleton4();
}
return instance;
}
}
这个里面有可能会有线程安全问题: 比如有两个线程
解释:
当第一个线程进入 if (instance == null)
方法判定为空 需要创建对象,这时阻塞了
第二个线程进入也遇到了 if (instance == null)
方法 判定为空 也需要创建对象
所以导致创建了两次对象 ,这个是有可能出现 而不是一定出现,
可以用同步来解决
线程安全(适用于多线程)
静态内部类形式(适用于多线程)
/**
* Created by The丶Best丶K
*
* 1丶 构造器私有化
* 2丶 私有静态变量
*
* 在内部类被加载和初始化时,才创建INSTANCE实例对象
* 静态内部类不会随着外部类的加载和初始化而初始化,它是要单独去加载和初始化的.
* 因为是在内部类初始化时初始化的,因此是线程安全的
*/
public class Singleton5 {
private Singleton5() {
}
private static class Inner{
private static final Singleton5 INSTANCE = new Singleton5();
}
public static Singleton5 getInstance() {
return Inner.INSTANCE;
}
}
总结:
- 如果是饿汉式: 枚举形式最简单
- 如果是懒汉式: 静态内部类形式最简单