先说观点: 在一般情况下, 饿汉式单例也不一定是立即加载的, 没必要特意使用写法复杂并且可能效率不高的懒汉模式。
Java中单例 (Singleton) 模式是一种广泛使用的设计模式。单例模式的主要作用是保证在Java程序中,某个类只有一个实例存在。
根据单例的初始化时机, java
单例可分为两大类别, 懒汉式和饿汉式。 (此外, 还有登记式, 不在本文讨论范围内)
饿汉式是在类加载器初始化该单例类时进行实例化。 常见方式如下:
public class EagerSingleton {
private static final EagerSingleton INSTANCE = new EagerSingleton();
private EagerSingleton() {
System.out.println("EagerSingleTon");
}
public static EagerSingleton getINSTANCE() {
return INSTANCE;
}
}
public enum EnumSingleton {
INSTANCE;
EnumSingleton() {
System.out.println("EnumSingleton");
}
}
DCL(双重校验锁定)单例
public class DoubleCheckLockSingleton {
private static volatile DoubleCheckLockSingleton instance;
private DoubleCheckLockSingleton() {
System.out.println("DoubleCheckLockSingleton");
}
public static DoubleCheckLockSingleton getInstance() {
if (instance == null) {
synchronized (DoubleCheckLockSingleton.class) {
if (instance == null) {
instance = new DoubleCheckLockSingleton();
}
}
}
return instance;
}
}
静态内部类式单例
public class HolderSingleton {
private HolderSingleton() {
System.out.println("HolderSingleton");
}
private HolderSingleton getInstance() {
return Holder.INSTANCE;
}
private static class Holder {
private static final HolderSingleton INSTANCE = new HolderSingleton();
}
}
在Java中, 静态的属性以及静态代码块, 是在类的初始化时进行加载的。 类被类加载器加载后, 并不会立即执行初始化的, 而是在特定时机才会去初始化, 具体的时机, jvm有相关规范, 大致如下:
从以上几点, 可以得出, 一个类, 在真正用到它时才会进行初始化, 才会去初始化(实例化)其静态属性和执行其静态代码快。 也就是说, 在一般情况下, 饿汉模式(本文中的静态工厂方法式饿汉和模具式单例), 对象的实例化的时候, 一般是在首次用到它的时候! 也就是说, 它是懒加载的。
当然, 只是一般情况下是如此, 也有例外, 如果项目中有骚操作会反射该单例类, 也会引起初始化。 另外不要在该类中定义其它静态属性(不包括基础数据类型常量和String类型常量, 它们不会引起类的初始化)和方法, 避免首次使用该类时是使用它们导致的初始化。
kotlin 官方文档推荐的单例模式:对象声明本质上也是常规的饿汉模式
一个对象声明式单例
object ObjClassTest {
init {
println("haha init")
}
fun test() {
println("hehe test")
}
}
编译再反编译成Java代码
@Metadata(bv = {1, 0, 3}, d1 = {"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0000\bÆ\u0002\u0018\u00002\u00020\u0001B\u0007\b\u0002¢\u0006\u0002\u0010\u0002J\u0006\u0010\u0003\u001a\u00020\u0004¨\u0006\u0005"}, d2 = {"Lxyz/liut/kotlin/lang/kt1_object_class/comm/ObjClassTest;", "", "()V", "test", "", "JavaCode"}, k = 1, mv = {1, 1, 15})
/* compiled from: ObjClassTest.kt */
public final class ObjClassTest {
public static final ObjClassTest INSTANCE = new ObjClassTest();
static {
System.out.println("haha init");
}
private ObjClassTest() {
}
public final void test() {
System.out.println("hehe test");
}
}
在一般情况下, 饿汉模式是最好的单例方式, 写法简单、 性能好、 线程安全。