2010-12-06 3 views
26

Каков наилучший/правильный способ создания одноэлементного класса в java?Как создать одноэлементный класс

Одна из реализованных мной реализаций - это использование частного конструктора и метода getInstance().

package singleton; 

public class Singleton { 

    private static Singleton me; 

    private Singleton() { 
    } 

    public static Singleton getInstance() { 
     if (me == null) { 
      me = new Singleton(); 
     } 

     return me; 
    } 
} 

Но реализация терпит неудачу в следующем тесте

package singleton; 

import java.lang.reflect.Constructor; 
import java.lang.reflect.InvocationTargetException; 

public class Test { 

    /** 
    * @param args 
    * @throws NoSuchMethodException 
    * @throws SecurityException 
    * @throws InvocationTargetException 
    * @throws IllegalAccessException 
    * @throws InstantiationException 
    * @throws IllegalArgumentException 
    */ 
    public static void main(String[] args) throws SecurityException, 
      NoSuchMethodException, IllegalArgumentException, 
      InstantiationException, IllegalAccessException, 
      InvocationTargetException { 
     Singleton singleton1 = Singleton.getInstance(); 
     System.out.println(singleton1); 

     Singleton singleton2 = Singleton.getInstance(); 
     System.out.println(singleton2); 

     Constructor<Singleton> c = Singleton.class 
       .getDeclaredConstructor((Class<?>[]) null); 
     c.setAccessible(true); 
     System.out.println(c); 

     Singleton singleton3 = c.newInstance((Object[]) null); 
     System.out.println(singleton3); 

     if(singleton1 == singleton2){ 
      System.out.println("Variable 1 and 2 referes same instance"); 
     }else{ 
      System.out.println("Variable 1 and 2 referes different instances"); 
     } 
     if(singleton1 == singleton3){ 
      System.out.println("Variable 1 and 3 referes same instance"); 
     }else{ 
      System.out.println("Variable 1 and 3 referes different instances"); 
     } 
    } 

} 

Как решить эту проблему?

Спасибо

+0

*» Но выполнение не выполняется в следующем тестовом случае »* - Что именно вы имеете в виду под« ошибкой »? – cdhowie 2010-12-06 03:24:17

+0

Позволяет создавать множественное отражение броска экземпляра – 2010-12-06 03:25:44

+6

Лучший способ, в 99,99% случаев, основанный на моем многолетнем профессиональном опыте, состоит в том, чтобы ** не делать этого **. Как вы думаете, на самом деле нужен синглтон? – 2010-12-06 03:27:07

ответ

16

Согласно комментарию на ваш вопрос:

У меня есть файл свойств, содержащий некоторое количество ключей значение пары, которые нужны во всем приложении, поэтому я думал об одноэлементном классе. Этот класс загрузит свойства из файла и сохранит его, и вы сможете использовать его из любого места приложения.

Не используйте одноэлементный кабель. Вам, видимо, не нужна единовременная ленивая инициализация (здесь и есть синглтон).Вы хотите одноразовую прямое инициализацию. Просто сделайте его статическим и загрузите его в статический инициализатор.

E.g.

public class Config { 

    private static final Properties PROPERTIES = new Properties(); 

    static { 
     try { 
      PROPERTIES.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("config.properties")); 
     } catch (IOException e) { 
      throw new ExceptionInInitializerError("Loading config file failed.", e); 
     } 
    } 

    public static String getProperty(String key) { 
     return PROPERTIES.getProperty(key); 
    } 

    // ... 
} 
0

Я думаю, вы можете проверить, существует ли уже экземпляр в конструкторе и если существует сгенерирует исключение

if(me != null){ 
    throw new InstanceAlreadyExistsException(); 
} 
4

Если вы используете отражение проколоть инкапсуляцию, вы не должны быть удивлены когда поведение вашего класса изменяется неверным образом. Частные члены должны быть частными в классе. Используя отражение для доступа к ним, вы намеренно нарушаете поведение класса, и ожидаемый «дублирующий синглтон» ожидается.

