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.<init>(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;
/**
*
*枚举实现单例
*<p>目前最好的方式,避免了反射的攻击和序列化的问题
*
*反射调用枚举私有构造函数测试结果:
* 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)
总结:
在三种方法中,单元素枚举类型的单例,代码简洁,无偿提供序列化机制,同时可防止多次实例化,在实际中应该是最佳选择。