Реализация Singleton в Java - PullRequest
5 голосов
/ 19 мая 2011

Обратите внимание, что я прошел через следующую ветку:

Как эффективен способ реализации одноэлементного шаблона в 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!

Ответы [ 2 ]

1 голос
/ 29 июля 2012

Оба из них гарантированы спецификацией языка Java:

Последний метод клонирования в Enum гарантирует, что константы enum никогда не будут клонированы, а специальная обработка механизмом сериализации гарантирует, что дублирующиеся экземпляры будутникогда не создавался в результате десериализации.Рефлексивная реализация типов перечислений запрещена.Вместе эти четыре вещи гарантируют, что не существует экземпляров типа enum, кроме тех, которые определены константами enum.

Более подробную информацию можно найти в http://docs.oracle.com/javase/7/docs/platform/serialization/spec/serial-arch.html#6469 и java.lang.reflect API .

Элемент 2: Синглтон, если он сериализуем, должен гарантировать, что десериализация не создает новый экземпляр гарантируется спецификацией сериализации enum.

Константы перечисления сериализуются иначе, чем обычные сериализуемые или экстернализуемые объекты.Сериализованная форма константы перечисления состоит исключительно из ее имени;Значения полей константы отсутствуют в форме.Чтобы сериализовать константу перечисления, ObjectOutputStream записывает значение, возвращаемое методом имени константы перечисления.Чтобы десериализовать константу перечисления, ObjectInputStream считывает имя константы из потока;десериализованная константа затем получается путем вызова метода java.lang.Enum.valueOf, передавая тип перечисления константы вместе с полученным именем константы в качестве аргументов.Как и другие сериализуемые или экстернализуемые объекты, константы перечисления могут функционировать как цели обратных ссылок, появляющихся впоследствии в потоке сериализации.

Процесс, с помощью которого сериализуются константы перечисления, не может быть настроен: любой специфичный для класса writeObject, readObject,Методы readObjectNoData, writeReplace и readResolve, определенные типами enum, игнорируются при сериализации и десериализации.

Элемент 3: В случае атаки с отражением должно быть выдано исключение / ошибка.

Создание новых экземпляров с помощью метода newInstance обречено на неудачу:

IllegalArgumentException ... если этот конструктор относится к типу перечисления.

0 голосов
/ 20 мая 2011

, взглянув на код (я надеюсь, что позже я рассмотрю его более подробно):

2 защищено, потому что вы не можете создать несколько экземпляров перечисляемого значения. INSTANCE записывается ... и поэтому, когда вы читаете его обратно, вы просто не можете создать другой экземпляр INSTANCE, если он существует. Приятная особенность языка :).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...