延遲載入是「按需初始化」的經典設計模式:將昂貴操作推遲到真正需要時才執行。在處理大量物件、巨大資料結構、昂貴 I/O 或資料庫存取時,能顯著降低啟動延遲與記憶體壓力。
public class DomainObject {
private final DataParser parser;
private final Map<String, String> header = new HashMap<>();
private final List<Data> data = new ArrayList<>();
public DomainObject(DataParser parser) { this.parser = parser; }
public String getHeaderField(String name) {
if (header.isEmpty()) { // 首次存取時載入
header.putAll(parser.parseHeader());
}
return header.get(name);
}
public Iterable<Data> getMeasurementValues() {
if (data.isEmpty()) { // 首次存取時載入
data.addAll(parser.parseMeasurements());
}
return data;
}
}
效果:表格渲染時間大幅縮減,記憶體佔用僅載入必要資料。
// 快速:昂貴操作僅在 aCondition() 為 true 時執行
if (aCondition() && expensiveOperation()) { ... }
// 緩慢:無論條件如何,expensiveOperation() 可能先被評估(視編譯器優化)
if (expensiveOperation() && aCondition()) { ... }
class DomainObject(parser: DataParser) {
// 首次存取時才初始化,線程安全
private lazy val header = parser.parseHeader()
lazy val measurementValues: Iterable[Data] = parser.parseMeasurements()
def getHeaderField(name: String): String =
header.get(name).getOrElse("")
}
Scala 編譯器自動生成線程安全的初始化邏輯(雙重檢查鎖定模式)。
Hibernate 預設對關聯集合、單向關聯採用延遲載入:
LazyInitializationException| ✅ 適合延遲載入 | ❌ 不適合 |
|---|---|
| 物件建構昂貴(I/O、計算、反序列化) | 物件極小、建構極快 |
| 大部分實例的某欄位從未被存取 | 幾乎所有欄位都會被立即使用 |
| 啟動時間敏感(CLI 工具、Serverless 函式) | 長期運行服務,啟動成本可攤銷 |
| 記憶體受限環境 | 記憶體充裕,預載入換取存取速度 |
volatileSupplier<T> + Memoization 工具類Suppliers.memoize() / memoizeWithExpiration()@Lazy Bean 初始化@Basic(fetch=LAZY) / @OneToMany(fetch=LAZY)延遲載入非銀彈,但在「大物件、稀疏存取、啟動敏感」的三重條件下,是效能優化的高杠桿手段。關鍵在於識別真正的熱點路徑,而非盲目套用。