延迟加载(Lazy Loading)
什么是延迟加载?
延迟加载是一种设计思想,指推迟对象的创建直到第一次使用时才初始化。这种技术可以:
- 减少内存占用
- 提升应用启动速度
- 优化资源利用
应用场景:
- 单例模式(懒汉式)
- ORM 框架(Hibernate 懒加载)
- 前端开发(图片懒加载)
- Spring Bean(延迟初始化)
在单例模式中,使用延迟加载的实现方式被称为懒汉式。
// 内部静态类示例
public class Dog {
private Dog() {}
public static Dog getDogInstance() {
return DogFunc.INSTANCE;
}
private static class DogFunc {
private static final Dog INSTANCE = new Dog();
}
}
实现方式
延迟加载有多种实现方式:
1. 普通静态方法
通过设置静态方法检查实例是否存在来创建。
存在线程安全问题,可以通过 synchronized 解决。
public class Dog {
private static Dog instance = null;
public static Dog getInstance() { //同步在这里 加 synchronized
if (instance == null) {
instance = new Dog();
return instance;
}
return instance;
}
}
2. 内部静态类
利用内部类不会随外部类同步创建而创建的特性实现。
简单高效,同时 final 关键字保证了在创建实例时的线程安全问题
final提供两个保证: 引用不可变:防止重新赋值可见性保证
- JMM(Java 内存模型)保证 final 字段初始化完成后,其他线程一定能看到正确值
- 禁止指令重排序
- 防止读到"半初始化"对象
3. 双重检查(DCL)
通过双重检查,第二次判断时加锁来保证线程安全。
相比直接在方法上加锁效率更高。
public class Dog {
private static volatile Dog instance;
public static Dog getInstance() {
if (instance == null) { // 第一次检查
synchronized (Dog.class) {
if (instance == null) { // 第二次检查
instance = new Dog(); // volatile 禁止重排序
}
}
}
return instance;
}
}
⚠️注意点
错误 1:非静态内部类
public class Dog {
private Dog() {}
public static Dog getInstance() {
return DogHolder.INSTANCE;
}
private class DogHolder { // 缺少 static
private static final Dog INSTANCE = new Dog();
}
}
// 编译错误(Java 16-):
// error: non-static inner class cannot have static declarations
错误 2:访问点不是静态
public class Dog {
private Dog() {}
public Dog getInstance() { // 缺少 static
return DogHolder.INSTANCE;
}
private static class DogHolder {
private static final Dog INSTANCE = new Dog();
}
}
// 编译错误:
// Dog.getInstance(); // 非静态方法无法通过类名调用
错误 3:忘记 final
public class Dog {
private Dog() {}
public static Dog getInstance() {
return DogHolder.INSTANCE;
}
private static class DogHolder {
private static Dog INSTANCE = new Dog(); // 缺少 final
}
}
// 问题:理论上线程安全,但违反最佳实践
// 可能被意外重新赋值
错误 4:使用反射破坏单例
// 反射可以创建新实例
Constructor<Dog> constructor = Dog.class.getDeclaredConstructor();
constructor.setAccessible(true);
Dog dog2 = constructor.newInstance(); // 新实例!
解决方案:
private Dog() {
// 防止反射攻击
if (DogHolder.INSTANCE != null) {
throw new IllegalStateException("单例已被创建,不允许通过反射创建新实例");
}
}
错误 5:序列化破坏单例
// 反序列化会创建新实例
Dog dog1 = Dog.getInstance();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(dog1);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
Dog dog2 = (Dog) ois.readObject(); // 新实例!
解决方案:
public class Dog implements Serializable {
private static final long serialVersionUID = 1L;
private Dog() {}
// 实现 readResolve 方法
private Object readResolve() {
return DogHolder.INSTANCE; // 返回单例实例
}
}
Reference
设计模式:单例模式 (关于饿汉式和懒汉式)单例模式是比较常见的一种设计模式,目的是保证一个类只能有一个实例,而且自行实例 - 掘金