现在更好的Java单例模式? [英] The better Java singleton pattern nowadays?

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

问题描述

你知道,自从Java 5发布以来,Java中写入Singleton模式的推荐方式是使用枚举。

You know, that since Java 5 is released the recommended way to write Singleton pattern in Java is using enum.

public enum Singleton {
    INSTANCE;
}

但是,我不喜欢的是强制客户端使用Singleton.INSTANCE来访问单例实例。
也许是在普通班级内隐藏Singleton的更好的方法,并提供更好的访问单一设施:

But, what I don't like in this - is to force the client to use Singleton.INSTANCE in order to have access to the singleton instance. Maybe, the better way to hide Singleton inside the ordinary class, and provide more better access to singleton facilities:

public class ApplicationSingleton {
    private static enum Singleton {
        INSTANCE;               

        private ResourceBundle bundle;

        private Singleton() {
            System.out.println("Singleton instance is created: " + 
            System.currentTimeMillis());

            bundle = ResourceBundle.getBundle("application");
        }

        private ResourceBundle getResourceBundle() {
            return bundle;
        }

        private String getResourceAsString(String name) {
            return bundle.getString(name);
        }
    };

    private ApplicationSingleton() {}

    public static ResourceBundle getResourceBundle() {
        return Singleton.INSTANCE.getResourceBundle();
    }

    public static String getResourceAsString(String name) {
        return Singleton.INSTANCE.getResourceAsString(name);
    }
}

所以,客户端现在可以简单地写: p>

So, the client now can simply write:

ApplicationSingleton.getResourceAsString("application.name")

例如


哪个好多了:

for example. Which is much better then:

Singleton.INSTANCE.getResourceAsString("application.name")

所以,问题是:它是正确的方法吗?这个代码是否有任何问题(线程安全?)?它具有枚举单身模式的所有优点吗?看来,这两个世界都要好一些。你怎么看?有什么更好的方法来实现吗?
谢谢。

So, the question is: Is it the right way to do it? Does this code have any issues (thread-safety?)? Does it have all the advantages the "enum singleton" pattern has? It seems that it takes the better from the both world. What do you think? Is any better way to achieve this? Thanks.

编辑

@all

Singleton模式的首先枚举使用是有效Java第2版中提到:维基百科:Java Enum Singleton 。我完全同意我们应该尽量减少单身人士的使用,但是我们不能完全摆脱他们的困扰。
在我提供另一个例子之前,让我说,ResourceBundle的第一个例子是只是一个例子,示例本身(和类名)不是来自真实的应用程序。但是,需要说的是,我不知道ResourceBundle的缓存管理,感谢那条信息)

EDIT
@all
First of all enum usage for Singleton pattern was mentioned in the Effective Java, 2nd Edition: wikipedia:Java Enum Singleton. I totally agree that we should minimize Singleton usage as much as possible, but we can't totally go away from them.
Before I provide another example, let me say, that the first example with ResourceBundle is just a case, the example itself (and the classes names) are not from the real application. But, need to say, that I didn't know about ResourceBundle cache management, thanks for that piece of information )

下面有两种不同的方法对于Singleton模式,第一个是Enum的新方法,第二个是我们以前使用的标准方法。我试图显示他们之间的显着差异。

Below, there are 2 different approaches for Singleton pattern, the first is the new approach with Enum, and the second is the standard approach most of us used before. And I try to show significant differences between them.

使用枚举的单例:

ApplicationSingleton类是:

Singleton using Enum:
ApplicationSingleton class is:

public class ApplicationSingleton implements Serializable {
    private static enum Singleton {
        INSTANCE;               

        private Registry registry;

        private Singleton() {
            long currentTime = System.currentTimeMillis(); 
            System.out.println("Singleton instance is created: " + 
                    currentTime);

            registry = new Registry(currentTime);
        }

        private Registry getRegistry() {
            return registry;
        }

        private long getInitializedTime() {
            return registry.getInitializedTime();
        }

        private List<Registry.Data> getData() {
            return registry.getData();
        }
    };

    private ApplicationSingleton() {}

    public static Registry getRegistry() {
        return Singleton.INSTANCE.getRegistry();
    }

    public static long getInitializedTime() {
        return Singleton.INSTANCE.getInitializedTime();
    }

    public static List<Registry.Data> getData() {
        return Singleton.INSTANCE.getData();
    }    
}

注册类是:

public class Registry {
    private List<Data> data = new ArrayList<Data>();
    private long initializedTime;

    public Registry(long initializedTime) {
        this.initializedTime = initializedTime;
        data.add(new Data("hello"));
        data.add(new Data("world"));
    }

    public long getInitializedTime() {
        return initializedTime;
    }

    public List<Data> getData() {
        return data;
    }

    public class Data {      
        private String name;

        public Data(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }                   
    }
}

和测试类: p>

And test class:

