Wetts's blog

Stay Hungry, Stay Foolish.

0%

Java-设计模式-单例模式问题为何可以通过枚举解决.md

转自:https://www.cnblogs.com/call-me-pengye/p/11214435.html

首先我们都知道 enum 默认继承了 java.lang.Enum 类并实现了 java.lang.Seriablizable 和 java.lang.Comparable 两个接口。接下来我们将依次说明枚举是如何防止这三种方式对单例的破环

克隆

一个普通的类要是clone必须实现 java.lang.Cloneable 接口,重写 clone() 方法,同理我们来看看枚举能否也是一样
1

我们可以看到 enum 是不被允许重写 clone(),因为 Enum 类已经将 clone() 方法定义为 final 了,并且 Enum 在使用 clone() 时直接抛出异常,如下图,这就是枚举为什么能防止克隆破环的原因,它根本就不允许克隆
2

反射

1
2
3
4
5
6
7
8
9
10
public class DestroySingleton {

public static void main(String[] args) throws Exception {
//通过反射获取
Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton reflex = constructor.newInstance();
System.out.println("reflex的hashCode:"+reflex.hashCode());
}
}

我们先来看一下反射实现的主要步骤:首先通过 class 的 getDeclaredConstructor() 获取到反射对象的构造器,然后通过 newInstance() 调用其构造方法获取对象,getDeclaredConstructor() 主要是通过 getConstructor0() 来获取构造器,具体代码如下:

1
2
3
4
5
6
@CallerSensitive
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
return getConstructor0(parameterTypes, Member.DECLARED);
}

在 getConstructor0 中,他会先调用 privateGetDeclaredConstructors 方法去获取;具体代码如下:

1
2
3
4
5
6
7
8
9
10
private Constructor<T> getConstructor0(Class<?>[] parameterTypes,int which) throws NoSuchMethodException
{
Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));
for (Constructor<T> constructor : constructors) {
if (arrayContentsEq(parameterTypes,constructor.getParameterTypes())) {
return getReflectionFactory().copyConstructor(constructor);
}
}
throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes));
}

在 privateGetDeclaredConstructors() 中 publicOnly 的值是 false,ReflectionData 的 publicConstructors 和 declaredConstructors 都是 null;而 privateGetDeclaredConstructors() 中真正决定 Constructor<T>[] 的代码是 getDeclaredConstructors0(publicOnly)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private Constructor<T>[] privateGetDeclaredConstructors(boolean publicOnly) {
checkInitted();
Constructor<T>[] res;
ReflectionData<T> rd = reflectionData();
if (rd != null) {
res = publicOnly ? rd.publicConstructors : rd.declaredConstructors;
if (res != null) return res;
}
// No cached value available; request value from VM
if (isInterface()) {
@SuppressWarnings("unchecked")
Constructor<T>[] temporaryRes = (Constructor<T>[]) new Constructor<?>[0];
res = temporaryRes;
} else {
res = getDeclaredConstructors0(publicOnly);
}
if (rd != null) {
if (publicOnly) {
rd.publicConstructors = res;
} else {
rd.declaredConstructors = res;
}
}
return res;
}

在得到 Constructor<T>[] 后回到 getConstructor0() 将依次对其进行轮询判断,找到合适的 Constructor 并交由 ReflectionFactory 工厂 copy 出一个 Constructor。其中轮询的判断条件由 parameterTypes 和 constructor.getParameterTypes() 决定,parameterTypes 是个空数组;普通类的 constructor.getParameterTypes() 得出的结果也是空数组,而枚举产生的数组为:[class java.lang.String, int];接着就交由 arrayContentsEq() 执行,并返回一个 boolean值。

arrayContentsEq 具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private static boolean arrayContentsEq(Object[] a1, Object[] a2) {
if (a1 == null) {
return a2 == null || a2.length == 0;
}

if (a2 == null) {
return a1.length == 0;
}

if (a1.length != a2.length) {
return false;
}

for (int i = 0; i < a1.length; i++) {
if (a1[i] != a2[i]) {
return false;
}
}

return true;
}

普通类在 arrayContentsEq() 中所有的 if 和 for 都通不过,最后直接返回 true;而枚举类则会因为 a1.length != a2.length(注:a2.length的之为2)条件成立而返回 false。于是普通类接着执行 return getReflectionFactory().copyConstructor(constructor); 而枚举类则直接抛出异常 throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes)); 具体错误信息如下:

