Java单例模式的几种写法详解

一般来说,单例模式有五种写法,每种写法没有绝对的好坏,还是要看具体的应用场景以及个人的使用喜好


一 饿汉式[推荐]:

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上的第一篇博客,大家共同学习成长!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值