在Java中实现Singleton

时间:2011-05-19 06:05:40

标签: java enums singleton

请注意我已完成以下主题:

What is an efficient way to implement a singleton pattern in Java?

总而言之,写单身时需要考虑一些因素:

  1. 多线程访问不得导致多个实例
  2. 单例(如果可序列化)必须确保反序列化不会创建新实例
  3. 如果发生反射攻击,则必须抛出异常/错误。
  4. 现在,正如上面的线程中所提到的,使用枚举来创建单例确保了上面提到的所有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点,我会很高兴的!

2 个答案:

答案 0 :(得分:1)

Java语言规范保证了这两者:

  

Enum中的最终克隆方法确保枚举常量永远不会   被克隆,并通过序列化机制进行特殊处理   确保永远不会创建重复实例   反序列化。禁止对枚举类型进行反射实例化。   总之,这四件事确保没有枚举类型的实例   存在于枚举常量定义的范围之外。

可以从http://docs.oracle.com/javase/7/docs/platform/serialization/spec/serial-arch.html#6469java.lang.reflect API找到更多详细信息。

第2项:单例,如果可序列化,必须确保反序列化不会创建新实例通过枚举序列化的规范得到保证。

  

枚举常量的序列化与普通的可序列化不同   或可外部化的对象。枚举常量的序列化形式   仅由其名称组成;常量的字段值不是   出现在表格中。序列化枚举常量ObjectOutputStream   写入枚举常量名称方法返回的值。至   反序列化枚举常量,ObjectInputStream读取常量   来自流的名称;然后通过获得反序列化常数   调用java.lang.Enum.valueOf方法,传递常量的枚举   键入以及接收的常量名称作为参数。像其他人一样   可序列化或可外化的对象,枚举常量可以作为   随后出现在后面的参考目标   序列化流。

     

枚举常量序列化的过程不能   自定义:任何特定于类的writeObject,readObject,   readObjectNoData,writeReplace和readResolve方法定义   在序列化和反序列化期间忽略枚举类型。

第3项:如果发生反射攻击,必须抛出异常/错误。

通过newInstance方法创建新实例注定要失败:

  

IllegalArgumentException ...如果此构造函数与枚举有关   类型。

答案 1 :(得分:0)

通过浏览代码(我希望以后能更彻底地看一下):

2受保护,因为您无法创建枚举值的多个实例。 INSTANCE写出来......所以当你重读它时,如果它存在,你就无法创建另一个实例实例。很好的语言功能:)。