1

这是一个单例类。

我想知道我们在这段代码中打破单例逻辑的方法

class Employee{ // class starts
    private Employee(){} // private constructor

    private static Employee emp; 
    /*static block*/

    static {    
        if (emp==null)
        {
            emp=new Employee(); 
        }
    }      
    /* static method*/          
    public static Employee getEmployee()
    {   
        return emp;  
    }
}
4

4 回答 4

7

你只想要一个Employee?

class Employee{ // class starts
   private Employee(){}
   public static final Employee INSTANCE = new Employee(); 
}

不需要吸气剂,代码是安全的,只需使用:

Employee.INSTANCE

只是一个注释:Employee听起来像一个值对象,拥有单例值对象似乎很不寻常。

更新:似乎我终于明白了这个问题是关于什么的。这个单例是安全的,除非使用了一些非常重的火炮:

  • 使用反射制作构造函数public(参见Marko Topolnik的精彩回答)。我想这可以通过一些安全管理器设置来避免

  • Java 序列化 - 不太可能,单例必须实现Serializable

  • 不同的类加载器(图示

除了这些常见的陷阱之外,您的代码还不错,但并不漂亮。static初始化保证在加载类时以原子方式运行一次。

于 2012-10-25T10:26:04.110 回答
5

如果您想访问多个Employee实例,将构造函数公开就足够了。如果您还希望此类的所有当前客户端每次都接收一个新实例而不是单例实例,那么实现getEmployee(){ return new Employee(); }. 然后,您可以删除private static变量和static初始化程序块。

第三,如果您无法更改源代码,但Employee无论如何都想获得很多实例,那么唯一的方法就是求助于反射:

try {
  final Constructor<Employee> c = Employee.class.getDeclaredConstructor();
  c.setAccessible(true);
  final Employee e = c.newInstance();
} catch (Exception e) { throw new RuntimeException(e); }

第四,如果您的问题真的是关于保护单例免受破坏,并且单例确实是可序列化的(未在您的示例中显示),那么该类必须实现readResolve反序列化机制中隐含的方法:

protected Object readResolve() {
    return emp;
}

SecurityManager请注意,除了启用 a和配置适当的权限外,没有什么可以防止反射。

于 2012-10-25T10:30:08.323 回答
1

您还应该在构造函数中执行以下操作以避免通过反射创建对象

private Employee(){ 
if(emp!=null){
   throws new InstantiationError("singleton breached ");
}

}

于 2012-10-25T13:33:28.113 回答
0

想要多个实例?小菜一碟:

    Constructor<Employee> constructor = Employee.class.getDeclaredConstructor();
    constructor.setAccessible(true);
    Employee wtf = constructor.newInstance();
    assertSame(wtf, Employee.getEmployee());

有两种方法可以保护单例。第二个最好的方法是使用单元素枚举:

enum Singleton {
    INSTANCE;
    void doSomething(){
        System.out.println("Doing something");
    }
}

// Usage:
Singleton.INSTANCE.doSomething();

最好和最简单的方法是根本不使用单例。

于 2012-10-25T13:50:28.273 回答