今天在我的采访中,一位采访者让我写了一个Singleton课程。我给出了答案
public class Singleton {
private static Singleton ref;
private Singleton() {
}
public static Singleton getInstance() {
if (ref == null) {
ref = new Singleton();
}
return ref;
}
}
突然他告诉我这是写作课的老方法。任何人都可以帮助我,为什么他这样说。
答案 0 :(得分:42)
创建单身时,我首先想到的是enum
。我通常使用枚举来实现单例:
enum Singleton {
INSTANCE;
}
使用枚举获得的一个好处是使用序列化。
使用singleton类,您必须确保序列化和反序列化不会通过实现readResolve()
方法创建新实例,而枚举则不是这样。
使用class你应该像这样创建单身:
public final class Singleton implements Serializable {
// For lazy-laoding (if only you want)
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton() {
if (SingletonHolder.INSTANCE != null) {
// throw Some Exception
}
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
// To avoid deserialization create new instance
@SuppressWarnings("unused")
private Singleton readResolve() {
return SingletonHolder.INSTANCE;
}
}
答案 1 :(得分:14)
最新标准解决方案:
使用Managed Beans / CDI的核心Java
@ApplicationScoped
public class MySingleton { ... }
EJB Lite(JEE6)
@Singleton
public class MySingleton { ... }
先前建议(来自'Effective Java 2'):
使用enum,只有一个虚假的枚举常量,例如:实例。数据字段和方法可以是静态的或非静态的(实例) - 两者的行为相同,因为只有一个实例
优点:
缺点(与上述标准解决方案相比):
答案 2 :(得分:10)
你可以做到
public enum Singleton {
INSTANCE;
}
以及没有实例的实用程序类
public enum Utility {
;
public static void method();
}
答案 3 :(得分:8)
正如其他人已经指出的那样,enum模式现在被广泛认为是单身人士与老派方法的更好方法,但我只是想指出一个缺点。
我们有以下形式的单身人士:
public enum Foo {
INSTANCE;
}
已经存在了一段时间,工作得很好。然后在代码审查期间,我们看到了这一点:
public enum Foo {
INSTANCE,
ANOTHER;
}
在我们用湿鲭鱼将他砸到脸上之后,有问题的编码器看到了他的方式的错误,并且必须退出和/或重写大量的代码。是的,我们在它投入生产之前就抓住了它,但是必须做好工作来消除它。
我觉得这种类型的单身人士的弱点(尽管很小,也许很少见)与老派的方式相比。是的,你可以通过错误地实现它来打破任何模式,但对于编码人员而言,打破辛格尔顿的枚举似乎要比形成良好的老派单身人士更容易。
修改强>
为了完整性,这里是一个枚举单例,用于防止以后添加的其他值:
public enum Foo
{
INSTANCE;
// adding another type here will cause a runtime
static
{
if (Foo.values().length != 1)
{
throw new IllegalStateException("Not a Singleton.");
}
}
}
答案 4 :(得分:3)
这是因为您的解决方案不是线程安全的。
现代方法是将实例绑定到enum
值:
enum Singleton {
INSTANCE;
}
如果您想使用实例的延迟初始化,那么您可以使用ClassLoader
来保证线程安全:
public class Singleton {
private Singleton() { }
private static class SingletonHolder {
public static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
更多信息on Wikipedia
答案 5 :(得分:3)
他可能会寻找这个答案:
public class Singleton
{
private static Singleton ref;
static
{
ref = new Singleton();
}
private Singleton()
{
}
public static Singleton getInstance()
{
return ref;
}
}
注意静态块。这种方法可能很重,因为实例是在类加载时创建的。
答案 6 :(得分:3)
我写的Singleton看起来像这样:
@Service
class PersonService {
// implementation here
}
但我也喜欢enum的想法。实际上,除了上面的那个之外,我从不写(也不需要)一个单身人士。
答案 7 :(得分:3)
为什么你不能做到
public class SingletonSandBox {
private static SingletonSandBox instance = new SingletonSandBox();
private SingletonSandBox(){
}
public static SingletonSandBox getInstance(){
return instance;
}
}
并测试
public static void main(String[] args) {
SingletonSandBox sss1 = SingletonSandBox.getInstance();
SingletonSandBox sss2 = SingletonSandBox.getInstance();
System.out.println(sss1 == sss2);
}
我知道这是线程安全的并且比使用静态块更短。同样,运行时与静态块相比,先读取静态字段声明。
答案 8 :(得分:3)
有些时候我们可能只需创建一个子类的实例,就像Java Toolkit类一样。 示例1:仅创建一个子类的实例将详细说明它。请参阅:Singleton Design Patter in Java
答案 9 :(得分:2)
来自What is an efficient way to implement a singleton pattern in Java?
使用枚举:
public enum Foo
{
INSTANCE;
}
Joshua Bloch在他的“Effective Java”一书中解释了这种方法
答案 10 :(得分:2)
OPs初始方法的线程安全版本,加上没有其他人敢于建议同步语句。
final class Singleton
{
private static Object lock = new Object();
private static volatile Singleton instance = null;
private Singleton() { }
public static Singleton getInstance()
{
if(instance == null)
{
synchronized(lock)
{
if(instance == null)
{
instance = new Singleton();
}
}
}
return instance;
}
}
答案 11 :(得分:2)
调用此单例实现,
延迟初始化
但问题是这个实现不是线程安全的。
Here,您可以找到最佳的线程安全实现。
其他一些流行的Singleton实现也很少。一个是,
急切初始化
final class EagerIntializedSingleton {
private static final EagerIntializedSingleton instance = new EagerIntializedSingleton();
private EagerIntializedSingleton (){}
private static EagerIntializedSingleton getInsance() {
return instance;
}
}
但是在这里,单例类的实例是在类加载时创建的。 (这是IntelliJ IDE创建的默认单例类)
下一个流行的实现是,
静态块初始化
private static StaticBlockSingleton instance;
private StaticBlockSingleton(){}
static {
try {
instance = new StaticBlockSingleton();
catch(Exception e) {
.............
}
}
此实现类似于急切初始化,除了在静态块中创建类的实例,该块提供“异常处理”选项。 急切初始化和静态块初始化都会在使用之前创建实例,这不是最佳实践。
答案 12 :(得分:1)
可能是因为它没有使用“double-checked-locking”(正如其他人所说的那样),或者也可能是因为显然可以调用private constructor using reflection(如果安全策略允许的话) 。
调用没有参数的构造函数传递一个空数组。
package org.example;
public class Singleton {
private static final Object LOCK = new Object();
private static final Singleton SINGLETON = new Singleton();
private static volatile boolean init = false; // 'volatile' to prevent threads from caching state locally (prevent optimizing)
private Singleton() {
synchronized (LOCK) {
if( init == true) {
throw new RuntimeException("This is a singleton class!");
}
init=true;
}
}
public static Singleton obtainClassInstance() {
return SINGLETON;
}
}
package org.example;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class SimpleSingletonTester {
/**
* @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
{
Class[] parameterTypes = {};
Object[] initargs = {};
Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor(parameterTypes);
System.out.println( constructor.isAccessible() );
constructor.setAccessible(true);
System.out.println( constructor.isAccessible() );
System.out.println( constructor.newInstance(initargs) );
System.out.println( constructor.newInstance(initargs) );
}
}