Exception in thread “main” java.lang.NoSuchMethodException: designPatterns.singleton.useenum.Singleton.()
     at java.lang.Class.getConstructor0(Class.java:3082)
     at java.lang.Class.getDeclaredConstructor(Class.java:2178)
     at designPatterns.singleton.useenum.DestroySingleton.main(DestroySingleton.java:18)

从控制台输出的信息来看 parameterTypes 的确是一个空对象,但是为什么给出 init() 的 NoSuchMethodException 异常。这就是为什么枚举不能通过反射实例化的原因之一,另一个原因就是:在获取到类构造器后通过 newInstance() 来实例化前,枚举是无法通过

1
if( (clazz.getModifiers() & Modifier.ENUM) != 0 )

条件判断的,具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@CallerSensitive
public T newInstance(Object ... initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, null, modifiers);
}
}
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
ConstructorAccessor ca = constructorAccessor; // read volatile
if (ca == null) {
ca = acquireConstructorAccessor();
}
@SuppressWarnings("unchecked")
T inst = (T) ca.newInstance(initargs);
return inst;
}

序列化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class CreateClassBySerialized {

@SuppressWarnings("unchecked")
public static <T extends Serializable> T createClassBySerialized(T t) throws IOException, ClassNotFoundException{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(t);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
T object = (T) ois.readObject();
if (ois != null) ois.close();
if (bis != null) bis.close();
if (oos != null) oos.close();
if (bos != null) bos.close();
return object;
}
}


public class DestroySingleton {

public static void main(String[] args) throws Exception {
//通过序列化,反序列化获取
Singleton serialize = CreateClassBySerialized.createClassBySerialized(Singleton.getInstance());
System.out.println("serialize的hashCode:"+serialize.hashCode());
}
}

首先我们先来看看为什么添加 readResolve() 方法就能防止序列化对单例的破环。关键的代码就是在 readObject() 里的 readObject0() 实现的,readObject() 具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public final Object readObject() throws IOException, ClassNotFoundException{
if (enableOverride) {
return readObjectOverride();
}
// if nested read, passHandle contains handle of enclosing object
int outerHandle = passHandle;
try {
Object obj = readObject0(false);
handles.markDependency(outerHandle, passHandle);
ClassNotFoundException ex = handles.lookupException(passHandle);
if (ex != null) {
throw ex;
}
if (depth == 0) {
vlist.doCallbacks();
}
return obj;
} finally {
passHandle = outerHandle;
if (closed && depth == 0) {
clear();
}
}
}

而 readObject0() 对类的实现体现在 switch 选择器上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
switch (tc) {
case TC_NULL:
return readNull();
case TC_REFERENCE:
return readHandle(unshared);
case TC_CLASS:
return readClass(unshared);
case TC_CLASSDESC:
case TC_PROXYCLASSDESC:
return readClassDesc(unshared);
case TC_STRING:
case TC_LONGSTRING:
return checkResolve(readString(unshared));
case TC_ARRAY:
return checkResolve(readArray(unshared));
case TC_ENUM:
return checkResolve(readEnum(unshared));
case TC_OBJECT:
return checkResolve(readOrdinaryObject(unshared));
case TC_EXCEPTION:
IOException ex = readFatalException();
throw new WriteAbortedException("writing aborted", ex);
case TC_BLOCKDATA:
case TC_BLOCKDATALONG:
if (oldMode) {
bin.setBlockDataMode(true);
bin.peek(); // force header read
throw new OptionalDataException(bin.currentBlockRemaining());
} else {
throw new StreamCorruptedException("unexpected block data");
}
case TC_ENDBLOCKDATA:
if (oldMode) {
throw new OptionalDataException(true);
} else {
throw new StreamCorruptedException("unexpected end of block data");
}
default:
throw new StreamCorruptedException(String.format("invalid type code: %02X", tc));
}

tc 值不同,类的实现方式也不同;普通类(TC_OBJECT)由 readOrdinaryObject(unshared) 来实现,枚举类(TC_ENUM)由 readOrdinaryObject(unshared) 来实现。

