一般来说,单例模式有五种写法,每种写法没有绝对的好坏,还是要看具体的应用场景以及个人的使用喜好
一 饿汉式[推荐]:
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
这种方式利用classloder机制避免了多线程的同步问题,这应该是用的最多的写法了,至于原因嘛。。。自然是因为最简单易懂,缺点只有一个没有Lazy Loading
一般情况下不延迟加载也是能接受的,因为这点内存消耗其实并没有多大关系,这也是大家选择的原因。但当一个项目里有很多的单例,或者某些单例比较庞大时,消耗还是不能忽视的,特别是对我这种有洁癖的人来说,哈哈~~
二 懒汉式:
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
解决了线程安全问题并且有延迟加载,但由于加了同步关键字 效率低下,因为99%以上的情况其实都不需要同步的,况且单例类一般情况下都使用频繁,这个性能消耗就显得没必要了
注意: synchronized 是必须的,不加严格意义上讲不叫单例模式
三 双重校验锁[不推荐]:
public class Singleton {
private volatile static Singleton instance;
private Singleton() {
}
public static Singleton getSingleton() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
双重校验方法的出现是为了解决懒汉式同步所带来的性能消耗,但在jdk1.5之前,双重校验其实是一种错误写法,
因为在Java里代码:
instance = new Singleton();
里,Singleton类的初始化和变量的赋值顺序是不可预料的,如果当对象还未初始化完成,就return并调用对象的方法,就会出现异常!所幸在jdk1.5里已经有了很好的解决方法,修饰符
volatile ,让双重校验产生价值,缺点:1.只有jdk1.5以及之后版本才能用 2.代码稍显复杂
注意: volatile 修饰符必须加,否则不能保证正确性
四 静态内部类[推荐]:
public class Singleton {
/**
* 静态内部类
* 该内部类的实例与外部类的实例 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载
*/
private static class SingletonHolder {
/**
* 静态初始化,由JVM来保证线程安全
*/
private static Singleton instance = new Singleton();
}
private Singleton() {
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
不知道谁发现的写法,真是非常的赞!充分利用JVM的运行机制来实现单例对象的延迟加载。代码优雅,有一个算不上缺点的缺点:并不是所有人都喜欢用内部类
五 枚举类[极推荐]:
public enum Singleton {
INSTANCE;
public void someMethod() {
}
}
你没有看错,枚举才是最简单的单例模式,单元素的枚举已经成为最佳的单例实现方法,它利用了枚举类的特性解决了延迟加载问题,此外它不仅避免了多线程同步问题,而且还能防止反序列化重新创建新的对象(这是之前的四种方法都存在的问题,虽然这种情况并不常见);唯一的缺点是 枚举是从jdk1.5之后才引入的新特性,很多人对它并不熟悉
有一点需要注意:
除枚举之外,前四种方法如果实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和复原。也就是说,如果你序列化一个单例类的对象,接下来复原多个那个对象,那你就会有多个单例类的实例。
总结:
1.每种单例都有自己的适用场合;懒汉、饿汉式虽然不是最优的单例写法,但却是最经典最易懂的写法,任何人只要看到代码就知道是单例!实际项目开发中,可读性也是一个考量标准
2.如果只有两种选择 懒汉、饿汉式 我更倾向于饿汉式 因为对一些普通开发来说,少了同步关键字就少了一层理解的障碍
3. 个人来说喜欢用 静态内部类和枚举类
orver! CSND上的第一篇博客,大家共同学习成长!