基于SO问题编写的最佳单例实现在Java中 - 即使用枚举创建单例 - 有什么区别/优点/缺点(构造函数省略)
public enum Elvis {
INSTANCE;
private int age;
public int getAge() {
return age;
}
}
Run Code Online (Sandbox Code Playgroud)
然后打电话 Elvis.INSTANCE.getAge()
和
public enum Elvis {
INSTANCE;
private int age;
public static int getAge() {
return INSTANCE.age;
}
}
Run Code Online (Sandbox Code Playgroud)
然后打电话 Elvis.getAge()
所以,我看着这个"枚举"类型,并且它种好像一个华而不实的数组/ArrayList/ List给我.它的用途究竟是什么?
在Java中使用标志时,我看到了两种主要方法.一个使用int值和一行if-else语句.另一种是使用枚举和case-switch语句.
我想知道在使用enums和ints for flags之间内存使用和速度方面是否存在差异?
在JDK1.5之前的Java中,"Typesafe Enum"模式是实现只能获取有限数量值的类型的常用方法:
public class Suit {
private final String name;
public static final Suit CLUBS =new Suit("clubs");
public static final Suit DIAMONDS =new Suit("diamonds");
public static final Suit HEARTS =new Suit("hearts");
public static final Suit SPADES =new Suit("spades");
private Suit(String name){
this.name =name;
}
public String toString(){
return name;
}
}
Run Code Online (Sandbox Code Playgroud)
(参见例如Bloch的Effective Java的第21项).
现在在JDK1.5 +中,"官方"方式显然是使用enum:
public enum Suit {
CLUBS("clubs"), DIAMONDS("diamonds"), HEARTS("hearts"), SPADES("spades");
private final String name;
private Suit(String name) {
this.name = name;
}
} …Run Code Online (Sandbox Code Playgroud) 这是一个非常广泛的枚举单例代码:
public enum enumClazz{
INSTANCE
enumClazz(){
//do something
}
}
Run Code Online (Sandbox Code Playgroud)
和一堆地方说这是一个懒惰的初始化.但在阅读" Java虚拟机内部"第7章- 类型的生命周期后,我感到困惑:
Java虚拟机规范为类和接口加载和链接的时序提供了实现的灵活性,但严格定义了初始化的时间.所有实现必须在其第一次活动使用时初始化每个类或接口.以下六种情况符合有效用途:
- 创建一个类的新实例(在字节码中,执行新指令.或者,通过隐式创建,反射,克隆或反序列化.)
- 调用由类声明的静态方法(在字节码中,执行invokestatic指令)
- 使用或赋值由类或接口声明的静态字段,除了最终的静态字段并由编译时常量表达式初始化(在字节码中,执行getstatic或putstatic指令)
- 在Java API中调用某些反射方法,例如Class类中的方法或java.lang.reflect包中的类
- 初始化类的子类(类的初始化需要事先初始化其超类.)
- 在Java虚拟机启动时将类指定为初始类(使用main()<方法)
粗体风格的第三点澄清了如果字段是static final,则字段的初始化发生在编译时.同样,INSTANCEin enumClazz隐含地等于public static final并符合第三点.
如果我的理解错了,有人可以纠正我吗?
在阅读了关于枚举的一些问题和答案后,我发现它并没有真正有用......
它是类和变量之间的东西,但我知道在哪里可以使用它,所以它比一个类或一些变量更有用.
我已经找到了三种实例化Singleton的方法,但我怀疑它们中是否有最好的方法.我在多线程环境中使用它们并且更喜欢惰性实例化.
样本1:
private static final ClassName INSTANCE = new ClassName();
public static ClassName getInstance() {
return INSTANCE;
}
Run Code Online (Sandbox Code Playgroud)
样本2:
private static class SingletonHolder {
public static final ClassName INSTANCE = new ClassName();
}
public static ClassName getInstance() {
return SingletonHolder.INSTANCE;
}
Run Code Online (Sandbox Code Playgroud)
样本3:
private static ClassName INSTANCE;
public static synchronized ClassName getInstance()
{
if (INSTANCE == null)
INSTANCE = new ClassName();
return INSTANCE;
}
Run Code Online (Sandbox Code Playgroud)
我正在使用ATM的项目到处都使用Sample 2,但我更喜欢Sample 3.还有Enum版本,但我只是不明白.
这里的问题是 - 在哪些情况下我应该/不应该使用这些变化中的任何一种?我不是在寻找冗长的解释(关于这一点还有很多其他主题,但它们最终都变成了争论IMO),我希望用几句话来理解它.
使用整数标志和按位运算是减少高容量对象的内存占用的有效方法吗?
记忆足迹
我的理解是,通常将a boolean存储为intJVM实现中的一个.它是否正确?在这种情况下,32个标志肯定代表了大量的内存占用减少.
虽然JVM实现当然有所不同,但情况可能并非总是如此.
性能
我的理解是,CPU是非常数字驱动的,并且按位操作与计算中的内容一样高效.
在布尔运算中使用按位运算会有性能损失 - 甚至是增益吗?
备择方案
有没有更好的方法来完成同样的事情?Enum是否允许组合标志,即FLAGX = FLAG1 | FLAG2?
注意,最后一个方法propogateMove()是递归的,可能每秒被调用数百次,并且直接影响我们应用程序的响应性,因此使用标志来避免逻辑位并调用其他方法.
// FLAGS helper functions
private final void setclear(int mask, boolean set) { if (set) set(mask); else clear(mask); }
private final void set(int mask) { flags |= mask; }
private final void clear(int mask) { flags &= ~mask; }
private final boolean test(int mask) { return ((flags & mask) == mask); }
// Flags //////////////////////////////////////////////////////////////////////
private …Run Code Online (Sandbox Code Playgroud)