书上分别说了以下三种
1)将公有静态成员做成final域享有特权的客户端可以借助AccessibleObject.setAccessible方法,通过反射机制调用私有构造器。
package com.lzw.singleton1;
import java.lang.reflect.Constructor;
/**
*
* 单例实现1
* <p>
* 公有静态成员是个public final域
* <p>
* 为防止client利用反射调用私有改造函数,所以在创建第二个实例的时候抛出了异常
*
* @author troyli
*
*/
public class Singleton1 {
public static final Singleton1 INSTANCE = new Singleton1();
// 私有改造函数
private Singleton1() {
if (INSTANCE != null) {
throw new IllegalArgumentException("不存在第二个实例对象……");
}
}
// 其他方法实现
public void otherMethod() {
System.out.println("call otherMethod");
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public static void main(String args[]) throws Exception {
Singleton1 singleton = Singleton1.INSTANCE;
singleton.otherMethod();
// 利用反射调用私有构造器
Constructor[] arrayConstructor = singleton.getClass()
.getDeclaredConstructors();
for (Constructor constructor : arrayConstructor) {
// 调用setAccessible(true);
constructor.setAccessible(true);
// 实例化,这里一定会抛出异常
constructor.newInstance();
}
}
}
问题:为什么会抛出异常(不存在第二个实例对象……),这里我们分析一下代码的执行顺序。
①JVM 装载类的静态成员变量:public static final Singleton1 INSTANCE;
②执行入口方面main;
③获取实例对象:Singleton1 singleton = Singleton1.INSTANCE;也就会执行private Singleton1(){……}
④执行newInstatnce:constructor.newInstance();就会是在已经有一个对象的时候重新调用private Singleton1(){……},于是就会抛出异常。
2)将公有静态成员做成final域和将公有成员做成静态工厂方法时,为了使Singleton类变成可序列化的,仅仅在声明上加上“implements Serializable”是不够的。
为了维护并保证Singleton,必须声明所有实例域都是瞬时的(transient),并提供一个readResolve方法。否则,每次反序列化一个序列化的实例时,都会创建一个新的实例。
package com.lzw.singleton2;
import java.io.Serializable;
/**
*
*单例实现2
*<p>
* 公有的成员为静态工厂方法
*<p>
* 序列化时,要实现readResolve方法,防止反序列化出新的实例(这个应该怎么测试呢???)
*
*@author troyli
*
*/
public class Singleton2 implements Serializable {
// 私有static Instance
private static final Singleton2 INSTANCE = new Singleton2();
// 私有构造函数
private Singleton2() {
}
// 获取单例方法
public static Singleton2 getInstance() {
return INSTANCE;
}
// 其他方法
public void otherMethod() {
//
}
// 必须提供该方法,以便重新指定反序列化得到的对象.
private Object readResolve() {
return INSTANCE;
}
}
这点对我是全新的东西,因此也在积累中……
3)单元素枚举类型更加简洁、无偿的提供了序列化机制,绝对防止多次实例化,即使是在面对复杂的序列化或者反射攻击时;它已经成为实现Singleton的最佳方法。
package com.lzw.singleton3;
import java.lang.reflect.Constructor;
/**
*
*枚举实现单例
*<p>
* 目前最好的方式,避免了反射的攻击和序列化的问题
*
*<pre>
* 射调用枚举私有构造函数测试结果:
* Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
at java.lang.reflect.Constructor.newInstance(Unknown Source)
at com.lzw.singleton3.Singleton3.main(Singleton3.java:41)
*</pre>
*
*@author troyli
*
*/
public class Singleton3 {
enum EnumSingleton {
INSTANCE;
public void otherMethod() {
}
}
// 测试,是否可以反射生成枚举
public static void main(String args[]) throws Exception {
// 利用反射调用私有构造器
Constructor[] arrayConstructor = EnumSingleton.INSTANCE.getClass()
.getDeclaredConstructors();
for (Constructor constructor : arrayConstructor) {
// 调用setAccessible(true),设置为可以访问;
constructor.setAccessible(true);
// 实例化,这里一定会抛出异常
constructor.newInstance();
}
}
}
可以看上面的第一种的问题解析,这里不再重复。
虽然这种方法目前还没有被广泛采用,但是单元素的枚举类型已经成为实现Singleton的最佳方法。