首页 > 代码库 > 单例模式详解
单例模式详解
1 为什么有的工具类使用单例不使用static呢?
首先你要明白static 是在什么时候初始化的,其设计意图是是什么,
单例 就是我们运行的当前虚拟机中有且只有一个需要的对象,不存在重复。
static 是给类静态成员变量使用的,属于类的属性,一般是一些常量之类的东西,从加载上来说对于类和对象之间,在类加载到内存时候静态成员变量就存在了,而对象还不存在,
另外 静态方法只能调用静态方法和静态变量这个你也应该知道,如果全部搞成静态方法那么意味着其他成员变量都要是静态的,很不方便,如果一天不要单例了也不容易扩展,很麻烦。
单例更符合OO思想。
另外,如果有static变量,必须考虑多线程的问题。
最近用sonar测评代码质量的时候,发现一个问题,工程中一些util类,以前写的static方法都提示最好用单例的方式进行改正。
为此,我仔细想了想,发现还是很有道理的。这里谈谈我个人对static方法与单例模式的理解。
这里我谈谈两种写法:
以前一些公共的类,我不假思索,习惯按照如下写法去写:
public class DateUtil { |
调用的时候直接DateUtil.changeDateFormat();
如果希望这个类作为一个单例,OK,可以直接在方法里添加一个私有的构造方法。
这样一来,既可以避免用户new一个对象,又可以实现方法的调用,看似很好。
其实,这种做法最大的缺点就是static作为一个静态方法,在加载类的时候就被加载到内存中,不管你用不用都占用这个位置,这种设计是不推荐的。
2 几种单例模式的介绍
第一种(懒汉,线程不安全)
Java代码
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
这种写法lazy loading(懒加载)很明显,但是致命的是在多线程不能正常工作。
第二种(懒汉,线程安全)
Java代码
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
这种写法能够在多线程中很好的工作,而且看起来它也具备很好的lazy loading,但是,遗憾的是,效率很低,99%情况下不需要同步。
第三种(饿汉)
Java代码
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
这种方式基于classloder机制避免了多线程的同步问题,不过,instance在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用getInstance方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance显然没有达到lazy loading的效果。
第四种(饿汉,变种)
Java代码
public class Singleton {
private Singleton instance = null;
static {
instance = new Singleton();
}
private Singleton (){}
public static Singleton getInstance() {
return this.instance;
}
}
表面上看起来差别挺大,其实更第三种方式差不多,都是在类初始化即实例化instance。
外面开发用第三,第四种比较多.在很多框架中,也会经常看到第四种的身影. 大家现在开始都使用第四种.
第五种(枚举):
Java代码
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒啊.
这种方式verygood.
3 单例模式的优缺点
优点 |
● 由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁地 创建、销毁时,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显。 ● 由于单例模式只生成一个实例,所以减少了系统的性能开销,当一个对象的产生需要 比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一 个单例对象,然后用永久驻留内存的方式来解决(在Java EE中采用单例模式时需要注意JV M 垃圾回收机制)。 |
● 单例模式可以避免对资源的多重占用,例如一个写文件动作,由于只有一个实例存在 内存中,避免对同一个资源文件的同时写操作。 |
● 单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如可以设计一个单 例类,负责所有数据表的映射处理。 |
注:性能优化 |
缺点 |
● 单例模式一般没有接口,扩展很困难,若要扩展,除了修改代码基本上没有第二种途 径可以实现。单例模式为什么不能增加接口呢?因为接口对单例模式是没有任何意义的,它 要求“自行实例化”,并且提供单一实例、接口或抽象类是不可能被实例化的。当然,在特殊情况下,单例模式可以实现接口、被继承等,需要在系统开发中根据环境判断。 |
● 单例模式对测试是不利的。在并行开发环境中,如果单例模式没有完成,是不能进行 测试的 |
● 单例模式与单一职责原则有冲突。一个类应该只实现一个逻辑,而不关心它是否是单 例的,是不是要单例取决于环境,单例模式把“要单例”和业务逻辑融合在一个类中。 |
注:不得于扩展与测试 |
4 何时使用单例模式
在一个系统中,要求一个类有且仅有一个对象,如果出现多个对象就会出现“不良反 应”,可以采用单例模式,具体的场景如下: |
● 要求生成唯一序列号的环境; |
● 在整个项目中需要一个共享访问点或共享数据,例如一个Web页面上的计数器,可以 不用把每次刷新都记录到数据库中,使用单例模式保持计数器的值,并确保是线程安全的; |
● 创建一个对象需要消耗的资源过多,如要访问I O和数据库等资源; |
● 需要定义大量的静态常量和静态方法(如工具类)的环境,可以采用单例模式(当 然,也可以直接声明为st at ic的方式)。 |
本文出自 “源码时代” 博客,请务必保留此出处http://6137563.blog.51cto.com/6127563/1888326
单例模式详解