Java单例模式

单例模式

单例模式的核心是保证一个类只有一个实例,并且提供一个为其他类访问的访问点。

单例模式的实现方式:
  • 懒汉式单例模式
  • 饿汉式单例模式
  • 静态内部类模式
  • 枚举类模式
单例模式的优缺点:

优点: 1.在单例模式中,活动的单例只有一个实例,对单例类的所有实例化得到的都是相同的一个实例。这样就 防止其它对象对自己的实例化,确保所有的对象都访问一个实例 2.单例模式具有一定的伸缩性,类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性。 3.提供了对唯一实例的受控访问。 4.由于在系统内存中只存在一个对象,因此可以 节约系统资源,当 需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能。 5.允许可变数目的实例。 6.避免对共享资源的多重占用。

缺点: 1.不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。 2.由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。 3.单例类的职责过重,在一定程度上违背了“单一职责原则”。 4.滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。

懒汉式

单例实例在第一次被使用时构建,延迟初始化。

/**
 * 懒汉式单例模式
 * @author 叶小陌
 *
 */
public class Mode02 {

	public static Mode02 mode02 = null;
	
	private Mode02(){
		//用于防止通过反射获取实例
		if (mode02 != null) {
			throw new RuntimeException("单例模式只能创建一个模式");
		}
	}
	public static synchronized Mode02 getInstance(){
		if (mode02 == null) {
			mode02 = new Mode02();
		}
		return mode02;
	}
	//重写该方法,防止序列化和反序列化获取实例
	private Object readResolve() {
		// TODO Auto-generated method stub
		return mode02;
	}
}

优点: 避免了饿汉式的那种在没有用到的情况下创建事例,资源利用率高,不执行getInstance()就不会被实例,可以执行该类的其他静态方法。

缺点: 懒汉式在单个线程中没有问题,但多个线程同事访问的时候就可能同事创建多个实例,而且这多个实例不是同一个对象,虽然后面创建的实例会覆盖先创建的实例,但是还是会存在拿到不同对象的情况。解决这个问题的办法就是加锁synchonized,第一次加载时不够快,多线程使用不必要的同步开销大。

饿汉式

单例实例在类装载时就构建,急切初始化。(预先加载法)

/**
 * 饿汉式单例模式
 * @author 叶小陌
 *
 */
public class Mode01 {
	public static Mode01 mode01 = new Mode01();	
	private Mode01(){	
		if (mode01 != null) {
			throw new RuntimeException("单例模式只能创建一个对象");
		}
	}
	public static Mode01 getInstance() {
		return mode01;
	}
}

优点 1.线程安全 2.在类加载的同时已经创建好一个静态对象,调用时反应速度快

缺点 资源效率不高,可能getInstance()永远不会执行到,但执行该类的其他静态方法或者加载了该类(class.forName),那么这个实例仍然初始化

静态内部类

/**
 * 静态内部类单例模式
 * @author 叶小陌
 *
 */
public class Mode03 {
	public static class GetInstance{
		public static final Mode03 mode03 = new Mode03();
	}
	private Mode03() {
		
	}	
	public static Mode03 getInstance() {
		return GetInstance.mode03;
	}
}

优点 资源利用率高,不执行getInstance()不被实例,可以执行该类其他静态方法

缺点 第一次加载时反应不够快

枚举类

/**
 * 单例模式:枚举实现方式
 * @author 叶小陌
 */
public enum Mode04 {
	//定义一个枚举元素,则这个元素就代替了Mode04的实例
	MODE04;
}

优点: 1、实现简单 2、枚举本身就是单例模式。由于JVM从根本上提供保障!避免了通过反射和反序列化的漏洞!

缺点: 1、无延迟加载

单例模式漏洞

漏洞一(反射的形式获取实例) 通过反射的方式我们依然可以获取单例模式的多个实例(枚举方式除外) 解决方式:我们可以在单例模式类中的无参构造方法中手动的抛出异常控制 代码如下:

private Mode02(){
		//用于防止通过反射获取实例
		if (mode02 != null) {
			throw new RuntimeException("单例模式只能创建一个模式");
		}
	}

漏洞二(反序列化形式获取实例) 通过反序列化的方式也可以破解上面几种方式(枚举方式除外) 解决方式:我们只需要在单例模式类中重写readResolve方式并在该方法中返回单例对象即可 代码如下:

	//重写该方法,防止序列化和反序列化获取实例
	private Object readResolve() throws ObjectStreamException {
		// TODO Auto-generated method stub
		return mode02;
	}

说明:readResolve方法是基于回调的,反序列化时,如果定义了readResolve()则直接返回此方法指定的对象,而不需要在创建新的对象!

总结: 一般采用饿汉式,若对资源十分在意可以采用静态内部类,不建议采用懒汉式及双重检测

转载于:https://my.oschina.net/u/4116644/blog/3038861

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值