【问题标题】:Implementing Singleton in Java在 Java 中实现单例
【发布时间】:2011-05-19 06:05:39
【问题描述】:

请注意,我已经完成了以下线程:

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

总而言之,在编写单例时有一些重要的注意事项:

  1. 多线程访问不得导致多个实例
  2. 如果单例可序列化,则必须确保反序列化不会创建新实例
  3. 在反射攻击的情况下,必须抛出异常/错误。

现在,正如上面线程中所提到的,使用枚举来创建单例可以确保上面提到的所有 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 点,我会很高兴!

【问题讨论】:

    标签: java enums singleton


    【解决方案1】:

    这两者都由 Java 语言规范保证:

    枚举中的最终克隆方法确保枚举常量永远不会 被克隆,并通过序列化机制进行特殊处理 确保不会因为以下原因而创建重复的实例 反序列化。禁止枚举类型的反射实例化。 这四件事一起确保没有枚举类型的实例 存在超出枚举常量定义的范围。

    更多详情请见http://docs.oracle.com/javase/7/docs/platform/serialization/spec/serial-arch.html#6469java.lang.reflect API

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

    枚举常量的序列化方式与普通可序列化不同 或可外化的对象。枚举常量的序列化形式 仅由其名称组成;常量的字段值不是 呈现在表格中。要序列化枚举常量,ObjectOutputStream 写入枚举常量的 name 方法返回的值。到 反序列化一个枚举常量,ObjectInputStream 读取该常量 流中的名称;然后通过以下方式获得反序列化的常数 调用 java.lang.Enum.valueOf 方法,传递常量的枚举 类型连同接收到的常量名称作为参数。和其他一样 可序列化或可外部化的对象,枚举常量可以用作 随后出现的反向引用的目标 序列化流。

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

    第 3 项:在反射攻击的情况下,必须抛出异常/错误。

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

    IllegalArgumentException ... 如果此构造函数与枚举有关 输入。

    【讨论】:

      【解决方案2】:

      通过查看代码(希望稍后我会更彻底地查看它):

      2 受到保护,因为您不能创建枚举值的多个实例。 INSTANCE 被写出......所以当你读回它时,如果它存在,你就不能创建另一个 INSTANCE 实例。该语言的不错功能:)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-01-28
        • 2014-12-04
        • 2012-03-20
        • 1970-01-01
        • 1970-01-01
        • 2011-06-10
        • 2015-02-18
        相关资源
        最近更新 更多