readOrdinaryObject(unshared) 决定了类是通过构造器实现还是通过 readResolve() 来实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
if (obj != null && handles.lookupException(passHandle) == null && desc.hasReadResolveMethod())
{
Object rep = desc.invokeReadResolve(obj);
if (unshared && rep.getClass().isArray()) {
rep = cloneArray(rep);
}
if (rep != obj) {
// Filter the replacement object
if (rep != null) {
if (rep.getClass().isArray()) {
filterCheck(rep.getClass(), Array.getLength(rep));
} else {
filterCheck(rep.getClass(), -1);
}
}
handles.setObject(passHandle, obj = rep);
}
}

其中 desc.hasReadResolveMethod() 就是来用判断是否有 readResolve()

1
2
3
4
boolean hasReadResolveMethod() {
requireInitialized();
return (readResolveMethod != null);
}

如果不存在 readResolve() 则 readResolveMethod 为 null,反之则为 readResolve() 对应的 Method 对象(我这儿的是 private java.lang.Object designPatterns.singleton.doublecheck.Singleton.readResolve() )。于是乎就执行 desc.invokeReadResolve(obj) 代码,通过 Method.invoke 执行 readResolve() 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Object invokeReadResolve(Object obj) throws IOException, UnsupportedOperationException
{
requireInitialized();
if (readResolveMethod != null) {
try {
return readResolveMethod.invoke(obj, (Object[]) null);
} catch (InvocationTargetException ex) {
Throwable th = ex.getTargetException();
if (th instanceof ObjectStreamException) {
throw (ObjectStreamException) th;
} else {
throwMiscException(th);
throw new InternalError(th); // never reached
}
} catch (IllegalAccessException ex) {
// should not occur, as access checks have been suppressed
throw new InternalError(ex);
}
} else {
throw new UnsupportedOperationException();
}
}

@CallerSensitive
public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException,InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
MethodAccessor ma = methodAccessor; // read volatile
if (ma == null) {
ma = acquireMethodAccessor();
}
return ma.invoke(obj, args); // readResolve最终执行处}

这样就得到了我们的静态 singleton,实现单例模式。而在实现枚举的 readEnum() 方法中,枚举的实现是通过调用 java.lang.Enum的 静态方法 valueOf 来实现的

1
2
3
4
5
6
7
8
9
public static <T extends Enum<T>> T valueOf(Class<T> enumType,String name) {
T result = enumType.enumConstantDirectory().get(name);
if (result != null)
return result;
if (name == null)
throw new NullPointerException("Name is null");
throw new IllegalArgumentException(
"No enum constant " + enumType.getCanonicalName() + "." + name);
}

enumType.enumConstantDirectory() 返回的对象是继承了枚举常量的 hashMap,其中 key 键是枚举常量名字,value 键是常量枚举对象本身;当它拿到枚举类中全部的枚举后,再其轮询将每一个枚举常量存入 hashMap 中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
Map<String, T> enumConstantDirectory() {
if (enumConstantDirectory == null) {
T[] universe = getEnumConstantsShared();
if (universe == null)
throw new IllegalArgumentException(
getName() + " is not an enum type");
Map<String, T> m = new HashMap<>(2 * universe.length);
for (T constant : universe)
m.put(((Enum<?>)constant).name(), constant);
enumConstantDirectory = m;
}
return enumConstantDirectory;
}

//getEnumConstantsShared()就是获取到的一个个枚举对象
T[] getEnumConstantsShared() {
if (enumConstants == null) {
if (!isEnum()) return null;
try {
final Method values = getMethod("values");
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
values.setAccessible(true);
return null;
}
});
@SuppressWarnings("unchecked")
T[] temporaryConstants = (T[])values.invoke(null);
enumConstants = temporaryConstants;
}
// These can happen when users concoct enum-like classes
// that don't comply with the enum spec.
catch (InvocationTargetException | NoSuchMethodException |
IllegalAccessException ex) { return null; }
}
return enumConstants;
}

当我们拿到枚举的 hashMap 后,通过 get(name) 方法获取对应的枚举然后层层返回。代码中实现枚举的入口代码是 Enum.valueOf((Class)cl, name),这样实现的现过其实就是 EnumClass.name(我代码的体现是Singleton.INSTANCE),这样来看的话无论是 EnumClass.name 获取对象,还是 Enum.valueOf((Class)cl, name) 获取对象,它们得到的都是同一个对象,这其实就是枚举保持单例的原理。