public class ApplicationSingletonTest {     

    public static void main(String[] args) throws Exception {                   

        String rAddress1 = 
            ApplicationSingleton.getRegistry().toString();

        Constructor<ApplicationSingleton> c = 
            ApplicationSingleton.class.getDeclaredConstructor();
        c.setAccessible(true);
        ApplicationSingleton applSingleton1 = c.newInstance();
        String rAddress2 = applSingleton1.getRegistry().toString();

        ApplicationSingleton applSingleton2 = c.newInstance();
        String rAddress3 = applSingleton2.getRegistry().toString();             


        // serialization

        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(byteOut);
        out.writeObject(applSingleton1);

        ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(byteOut.toByteArray()));
        ApplicationSingleton applSingleton3 = (ApplicationSingleton) in.readObject();

        String rAddress4 = applSingleton3.getRegistry().toString();

        List<Registry.Data> data = ApplicationSingleton.getData();
        List<Registry.Data> data1 = applSingleton1.getData();
        List<Registry.Data> data2 = applSingleton2.getData();
        List<Registry.Data> data3 = applSingleton3.getData();

        System.out.printf("applSingleton1=%s, applSingleton2=%s, applSingleton3=%s\n", applSingleton1, applSingleton2, applSingleton3);
        System.out.printf("rAddr1=%s, rAddr2=%s, rAddr3=%s, rAddr4=%s\n", rAddress1, rAddress2, rAddress3, rAddress4);
        System.out.printf("dAddr1=%s, dAddr2=%s, dAddr3=%s, dAddr4=%s\n", data, data1, data2, data3);
        System.out.printf("time0=%d, time1=%d, time2=%d, time3=%d\n",
                ApplicationSingleton.getInitializedTime(),
                applSingleton1.getInitializedTime(), 
                applSingleton2.getInitializedTime(),
                applSingleton3.getInitializedTime());
    }

}

这里是输出: / p>

And here is the output:

Singleton instance is created: 1304067070250
applSingleton1=ApplicationSingleton@18a7efd, applSingleton2=ApplicationSingleton@e3b895, applSingleton3=ApplicationSingleton@6b7920
rAddr1=Registry@1e5e2c3, rAddr2=Registry@1e5e2c3, rAddr3=Registry@1e5e2c3, rAddr4=Registry@1e5e2c3
dAddr1=[Registry$Data@1dd46f7, Registry$Data@5e3974], dAddr2=[Registry$Data@1dd46f7, Registry$Data@5e3974], dAddr3=[Registry$Data@1dd46f7, Registry$Data@5e3974], dAddr4=[Registry$Data@1dd46f7, Registry$Data@5e3974]
time0=1304067070250, time1=1304067070250, time2=1304067070250, time3=1304067070250

应该提到什么:


What should to mention:


  1. 单例实例只创建一次

  2. 是的,有几个不同的ApplicationSingletion实例,但是所有包含相同的单例实例

  3. 注册表内部da对于所有不同的 ApplicationSingleton实例,ta相同

  1. Singleton instance was created only once
  2. Yes, there are several different instances of ApplicationSingletion, but all of them contain the same Singleton instance
  3. Registry internal data is the same for all different ApplicationSingleton instance

所以,总结 :枚举方法工作正常,并通过反射攻击防止重复的Singleton创建,并在序列化后返回相同的实例。

So, to summarize: Enum approach works fine and prevent duplicate Singleton creation through reflection attack, and return the same instance after being serialized.

使用标准方法的单身人士


ApplicationSingleton类是:

Singleton using standard approach:
ApplicationSingleton class is:

public class ApplicationSingleton implements Serializable {
    private static ApplicationSingleton INSTANCE;

    private Registry registry;

    private ApplicationSingleton() {
        try {
            Thread.sleep(10);
        } catch (InterruptedException ex) {}        
        long currentTime = System.currentTimeMillis();
        System.out.println("Singleton instance is created: " + 
                currentTime);
        registry = new Registry(currentTime);
    }

    public static ApplicationSingleton getInstance() {
        if (INSTANCE == null) {
            return newInstance();
        }
        return INSTANCE;

    }

    private synchronized static ApplicationSingleton newInstance() {
        if (INSTANCE != null) {
            return INSTANCE;
        }
        ApplicationSingleton instance = new ApplicationSingleton();
        INSTANCE = instance;

        return INSTANCE;
    }

    public Registry getRegistry() {
        return registry;
    }

    public long getInitializedTime() {
        return registry.getInitializedTime();
    }

    public List<Registry.Data> getData() {
        return registry.getData();
    }
}

注册表类是(请注意,注册表和数据类明确应该实现Serializable序列化工作):

Registry class is (note that Registry and Data classes explicitly should implement Serializable in order serialization to work):

//now Registry should be Serializable in order serialization to work!!!
public class Registry implements Serializable {
    private List<Data> data = new ArrayList<Data>();
    private long initializedTime;

