Singleton 单例模式

本文详细介绍了单例模式的实现方式及其特点,包括饿汉式和懒汉式的区别。此外,还探讨了如何让单例模式支持继承,以及在特定场景下使用多例模式的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

      单例模式是类的创建模式。 满足三个要点:

      (1) 单例类只能有一个实例。

      (2) 必须由单例类自己创建这个实例。

      (3) 单例类必须向外界提供这个实例。

      为了保证单例类的实例不被其它类创建,必须使单例类的构造子为私有,因此,单例类不能被继承,不能有子类。

public class Singleton {

	private static Singleton s1 = new Singleton();

	private Singleton() {}
	
	public static Singleton getInstance() {
		return s1;
	}
}

      这一种实现方式也叫作“饿汉式”单例类,因为它本身持有一个对自身实例的静态引用,所以当此类被首次加载时便会将自己实例化。还有一种方式“懒汉式”--lazy:

public class Singleton {

	private static Singleton s1 = null;

	private Singleton() {}
	
	public static Singleton getInstance() {
		if (null == s1) {
			s1 = new Singleton();
		}
		return s1;
	}
}

      当有外部首次获取它的实例的时候,即 Singleton.getInstance() 时才会去创建它的实例。

      以上两种实现方法严格实现了单例模式的定义,但由于其构造子都是私有的,因此不能被继承。为了使其能够被继承,唯一的办法是使其构造子对其子类可见,但这样就使得可以从外部使用 new 操作符创建其实力,失去了单例类的意义,只有严格遵守使用 getInstance()方法而不是new 来获得其实例,这个单例类才有意义。

public class Singleton {

	private static Map<String, Object> map = new HashMap<String, Object>();

	static {
		Singleton s = new Singleton();
		map.put(s.getClass().getName(), s);
	}

	protected Singleton() { }

	public static Singleton getInstance(String className)
			throws InstantiationException, IllegalAccessException,
			ClassNotFoundException {
		
		if (null == className) {
			className = "com.mycompany.singleton.Singleton";
		}

		if (null == map.get(className)) {
			map.put(className, Class.forName(className).newInstance());
		}

		return (Singleton) map.get(className);
	}
}

      这种方法是通过在类加载期间就创建其实例,并将其实例保存在 Map 中,以类名作为键值,调用 getInstance()方法时就从这个Map中取得这个加载期间产生的实例。

      其子类的实例也有父类的 getInstance() 维护,保存在map中,我们需要将子类的类名传给getInstance()方法,父类使用这个类名创建其子类对象并登记在Map中。子类也是通过到这个Map中获得其实例。

public class SubSingleton extends Singleton {

	protected SubSingleton() { }

	public static SubSingleton getInstance() throws InstantiationException,
			IllegalAccessException, ClassNotFoundException {
		
		return ((SubSingleton) Singleton
				.getInstance("com.mycompany.singleton.SubSingleton"));
	}
}

 

      由于父类的 getInstance(String className) 负责创建子类的实例,因此子类的构造方法对父类也必须是可见的,这样子类和父类都不能有私有构造子。所以除非必要,尽量不要试图继承单例类。

           单例类可能维护了状态属性,比如维护一个 int 型的计数器,统计网站访问人数。这样的单例类是有状态的单例类。在一些情况下,如多个 ClassLoader 可能同时加载同一单例类的情况下,单例类可能有不同的实例,因为不同的类加载器加载的同一个类会被认为是不同的类型。这时候应避免使用有状态的单例类,因为有不同的实例同时存在,所以不同的用户看到的状态可能是不一致的。

 

      多例模式 Multiton:

      单例模式可以和容易推广到多例模式。既然单例模式可以维护一个自己创建的实例,那他也可以维护多个自己创建的实例并向外界提供他的实例。这种实现就是多例。

 

public class Multiton {

	private static Multiton en = new Multiton();
	private static Multiton jp = new Multiton();
	private static Multiton cn = new Multiton();
	private static Multiton tw = new Multiton();
	
	private Multiton() { }
	
	public static Multiton getInstance(String locale) {
		if ("en".equals(locale)) {
			return en;
		} else if ("jp".equals(locale)) {
			return jp;
		} else if ("cn".equals(locale)) {
			return cn;
		} else if ("tw".equals(locale)) {
			return tw;
		} else {
			return en;
		}
	}
	
	public void showLocale() {
		System.out.println(this);
	}
}

这种实现可以应用到多语言版本的实现中。

 

      Tips:  什么是i18n? -- i18n 是英文单词 Internationalization 的一种简写方式,因为在字母 i - n 之间一共有18个字母,所以又叫i18n.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值