Вкратце: не делайте этого.

Кроме того, вы можете подумать о создании экземпляра singleton в статическом конструкторе. Статические конструкторы синхронизированы и будут выполняться только один раз. Ваш текущий класс содержит условие гонки - если два отдельных потока вызывают getInstance(), когда он еще не был вызван, существует вероятность того, что будут созданы два экземпляра, один из которых будет эксклюзивным для одного из потоков, а другой станет например, что будущие звонки getInstance() вернутся.

-2

просто следовать диаграммы одноплодной класса модель,

SingletonClass - singletonObject: SingletonClass - SingletonClass() + GetObject(): SingletonClass

Ключевой момент,

  • Приватные конструктор
  • экземпляр вашего класса должен находиться внутри класса
  • обеспечивают функцию, чтобы вернуть ваш экземпляр

Некоторые код,

public class SingletonClass { 
    private static boolean hasObject = false; 
    private static SingletonClass singletonObject = null; 

    public static SingletonClass getObject() { 
     if (hasObject) { 
      return singletonObject; 
     } else { 
      hasObject = true; 
      singletonObject = new SingletonClass(); 
      return singletonObject; 
     } 
    } 

    private SingletonClass() { 
     // Initialize your object. 
    } 
} 
0

Лучший способ создания Singleton Class в java использует Enums.

Пример, как показано ниже:

import java.io.FileInputStream; 
import java.io.FileNotFoundException; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.io.ObjectInputStream; 
import java.io.ObjectOutputStream; 
import java.io.Serializable; 
import java.lang.reflect.Constructor; 
import java.lang.reflect.InvocationTargetException; 
import java.lang.reflect.Method; 

enum SingleInstance{ 
    INSTANCE; 

    private SingleInstance() { 
     System.out.println("constructor"); 
    } 
} 

public class EnumSingletonDemo { 

    public static void main (String args[]) throws FileNotFoundException, IOException, ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException 
    { 
     SingleInstance s=SingleInstance.INSTANCE; 
     SingleInstance s1=SingleInstance.INSTANCE; 

     System.out.println(s.hashCode() + " "+s1.hashCode());//prints same hashcode indicates only one instance created 

    //------- Serialization ------- 
    ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("sample.ser")); 
    oos.writeObject(s); 
    oos.close(); 

    //------- De-Serialization ------- 
    ObjectInputStream ois=new ObjectInputStream(new FileInputStream("sample.ser")); 
    SingleInstance s2=(SingleInstance) ois.readObject(); 

    System.out.println("Serialization :: "+s.hashCode()+" "+s2.hashCode());// prints same hashcodes because JVM handles serialization in case of enum(we dont need to override readResolve() method) 

    //-----Accessing private enum constructor using Reflection----- 

    Class c=Class.forName("SingleInstance"); 

    Constructor co=c.getDeclaredConstructor();//throws NoSuchMethodException 
    co.setAccessible(true); 
    SingleInstance newInst=(SingleInstance) co.newInstance();   

} 
} 

NoSuchMethodException выбрасывается, потому что мы не можем создать еще один экземпляр перечисления «SingleInstance» через частный конструктор с помощью отражения.

В случае сериализации перечисление по умолчанию реализует сериализуемый интерфейс.

3

Я реализую синглтон ниже.

От Singleton_pattern описывается wikiepdia с помощью инициализации по требованию держателя идиомы

Это решение является поточно-не требуя специальных языковых конструкций (т.е. volatile или synchronized

public final class LazySingleton { 
    private LazySingleton() {} 
    public static LazySingleton getInstance() { 
     return LazyHolder.INSTANCE; 
    } 
    private static class LazyHolder { 
     private static final LazySingleton INSTANCE = new LazySingleton(); 
    } 
    private Object readResolve() { 
     return LazyHolder.INSTANCE; 
    } 
} 
Смежные вопросы