在Java中实现单例模式主要有几种经典的方法,下面列举其中几种常见的实现方式:
饿汉式(静态初始化) (线程安全)
public class Singleton {
// 使用 static final 常量保证类加载时就初始化
private static final Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return INSTANCE;
}
}
这种方式在类加载时即完成实例化,因此是线程安全的,并且只初始化一次。
懒汉式(非线程安全) (非线程安全,延迟加载)
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
这种方式不是线程安全的,如果多个线程同时调用 getInstance()
,可能导致多个实例。
懒汉式(线程安全,同步方法) (线程安全,但效率低)
public class Singleton {
private static Singleton instance;
private Singleton() {}
public synchronized static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
线程安全,但在多线程环境下,每次调用 getInstance()
方法都会进行同步,降低了性能。
双重检查锁定(DCL) (线程安全,延迟加载,高效)
public class Singleton {
private volatile static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
DCL 在确保线程安全的同时,避免了不必要的同步开销。只有当 instance
为 null
且需要创建实例时才会进行同步。
静态内部类 (线程安全,延迟加载)
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
类加载机制保证了静态内部类只会被加载一次,从而保证了 Singleton 的唯一性,同时也实现了延迟加载。
选择哪种实现方式取决于项目需求和性能要求。在大多数情况下,双重检查锁定或静态内部类的方式是比较推荐的。
在Java中,要创建一个不可变类,需要遵循以下原则:
类的实例字段应该是final的:这意味着一旦对象被构造完成,这些字段的值就不能再改变。
不提供任何可以修改字段的方法:包括公共方法、保护方法或包私有方法。
如果类包含任何可变引用类型(如列表、集合等),确保它们是不可变的或者在返回给客户端之前复制为不可变副本。
确保类不能被继承,可以通过声明为final
来实现。
下面是一个简单的不可变类的例子:
import java.util.Collections;
import java.util.List;
public final class ImmutablePerson {
private final String name;
private final int age;
private final List hobbies; // 假设hobbies列表在构造时已被转换为不可变
public ImmutablePerson(String name, int age, List hobbies) {
this.name = name;
this.age = age;
this.hobbies = Collections.unmodifiableList(new ArrayList<>(hobbies));
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public List getHobbies() {
return hobbies;
}
}
在这个例子中,ImmutablePerson
类具有三个final字段,分别是name
、age
和一个不可变的hobbies
列表。构造函数接受参数并将其设置为最终值。由于类被声明为final
,因此它不能被继承。同时,我们没有提供任何用于更改字段值的方法。这样就创建了一个不可变类。
EnumMap和EnumSet是Java中专门为枚举类型(enum)设计的数据结构,它们分别实现了Map接口和Set接口。
EnumMap 是一个高效且专用于存储枚举类型作为键的映射数据结构。它的内部实现利用了枚举类型的有序性和确定性,因此相比于一般的HashMap,它在性能上有所优化,因为它不需要进行散列计算和处理碰撞等开销。
当你创建一个EnumMap
时,你需要指定枚举类型作为其键的来源。由于枚举类型的实例数量是固定的,并且在编译时就已经知晓,所以EnumMap
能够预先分配适当的空间并直接将每个枚举实例与对应的值关联起来,从而提供了快速访问的能力。
例如:
public enum Color { RED, GREEN, BLUE }
EnumMap colorMap = new EnumMap<>(Color.class);
colorMap.put(Color.RED, "Red Color");
colorMap.put(Color.GREEN, "Green Color");
// ...
EnumSet 是一个高性能的集合类,它表示了一个枚举类型的无序、不重复的元素集合。与常规的Set不同,EnumSet针对枚举类型进行了高度定制,没有泛型参数,而是直接基于枚举类型工作。
如同EnumMap一样,由于枚举类型的特性,EnumSet的内部实现也十分高效,不需要像普通Set那样使用散列或其他复杂数据结构来存储元素。它通常使用位向量或其他紧凑的数据结构来存储枚举值,这使得EnumSet在空间和时间效率方面都非常出色。
创建一个EnumSet
可以通过以下方式:
public enum Action { TURN_LEFT, TURN_RIGHT, SHOOT }
EnumSet actions = EnumSet.of(Action.TURN_LEFT, Action.TURN_RIGHT);
actions.add(Action.SHOOT); // 或者使用 allOf 或 noneOf 创建预定义的集合
总之,EnumMap和EnumSet都充分利用了枚举类型的特性,提供了一种更安全、高效的方式来处理枚举类型的集合数据,尤其适合于枚举类型与数据之间存在一一对应关系或集合操作的场景。