一、什么是单例
单例模式是一种常用的软件设计模式,他的定义是单例对象的类只能允许一个实例存在。
许多时候一个项目只需要拥有一个全局对象,我们就可以使用单例的思想,这样有利于优化程序运行时所占的内存,也能协调系统整体的行为。
注意:
- 单例类只能有一个实例。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
明确定义和让我们看看代码
二、饿汉式
public class EagerSingletonEx {
//私有化使外部不能直接调用
//静态是为了可以直接return
private static EagerSingletonEx instance = new EagerSingletonEx();
//私有的无参构造器,使外部不能new对象
private EagerSingletonEx(){
}
//通过getInstance方法来返回一个instance对象
public static EagerSingletonEx getInstance(){
return instance;
}
}
以上就是饿汉式单例,他在类加载时就把单例初始化完成,保证getInstance()的时候,单例是已经存在的了。
饿汉式时线程安全的,这种方式比较常用,因为没有加锁,执行效率会提高,但容易产生垃圾对象。
他在类加载时就初始化,会浪费内存。
三、懒汉式
public class LazySingleton {
//私有化使外部不能直接调用
//静态是为了可以直接return
private static LazySingleton m_instance = null;
//无参构造器,外部不能new
private LazySingleton(){
}
//getInstance方法种判断一下m_instance是否被创建,创建了则返回已有对象,没创建就创建新对象
synchronized public static LazySingleton getInstance(){
if (m_instance==null){
return new LazySingleton();
}else {
return m_instance;
}
}
}
懒汉式比较懒,只有当调用getInstance的时候,才会去初始化这个单例。这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,在多线程不能正常工作,所以严格意义上它并不算单例模式。
四、饿汉式和懒汉式对比
1、线程安全:
- 饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题,
- 懒汉式本身是非线程安全的,为了实现线程安全有几种写法。
public class SingletonLHsyn {
private static SingletonLHsyn instance;
private SingletonLHsyn (){}
//在这加了一个synchronized,使它变成了线程安全的
public static synchronized SingletonLHsyn getInstance() {
if (instance == null) {
instance = new SingletonLHsyn();
}
return instance;
}
}
这种方式具备很好的 lazy loading,他在第一次调用才初始化,避免了内存浪费,能够在多线程中很好的工作。
但是,效率很低,必须加锁 synchronized 才能保证单例,但加锁会影响效率。
不过getInstance() 的性能对应用程序不是很关键(因为该方法使用不太频繁)。
2、资源加载和性能:
饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成。
而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。
五、Spring中的单例实现
public class ResSingleton {
private static HashMap map = new HashMap();
//静态块,在类被加载时自动执行
static {
ResSingleton singleton = new ResSingleton();
//key==类名 value==ResSingleton对象
map.put(singleton.getClass().getName(), singleton);
}
//受保护的默认构造函数,如果为继承关系,则可以调用,克服了单例类不能为继承的缺点
protected ResSingleton(){}
public static ResSingleton getInstance(String name){
//判断当前传入的name值是否为null
if (null==name){
//是的话就将类名给他
name = ResSingleton.class.getName();
}
//如果key不是null,检查一下value有没有值
//不为null就说明在map中之前创建过这个实例,直接返回这个实例。
if (null == map.get(name)){
try {
//如果是空就使用反射创建一个实例
//此时key为传入的name,value为这个类名对应的类
map.put(name, Class.forName(name).newInstance());
}catch (Exception e){
System.out.println("Error happened!\n" + e.getMessage());
}
}
//返回实例
return (ResSingleton) map.get(name);
}
}
简单地说就是你给我一个类名,我去Map中根据你给我的名字查找,有实例我就直接给你,没有对应的实例我就使用反射创建一个实例给你。
它其实就是工厂模式的一种,帮你管理实例的产生。