什么是在Java中实现单例模式的有效方式?
什么是在Java中实现单例模式的有效方式?
使用枚举:
public enum Foo {
INSTANCE;
}
Joshua Bloch在Google I / O 2008上的Effective Java Reloaded讲座中解释了这种方法:链接到视频。 另请参阅演示文稿的幻灯片30-32(effective_java_reloaded.pdf):
实现可串行化单例的正确方法
public enum Elvis {
INSTANCE;
private final String[] favoriteSongs =
{ "Hound Dog", "Heartbreak Hotel" };
public void printFavorites() {
System.out.println(Arrays.toString(favoriteSongs));
}
}
编辑: “有效的Java”的在线部分说:
“这种方法在功能上等同于公共领域的方法,除了它更简洁,提供了免费的序列化机制,并且即使面对复杂的序列化或反射攻击,也可以提供对多重实例化的铁血保证。尚未被广泛采用, 单元素枚举类型是实现单例的最好方法 。“
根据使用情况,有几个“正确的”答案。
由于java5最好的办法是使用枚举:
public enum Foo {
INSTANCE;
}
在java5之前,最简单的情况是:
public final class Foo {
private static final Foo INSTANCE = new Foo();
private Foo() {
if (INSTANCE != null) {
throw new IllegalStateException("Already instantiated");
}
}
public static Foo getInstance() {
return INSTANCE;
}
public Object clone() throws CloneNotSupportedException{
throw new CloneNotSupportedException("Cannot clone instance of this class");
}
}
我们来看看代码。 首先,你希望这个班是最后的。 在这种情况下,我使用final
关键字让用户知道它是最终的。 然后,您需要将构造函数设为私有,以防止用户创建自己的Foo。 从构造函数中抛出异常会阻止用户使用反射来创建第二个Foo。 然后创建一个private static final Foo
字段来保存唯一的实例,并使用一个public static Foo getInstance()
方法来返回它。 Java规范确保构造函数仅在第一次使用类时调用。
当你有一个非常大的对象或重构建代码,并且还有其他可用的静态方法或可能在需要实例前使用的字段时,那么只有这样你才需要使用延迟初始化。
您可以使用private static class
来加载实例。 代码将如下所示:
public final class Foo {
private static class FooLoader {
private static final Foo INSTANCE = new Foo();
}
private Foo() {
if (FooLoader.INSTANCE != null) {
throw new IllegalStateException("Already instantiated");
}
}
public static Foo getInstance() {
return FooLoader.INSTANCE;
}
}
由于行private static final Foo INSTANCE = new Foo();
只有在实际使用FooLoader类时才会执行,这需要处理懒惰的实例化,并保证它是线程安全的。
当你也想要序列化你的对象时,你需要确保反序列化不会创建一个副本。
public final class Foo implements Serializable {
private static final long serialVersionUID = 1L;
private static class FooLoader {
private static final Foo INSTANCE = new Foo();
}
private Foo() {
if (FooLoader.INSTANCE != null) {
throw new IllegalStateException("Already instantiated");
}
}
public static Foo getInstance() {
return FooLoader.INSTANCE;
}
@SuppressWarnings("unused")
private Foo readResolve() {
return FooLoader.INSTANCE;
}
}
readResolve()
方法readResolve()
确保返回唯一的实例,即使在程序的上一次运行中序列化对象时也是如此。
Stu Thompson发布的解决方案在Java5.0及更高版本中有效。 但是我宁愿不使用它,因为我认为它很容易出错。
很容易忘记易变的陈述,并且很难理解为什么这是必要的。 没有易失性,由于双重检查锁定反模式,此代码不再是线程安全的。 请参阅Java并发实践的第16.2.4节中的更多内容。 简而言之:这种模式(在Java5.0之前或没有volatile语句)可能会返回一个对(仍然)处于不正确状态的Bar对象的引用。
这种模式是为了性能优化而发明的。 但是这实际上不再是真正的问题。 以下惰性初始化代码很快且更重要,更易于阅读。
class Bar {
private static class BarHolder {
public static Bar bar = new Bar();
}
public static Bar getBar() {
return BarHolder.bar;
}
}
链接地址: http://www.djcxy.com/p/13827.html
上一篇: What is an efficient way to implement a singleton pattern in Java?