在Java中实现Singleton [英] Implementing Singleton in Java

查看:85
本文介绍了在Java中实现Singleton的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请注意,我已经通过以下线索:



什么是在Java中实现单例模式的有效方法?



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


  1. 多线程访问不得导致多个实例

  2. 单例,如果可序列化,则必须确保解除序列化不会创建新实例

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

现在,正如上面提到的那样,使用枚举来创建单例,可以确保上面提到的所有3点。



以下是我写的示例代码

  / * Singleton类使用枚举* / 
包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();
}
}

  / *访问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;
构造函数[] 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();
}
}
}

  / *将枚举写入文件的类* / 
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 {

文件文件= 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();
}
}
}

  / *从文件中读取序列化枚举的类* / 
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(){
文件文件= 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(从序列化文件读取+ instance.returnHashCodeOfInstance());
}
} catch(FileNotFoundException e){
// TODO自动生成的catch块
e.printStackTrace();
} catch(IOException e){
// TODO自动生成的catch块
e.printStackTrace();
} catch(ClassNotFoundException e){
// TODO自动生成的catch块
e.printStackTrace();
}
}
}

如果我会很高兴我解释了点2和3如何保证!

解决方案

这两个都是由Java语言规范保证的:


枚举中的最终克隆方法确保枚举常量永远不会
被克隆,并且序列化机制
的特殊处理确保作为
反序列化的结果,不会创建重复的实例。枚举类型的反射实例被禁止。
这四个东西一起确保没有枚举类型
的实例超出了枚举常量所定义的枚举。


可以从查找更多详细信息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:如果发生反射攻击,必须抛出异常/错误。 / p>

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


IllegalArgumentException ...如果此构造函数属于枚举
类型。



Please note that I have gone through the below thread :

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

To summarize, there are important considerations while writing a singleton :

  1. Multiple threads access must not lead to multiple instances
  2. The singleton,if serializable, must ensure that de-serialization doesn't create a new instance
  3. In case of a reflection attack, an exception/error must be thrown.

Now, as mentioned in the above thread too, using the enumeration for creating a singleton ensures all the 3 points mentioned above.

Below are the sample codes I wrote

/*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();
        }
    }
}

I would be glad if I am explained how the points 2 and 3 are ensured !

解决方案

Both of those are guaranteed by Java Language specification:

The final clone method in Enum ensures that enum constants can never be cloned, and the special treatment by the serialization mechanism ensures that duplicate instances are never created as a result of deserialization. Reflective instantiation of enum types is prohibited. Together, these four things ensure that no instances of an enum type exist beyond those defined by the enum constants.

More details can be found from http://docs.oracle.com/javase/7/docs/platform/serialization/spec/serial-arch.html#6469 and java.lang.reflect API.

Item 2: The singleton,if serializable, must ensure that de-serialization doesn't create a new instance is guaranteed by specification of enum serialization.

Enum constants are serialized differently than ordinary serializable or externalizable objects. The serialized form of an enum constant consists solely of its name; field values of the constant are not present in the form. To serialize an enum constant, ObjectOutputStream writes the value returned by the enum constant's name method. To deserialize an enum constant, ObjectInputStream reads the constant name from the stream; the deserialized constant is then obtained by calling the java.lang.Enum.valueOf method, passing the constant's enum type along with the received constant name as arguments. Like other serializable or externalizable objects, enum constants can function as the targets of back references appearing subsequently in the serialization stream.

The process by which enum constants are serialized cannot be customized: any class-specific writeObject, readObject, readObjectNoData, writeReplace, and readResolve methods defined by enum types are ignored during serialization and deserialization.

Item 3: In case of a reflection attack, an exception/error must be thrown.

Creating new instances via newInstance method is doomed to fail:

IllegalArgumentException ... if this constructor pertains to an enum type.

这篇关于在Java中实现Singleton的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