3. 用私有构造器或枚举类型强化singleton属性

阅读更多

Effective Java 中提出实现单例的3中方法,详细内容如下:

1.将公有静态成员变量做成final域

package com.jason.effectivejava.rule3.one;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Elvis {

	public static final Elvis INSTANCE = new Elvis();

	private Elvis() {
               //it is very import for this type
		if(INSTANCE != null){
			throw new IllegalArgumentException("No exist the second instance");
		}
	}

	public void leaveTheBuilding() {

		System.out.println("Whoa baby, I'm outta here!");

	}

	public static void main(String[] args) {

		Elvis singleton = Elvis.INSTANCE;
		singleton.leaveTheBuilding();
		Constructor[] arrayConstructor = singleton.getClass().getDeclaredConstructors();
		arrayConstructor[0].setAccessible(true);
		
		try {
			arrayConstructor[0].newInstance();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

 注意:为了防止特殊用户通过AccessibleObject.setAccessible方法,利用反射机制访问私有构造函数,创建新的实例,应在私有构造函数中添加判断,阻止创建新的实例。

测试结果如下:

Whoa baby, I'm outta here!
java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
	at com.jason.effectivejava.rule3.Elvis.main(Elvis.java:30)
Caused by: java.lang.IllegalArgumentException: No exist the second instance
	at com.jason.effectivejava.rule3.Elvis.(Elvis.java:12)
	... 5 more

2.将公有成员变量做成静态工厂方法

package com.jason.effectivejava.rule3.two;

public class Elvis {

	private static final Elvis INSTANCE = new Elvis();

	private Elvis() {
	}

	public static Elvis getInstance() {
		return INSTANCE;
	}

	public void leaveTheBuilding() {

		System.out.println("Whoa baby, I'm outta here!");
	}

	// 必须提供该方法,以便重新指定反序列化得到的对象.
	private Object readResolve(){

		return INSTANCE;
	}

	public static void main(String[] args) {

		Elvis elvis = Elvis.getInstance();
		elvis.leaveTheBuilding();
	}
}

3.单元素枚举类型

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
*
*枚举实现单例
*

目前最好的方式,避免了反射的攻击和序列化的问题 * *反射调用枚举私有构造函数测试结果: * Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects at java.lang.reflect.Constructor.newInstance(Unknown Source) at com.book.chap2.singleton.Singleton3.main(Singleton3.java:34) * */ public enum Elvis { INSTANCE; public void leaveTheBuilding() { System.out.println("Whoa baby, I'm outta here!"); } public static void main(String[] args) { Elvis elvis = Elvis.INSTANCE; elvis.leaveTheBuilding(); //测试,是否可以反射生成枚举,利用反射调用私有构造器 Constructor[] arrayConstructor=Elvis.INSTANCE.getClass().getDeclaredConstructors(); arrayConstructor[0].setAccessible(true); try { arrayConstructor[0].newInstance(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }

 试结果:

Whoa baby, I'm outta here!
java.lang.IllegalArgumentException: Cannot reflectively create enum objects
	at java.lang.reflect.Constructor.newInstance(Constructor.java:511)
	at com.jason.effectivejava.rule3.two.Elvis.main(Elvis.java:36)

 总结:

在三种方法中,单元素枚举类型的单例,代码简洁,无偿提供序列化机制,同时可防止多次实例化,在实际中应该是最佳选择。

你可能感兴趣的:(3. 用私有构造器或枚举类型强化singleton属性)