请注意我已完成以下主题:
What is an efficient way to implement a singleton pattern in Java?
总而言之,写单身时需要考虑一些因素:
现在,正如上面的线程中所提到的,使用枚举来创建单例确保了上面提到的所有3个点。
以下是我写的示例代码
/*Singleton class using enum*/
package com.java.patterns;
public enum MemoryTasks {
INSTANCE;
public void performScheduleTasks(){
System.out.println("In performScheduleTasks().");
}
private MemoryTasks(){
System.out.println("In private constructor."+this.hashCode());
}
public int returnHashCodeOfInstance(){
return INSTANCE.hashCode();
}
}
/*Class to access private constructor of the Singleton*/
package com.java.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import com.java.patterns.MemoryTasks;
public class LaunchReflection {
public static void main(String[] args) {
launchRelectionAttack();
}
public static void launchRelectionAttack(){
Class vulnClass = null;
Constructor [] vulClassConstr = null;
Type [] vulClassConstrParamTypes = null;
try {
vulnClass = Class.forName("com.java.patterns.MemoryTasks");
vulClassConstr = vulnClass.getDeclaredConstructors();
for(Constructor constr : vulClassConstr){
vulClassConstrParamTypes = constr.getGenericParameterTypes();
System.out.println("Modifier private ? "+Modifier.isPrivate(constr.getModifiers()));
}
/*for(Type paramType : vulClassConstrParamTypes){
System.out.println(paramType.toString());
}*/
System.out.println(MemoryTasks.INSTANCE.returnHashCodeOfInstance());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
/*Class to write the enum to a file*/
package com.java.io.serialize;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import com.java.patterns.MemoryTasks;
public class ObjectWriter {
public static void main(String[] args) {
try {
writeSerObjectToFile();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void writeSerObjectToFile() throws IOException {
File file = null;
FileOutputStream fos = null;
ObjectOutputStream oos = null;
file = new File("D:/Omkar/Dump/SerObj");
try{
if(!file.exists()){
file.createNewFile();
}
fos = new FileOutputStream(file);
oos = new ObjectOutputStream(fos);
oos.writeObject(MemoryTasks.INSTANCE);
}catch(IOException e){
e.printStackTrace();
}
finally{
oos.close();
fos.close();
}
}
}
/*Class to read the serialized enum from file*/
package com.java.io.serialize;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import com.java.patterns.MemoryTasks;
public class ObjectRead {
public static void main(String[] args) {
readSerObjFromFile();
}
private static void readSerObjFromFile() {
File file = null;
FileInputStream fis = null;
ObjectInputStream ois = null;
file = new File("D:/Omkar/Dump/SerObj");
try {
fis = new FileInputStream(file);
if(fis.available() > 0){
ois = new ObjectInputStream(fis);
MemoryTasks instance = (MemoryTasks) ois.readObject();
System.out.println("Reading from serialised file : "+instance.returnHashCodeOfInstance());
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
如果我解释如何确保第2点和第3点,我会很高兴的!
答案 0 :(得分:1)
Java语言规范保证了这两者:
Enum中的最终克隆方法确保枚举常量永远不会 被克隆,并通过序列化机制进行特殊处理 确保永远不会创建重复实例 反序列化。禁止对枚举类型进行反射实例化。 总之,这四件事确保没有枚举类型的实例 存在于枚举常量定义的范围之外。
可以从http://docs.oracle.com/javase/7/docs/platform/serialization/spec/serial-arch.html#6469和java.lang.reflect API找到更多详细信息。
第2项:单例,如果可序列化,必须确保反序列化不会创建新实例通过枚举序列化的规范得到保证。
枚举常量的序列化与普通的可序列化不同 或可外部化的对象。枚举常量的序列化形式 仅由其名称组成;常量的字段值不是 出现在表格中。序列化枚举常量ObjectOutputStream 写入枚举常量名称方法返回的值。至 反序列化枚举常量,ObjectInputStream读取常量 来自流的名称;然后通过获得反序列化常数 调用java.lang.Enum.valueOf方法,传递常量的枚举 键入以及接收的常量名称作为参数。像其他人一样 可序列化或可外化的对象,枚举常量可以作为 随后出现在后面的参考目标 序列化流。
枚举常量序列化的过程不能 自定义:任何特定于类的writeObject,readObject, readObjectNoData,writeReplace和readResolve方法定义 在序列化和反序列化期间忽略枚举类型。
第3项:如果发生反射攻击,必须抛出异常/错误。
通过newInstance
方法创建新实例注定要失败:
IllegalArgumentException ...如果此构造函数与枚举有关 类型。
答案 1 :(得分:0)
通过浏览代码(我希望以后能更彻底地看一下):
2受保护,因为您无法创建枚举值的多个实例。 INSTANCE写出来......所以当你重读它时,如果它存在,你就无法创建另一个实例实例。很好的语言功能:)。