面试题——单例模式与反射、序列化
程序员文章站
2022-04-11 18:44:21
...
饿汉式
立即加载,线程安全。
public class Singleton {
private static Singleton INSTANCE = new Singleton();//立即加载
private Singleton(){};
public static Singleton getInstance() {
return INSTANCE;
}
}
懒汉式
延迟加载,线程不安全。
public class Singleton {
private static Singleton INSTANCE;//延迟加载
private Singleton(){};
public static Singleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
双检锁(DCL)
延迟加载,线程安全。
public class Singleton {
private volatile static Singleton INSTANCE;//延迟加载
private Singleton(){};
public static Singleton getInstance() {
if (INSTANCE == null) {
synchronized (Singleton.class) {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
静态内部类
延迟加载,线程安全。
静态内部类属于被动引用,只有在调用时才加载。而JVM的类加载机制会保证只有一个线程成功执行<clinit>
方法,初始化类变量。
public class Singleton {
private Singleton(){}
private static class SingletonHolder {
private static Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
枚举
延迟加载,线程安全。
public class Singleton {
private Singleton(){}
//静态枚举类
static enum SingletonHolder {
INSTANCE;
private Singleton singleton;
private SingletonHolder() {
singleton = new Singleton();
}
public Singleton getInstance() {
return singleton;
}
}
//对外暴露的获取方法
public static Singleton getInstance() {
return SingletonHolder.INSTANCE.getInstance();
}
}
单例与反射
反射破坏单例:
public class Singleton {
private Singleton(){}
private static class SingletonHolder {
private static Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
public static void main(String[] args) throws Exception {
Class clazz = Singleton.class;
Constructor constructor = clazz.getDeclaredConstructor(null);
constructor.setAccessible(true);
Singleton o1 = (Singleton) constructor.newInstance();
Singleton o2 = (Singleton) constructor.newInstance();
System.out.println(o1);
System.out.println(o2);
System.out.println(o1 == o2);//false
}
}
一般而言有两种解决方案:
一:在私有构造中设置标志位,进行访问次数的判断,限制只能访问一次构造函数。
public class Singleton {
private static boolean flag = false;
private Singleton(){
if (flag == false) {
flag = true;
} else {
throw new RuntimeException("反射尝试破坏...");
}
}
private static class SingletonHolder {
private static Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
public static void main(String[] args) throws Exception {
Class clazz = Singleton.class;
Constructor constructor = clazz.getDeclaredConstructor(null);
constructor.setAccessible(true);
Singleton o1 = (Singleton) constructor.newInstance();
Singleton o2 = (Singleton) constructor.newInstance();
System.out.println(o1);
System.out.println(o2);
System.out.println(o1 == o2);
}
}
二:直接使用枚举式单例,枚举没有无参构造,天然单例防反射。
单例与序列化
序列化单例对象,再反序列化也可以破坏单例模式:
public class Singleton implements Serializable{
private static boolean flag = false;
private Singleton(){
if (flag == false) {
flag = true;
} else {
throw new RuntimeException("反射尝试破坏...");
}
}
private static class SingletonHolder {
private static Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
public static void main(String[] args) throws Exception {
Singleton source = Singleton.getInstance();
writeObject(source);//序列化
Singleton target = (Singleton) readObjcet();//反序列化
System.out.println(source);
System.out.println(target);
System.out.println(source == target);
}
public static void writeObject(Object o) {
FileOutputStream fileOut = null;
ObjectOutputStream out = null;
try {
fileOut = new FileOutputStream("test.txt");
out = new ObjectOutputStream(fileOut);
out.writeObject(o);
System.out.println("Serialized data is saved");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
fileOut.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static Object readObjcet() {
Object temp = null;
FileInputStream fileIn = null;
ObjectInputStream in = null;
try {
fileIn = new FileInputStream("test.txt");
in = new ObjectInputStream(fileIn);
temp = in.readObject();
System.out.println("Deserialized Object...");
return temp;
} catch (Exception e) {
e.printStackTrace();
return null;
}finally {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
fileIn.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
解决方案:添加readResolve()方法
,在方法中返回单例
public class Singleton implements Serializable{
private static boolean flag = false;
private Singleton(){
if (flag == false) {
flag = true;
} else {
throw new RuntimeException("反射尝试破坏...");
}
}
private static class SingletonHolder {
private static Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
protected Object readResolve() throws ObjectStreamException{
System.out.println("调用了readReslove...");
return SingletonHolder.INSTANCE;
}
}
原理:在ObjectInputStream
类的readOrdinaryObject()
方法中,对readResolve()
方法的存在做了判断,如果存在,则调用该方法获取返回值作为最终返回。
上一篇: PHP cron中的批处理
下一篇: php5新改动之短标记启用方法