面试题答案
一键面试通过反射破坏单例模式代码示例
假设我们有一个简单的单例类:
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
通过反射破坏单例模式的代码如下:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class ReflectionSingletonBreak {
public static void main(String[] args) {
try {
// 获取Singleton类的构造函数
Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
// 设置构造函数可访问,因为构造函数是private的
constructor.setAccessible(true);
// 通过反射创建实例
Singleton instance1 = constructor.newInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println("instance1 == instance2: " + (instance1 == instance2));
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) {
e.printStackTrace();
}
}
}
防止反射破坏单例模式的防御策略及实现
- 在构造函数中检查实例是否已存在 原理:在构造函数中检查是否已经有实例存在,如果存在则抛出异常,防止多次创建实例。 代码修改如下:
public class Singleton {
private static Singleton instance;
private Singleton() {
if (instance != null) {
throw new RuntimeException("Singleton instance already exists.");
}
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
在这种情况下,当反射尝试调用构造函数时,如果实例已经存在,就会抛出异常,从而防止反射破坏单例模式。
- 使用枚举实现单例 原理:枚举类型在Java中本身就保证了全局唯一,并且JVM会确保枚举类型的实例化是线程安全且不会被反射破坏。 代码如下:
public enum EnumSingleton {
INSTANCE;
// 可以在这里添加其他方法和属性
}
使用方式:
public class EnumSingletonUser {
public static void main(String[] args) {
EnumSingleton instance1 = EnumSingleton.INSTANCE;
EnumSingleton instance2 = EnumSingleton.INSTANCE;
System.out.println("instance1 == instance2: " + (instance1 == instance2));
}
}
由于枚举类型的特殊性,反射无法创建新的实例,从而有效防止反射破坏单例模式。