这是一个单身人士类。
我想知道在这段代码中打破单例逻辑的方法
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;
}
}
答案 0 :(得分:7)
您只需要一个Employee
的实例?
class Employee{ // class starts
private Employee(){}
public static final Employee INSTANCE = new Employee();
}
不需要吸气剂,代码是安全的,只需使用:
Employee.INSTANCE
只是一个注释:Employee
听起来像一个值对象,看起来很难有单例值对象。
更新:似乎我终于明白了问题是什么。这个单身人士是安全的,除非使用一些非常重的艺术品:
使用反射制作构造函数public
(参见 Marko Topolnik 的精彩答案)。我想这可以通过一些安全管理器设置来避免
Java序列化 - 不太可能,单例必须实现Serializable
不同的类加载器(illustrated)
除了这些常见问题之外,你的代码很好,但并不漂亮。 1}}初始化保证在加载类时以原子方式运行。
答案 1 :(得分:5)
如果要访问多个Employee
实例,则只需将构造函数设置为public即可。如果您还希望此类的所有当前客户端每次都接收一个新实例而不是单例实例,那么将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
并配置适当的权限外,没有任何内容可以防止反射。
答案 2 :(得分:0)
您还应该在构造函数中执行以下操作,以避免通过反射创建对象
private Employee(){
if(emp!=null){
throws new InstantiationError("singleton breached ");
}
}
答案 3 :(得分: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();
最好和最简单的方法是根本不使用单身人士。