定义:保证一个类再运行期间只有一个实例对象,并提供一个访问它的全局访问方法。
单例模式的范围:是一个ClassLoader极其子类的范围,每被ClassLoaser加载一次就会创建一次。
饿汉式:
public class Singleton {
private Singleton() {
}
private static Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
}
懒汉式:
public class Singleton {
private Singleton() {
}
private volatile static Singleton instance = null;
public static Singleton getInstance() {
if(instance == null) {
synchronized (Singleton.class) {
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
不论饿汉式还是懒汉式,都需要私有化构造方法。饿汉式在类加载的时候就创建实例;懒汉式是在调用方法的是时候才去判断时否创建实例。
以上懒汉示例应用了双重检查机制,避免多次判断,节省时间。为了在多线程环境下仍可以实现单例的目标,应用了synchronized 同步创建过程。为了避免在多线程环境中,出现instance不为null,但是instance还没初始化完成的情况,使用volatile 关键字修饰instance 。
单例模式体现的设计思想:
1.延迟加载思想(懒汉式)
2.缓存思想 (懒汉式)
(1). 定义一个缓存数据的容器
(2). 缓存中存取数据,先从容器取值,再判断时否有值,有值直接返回,如果没有就获取相应的数据,然后放到容器中,再返回。
缓存的实现示例:
public class JavaCache {
private Map<String, String> map = new HashMap<>();
public Object getValue(String key) {
String value = map.get(key);
//value不存在,所有先取值,然后放到容器中
if(value == null) {
//此处仅仅是取值示例
value = key + "value";
map.put(key, value);
}
return value;
}
}
单例模式的应用场景示例:读取配置文件的类,一般在系统初始化的时候有一个示例进行配置初始化就可以了,如果存在多个实例,多次加载配置文件,会造成系统资源的浪费。
配置文件:config.properties
paramA=xx
paramB=yy
读取配置文件的类:AppConfig.properties
public class AppConfig {
/**
* 私有化构造器,并在创建实例的时候,加载配置文件
*/
private AppConfig() {
readConfig();
}
private static AppConfig instance = new AppConfig();
public static AppConfig getInstance() {
return instance;
}
private String paramA;
private String paramB;
public String getParamA() {
return paramA;
}
public String getParamB() {
return paramB;
}
/**
* 读取配置文件,一般只需加载一次即可
* @throws IOException
*/
private void readConfig() {
Properties p = new Properties();
InputStream in = null;
System.out.println("1111");
try {
in = AppConfig.class.getResourceAsStream("config.properties");
p.load(in);
this.paramA = p.getProperty("paramA");
this.paramB = p.getProperty("paramB");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
单例模式的本质是:控制实例的数量。
扩展:如何控制类的特定数量的实例?
public class SingletonExtend {
private SingletonExtend() {
};
// 存放创建的实例的容器
private static Map<String, SingletonExtend> map = new HashMap<>();
// 记录当前实例的位置
private static int num = 1;
//缺省key值前缀
private static final String DEFAULT_KEY = "cache";
// 最大的实例数量
private static final int maxNum = 3;
public static SingletonExtend getInstance() {
String key = DEFAULT_KEY + num;
SingletonExtend singletonExtend = map.get(key);
if (singletonExtend == null) {
map.put(key, new SingletonExtend());
}
if (num++ >= maxNum)
num = 1;
return map.get(key);
}
}