单例模式

7

这个问题,和我之前的问题一样,参考了Effective Java。这次我有很多子问题。

  1. A privileged client can invoke the private constructor reflectively with the aid of the AccessibleObject.setAccessible() method. If you need to defend against this, modify the constructor.
    How, exactly, can a private constructor be invoked? And what is AccessibleObject.setAccessible()?

  2. What approach do you experts follow with singletons?

    // Approach A
    public class Test{
        public static final Test TestInstance = new Test();
        private Test(){ ... }
        .
        .
        .
    }
    
    
    // Approach B
    public class Test{
        private static final Test TestInstance = new Test();
        private Test(){ ... }
        public static Test getInstance() { return TestInstance; }
        .
        .
        .
    }
    

    Isn't the second approach more flexible, in case we have to make a check for new instances every time or the same instance every time?

  3. What if I try to clone the class/object?

  4. a single-element enum type is the best way to implement a singleton.
    Why? How?


4
Lol... B并不是单例。 - Cristian
从我的记忆中,我认为这本书直接解决了第二个问题。使用枚举而不是带有私有成员变量的常规类。(加1 @Cristian; 我假设那只是一个打字错误。) - Pops
@Cristian,抱歉 :) 希望现在可以了吗? - t0mcat
3
-1 因为提出了四个问题,尤其是第一个问题并不真正涉及单例模式。 - Pops
@ Lord Torgamus,Tim,将来会这样做。对此很抱歉。 - t0mcat
显示剩余2条评论
5个回答

11
一个特权客户端可以通过AccessibleObject.setAccessible方法反射调用私有构造函数,如果需要保护此功能,请修改构造函数。我的问题是:私有构造函数如何被调用?AccessibleObject.setAccessible是什么?显然,类本身可以调用私有构造函数(例如从静态工厂方法中)。Bloch所说的反射就是这样实现的:
import java.lang.reflect.Constructor;

public class PrivateInvoker {
    public static void main(String[] args) throws Exception{
        //compile error 
//      Private p = new Private();

        //works fine
        Constructor<?> con = Private.class.getDeclaredConstructors()[0];
        con.setAccessible(true);
        Private p = (Private) con.newInstance();
    } 
}

class Private {
    private Private() {
        System.out.println("Hello!");
    } 
}

2. 对于单例模式,您这些专家遵循哪种方法:

...

通常情况下,大多数人更喜欢第一种方法。如果您在返回一个新实例之前测试 TestInstance 是否为 null,第二种方法可以获得延迟加载的效果,但需要进行同步或线程不安全处理。

当您的第二个示例在声明时未将实例分配给 TestInstance 时,我编写了上述内容。正如现在所述,上述考虑已经无关紧要。

难道第二种方法不是更灵活吗?例如我们每次都需要检查新实例还是同一个实例?

这与灵活性无关,而是关于在何时产生创建(唯一)实例的成本。如果选择 a),则会在类加载时产生成本。通常情况下没问题,因为只有在需要时才会加载类。

当您的第二个示例在声明时未将实例分配给 TestInstance 时,我编写了上述内容。正如现在所述,在两种情况下,单例都将在类加载时创建。

如果我尝试克隆该类/对象会发生什么?

为了明显的原因,单例不应允许克隆。应该抛出 CloneNotSupportedException 异常,除非您以某种原因实现了Cloneable 接口。

单一元素枚举类型是实现单例模式的最佳方式。为什么?如何实现?

在书中有关于这方面的示例和理由。您不理解哪一部分?


2
一位特权客户可以通过AccessibleObject.setAccessible方法反射调用私有构造函数。如果您需要进行保护,请修改构造函数。我的问题是:如何确切地调用私有构造函数?以及什么是AccessibleObject.setAccessible?
您可以使用Java反射来调用私有构造函数。
专家们在单例模式中采用什么方法?
我喜欢使用枚举来实现这一点。这也在书中有提到。如果这不是一个选项,那么选择A会更简单,因为您不必检查或担心实例是否已经被创建。
如果我尝试克隆类/对象怎么办?
不确定您的意思是什么?您是指clone()还是其他我不知道的东西?
单元素枚举类型是实现单例模式的最佳方式。为什么?如何实现?
啊哈,这是我的答案。这是最好的方法,因为在这种情况下,Java编程语言保证了单例,而开发人员不必检查单例。这几乎就像单例是框架/语言的一部分。
编辑: 我之前没有看到它是一个getter。更新我的答案 - 最好使用getInstance等函数,因为您可以通过getter控制发生的事情,但是如果每个人都直接使用引用,则无法做到相同。考虑未来。如果您最终执行SomeClass.INTANCE,然后稍后想使其变为lazy以便不立即加载,则需要在使用它的所有位置进行更改。

我认为单例类应该是可克隆的。我不明白为什么不能这样做。你可能想在实现中抛出运行时异常或者只是“返回 this”。 - Amir Raminfar

2

1
单例(反)模式的第一条规则是不要使用它。 第二条规则是,不要使用它为了方便获取您希望许多其他对象共享的类的单个实例,特别是如果它是这些类的依赖项。改用依赖注入。 单例有有效的用途,但人们往往会因为它们太“容易”使用而滥用它们。 它们使得测试依赖于它们的类变得非常困难,并使系统缺乏灵活性。

至于您的问题,我认为123都可以回答“使用一个enum -singleton”。然后您就不必担心构造函数可访问性问题,clone()等。对于4 ,上述内容是支持它们的好论据。我还必须问,您是否阅读了《Effective Java》中明确回答您问题的部分?


0

单例模式懒加载示例: 主类:

public class Main {

    public static void main(String[] args) {
        System.out.println(Singleton.getInstance("first").value);
        System.out.println(Singleton.getInstance("second").value);
        System.out.println(Singleton.getInstance("therd").value);
    }
}

单例模式类:

public class Singleton {
    private static Singleton instance;
    public String value;

    private Singleton (String s){
        this.value =s;
    }
    public static Singleton getInstance(String param) {
        if (instance == null) 
            instance = new Singleton(param);
        return instance;    
    }
}

当启动应用程序时,控制台将包含以下字符串:

first
first
first

\|/ 73


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接