    public Registry(long initializedTime) {
        this.initializedTime = initializedTime;
        data.add(new Data("hello"));
        data.add(new Data("world"));
    }

    public long getInitializedTime() {
        return initializedTime;
    }

    public List<Data> getData() {
        return data;
    }

    // now Data should be Serializable in order serialization to work!!!
    public class Data implements Serializable {      
        private String name;

        public Data(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }                   
    }
}

和ApplicationSingletionTest类(大部分相同):

And ApplicationSingletionTest class is (mostly the same):

public class ApplicationSingletonTest {     

    public static void main(String[] args) throws Exception {

        String rAddress1 = 
            ApplicationSingleton.getInstance().getRegistry().toString();

        Constructor<ApplicationSingleton> c = 
            ApplicationSingleton.class.getDeclaredConstructor();
        c.setAccessible(true);
        ApplicationSingleton applSingleton1 = c.newInstance();
        String rAddress2 = applSingleton1.getRegistry().toString();

        ApplicationSingleton applSingleton2 = c.newInstance();
        String rAddress3 = applSingleton2.getRegistry().toString();             


        // serialization

        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(byteOut);
        out.writeObject(applSingleton1);

        ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(byteOut.toByteArray()));
        ApplicationSingleton applSingleton3 = (ApplicationSingleton) in.readObject();

        String rAddress4 = applSingleton3.getRegistry().toString();

        List<Registry.Data> data = ApplicationSingleton.getInstance().getData();
        List<Registry.Data> data1 = applSingleton1.getData();
        List<Registry.Data> data2 = applSingleton2.getData();
        List<Registry.Data> data3 = applSingleton3.getData();

        System.out.printf("applSingleton1=%s, applSingleton2=%s, applSingleton3=%s\n", applSingleton1, applSingleton2, applSingleton3);
        System.out.printf("rAddr1=%s, rAddr2=%s, rAddr3=%s, rAddr4=%s\n", rAddress1, rAddress2, rAddress3, rAddress4);
        System.out.printf("dAddr1=%s, dAddr2=%s, dAddr3=%s, dAddr4=%s\n", data, data1, data2, data3);
        System.out.printf("time0=%d, time1=%d, time2=%d, time3=%d\n",
                ApplicationSingleton.getInstance().getInitializedTime(),
                applSingleton1.getInitializedTime(), 
                applSingleton2.getInitializedTime(),
                applSingleton3.getInitializedTime());
    }

}

这里是输出: / p>

And here is the output:

Singleton instance is created: 1304068111203
Singleton instance is created: 1304068111218
Singleton instance is created: 1304068111234
applSingleton1=ApplicationSingleton@16cd7d5, applSingleton2=ApplicationSingleton@15b9e68, applSingleton3=ApplicationSingleton@1fcf0ce
rAddr1=Registry@f72617, rAddr2=Registry@4f1d0d, rAddr3=Registry@1fc4bec, rAddr4=Registry@1174b07
dAddr1=[Registry$Data@1256ea2, Registry$Data@82701e], dAddr2=[Registry$Data@1f934ad, Registry$Data@fd54d6], dAddr3=[Registry$Data@18ee9d6, Registry$Data@19a0c7c], dAddr4=[Registry$Data@a9ae05, Registry$Data@1dff3a2]
time0=1304068111203, time1=1304068111218, time2=1304068111234, time3=1304068111218

应该提到什么:


  1. Singleton实例创建了几个!次

  2. 所有注册表对象都是具有自己数据的不同对象

总结:反射攻击的标准方法很弱,并且在序列化后返回不同的实例,但是使用相同的数据可以返回。

So, to summarize: Standard approach is weak for reflection attack, and return different instance after being serialized, but yes with the same data.

所以,似乎Enum方法更加坚固耐用。而现在Java中使用Singleton模式的推荐方法呢?你觉得怎么样?

有趣的事实是为了解释:为什么enum中的对象可以用它自己的类序列化不能实现Serializable?它是功能还是错误?

So, it seems that Enum approach is more solid and robust. And is it the recommended way for using Singleton pattern in Java nowadays? What do you think?
Interesting fact to explain: why objects inside enum can be serialized with its owning class does not implement Serializable? Is it feature or bug?

推荐答案

我不知道这些枚举是Java构建单例的方法。但是,如果要这样做,您可以直接使用枚举。没有任何好的理由将单例封装在一堆静态成员方法之后;一旦你这样做了,你可以写一个静态类与私人静态成员开始。

I was not aware that enums were the Java way to build singletons these days. But if you're going to do it that way, you may as well just use the enum directly. I don't see any good reason to encapsulate the singleton behind a bunch of static member methods; once you've done that, you may as well have written a static class with private static members to begin with.

这篇关于现在更好的Java单例模式?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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