class Logger {
static final Logger _instance = Logger._internal();

// 私有命名构造函数
Logger._internal();

// 工厂构造函数,总是返回唯一的实例
factory Logger() {
return _instance;
}
}

1. 解读 static final Logger _instance = Logger._internal();

首先,我们来分析这行代码的每一个关键字:

  • static: 这个关键字意味着 _instance 变量不属于任何一个 Logger 对象,而是属于 Logger 这个类本身。它在内存中只有一份。程序第一次使用 Logger 类时,这个静态变量就会被初始化,且只初始化这一次。

  • final: 这表示 _instance 变量在被赋值一次后,就不能再被修改了。它的引用地址是固定的,确保了我们始终指向同一个 Logger 实例。

  • = Logger._internal(): 这是最关键的部分。这里不是“复制构造函数”,而是 “调用” Logger._internal() 这个私有命名构造函数来创建一个真实的 Logger 实例,然后把这个新创建的实例赋值给 _instance 变量。

问:Logger 类内部可以使用自己的 Logger 吗? 答:可以,而且必须可以! 一个类当然可以在自己的定义内部创建自己的实例。正是因为 _instance 的声明在 Logger 类的内部,所以它有权限调用私有的 Logger._internal() 构造函数。如果是在类的外部,这个调用就会因为私有性而报错。


2. 谁先执行?静态变量初始化优先!

你的问题是“工厂构造函数优先执行吗”,答案是:不优先。执行顺序是这样的:

  1. 类加载时(第一次使用 Logger 类时):

    • Dart 运行时发现有一个静态变量 _instance 需要初始化。

    • 它立刻执行 = Logger._internal() 这部分代码,调用私有构造函数创建了一个 Logger 对象。

    • 这个对象被赋值给 _instance,并存储在内存中。

    • 此时,单例对象已经创建好了,并且静静地等待被使用。

  2. 代码调用时(例如 var myLogger = Logger();):

    • 这时,factory Logger() 这个工厂构造函数才会被调用。

    • 工厂构造函数内部的逻辑非常简单:return _instance;。

    • 它不去创建新对象,而是直接把你在第 1 步中已经创建好的那个 _instance 对象返回给你。

所以,无论你调用多少次 Logger(),工厂构造函数每次都只是把你早已创建好的那个唯一的实例交给你。


3. 三者关系:分工明确的团队合作

你可以把这三个部分想象成一个分工合作的团队:

  • Logger._internal() (私有命名构造函数)

    • 角色:工人

    • 职责:它的唯一任务就是实际地、真正地去创建一个 Logger 对象。它被设为私有 (_),就是为了防止外部任何人随意雇佣这个“工人”去创建新的、多余的对象,保证了对象创建的唯一入口。

  • static final Logger _instance (静态实例)

    • 角色:仓库

    • 职责:它是一个存放唯一产品的仓库。这个仓库在程序开始时,就让“工人”生产了一个产品并存了进去 (= Logger._internal())。因为是 static,所以全局只有一个仓库;因为是 final,所以仓库里的产品不能被替换。

  • factory Logger() (工厂构造函数)

    • 角色:门店/客服

    • 职责:这是对外服务的窗口。当你的代码(客户)说“我想要一个 Logger” (Logger()) 时,这个“客服”并不会去重新生产一个,而是直接去“仓库”里,把那个唯一的产品 (_instance) 拿出来交给你。

总结

  • 执行顺序:static 变量的初始化先于工厂构造函数的调用。实例在第一次使用类时就已经被创建。

  • 关系:

    • 私有命名构造函数是真正的创建者。

    • 静态 final 变量是持有者,它持有那个被唯一创建的实例。

    • 工厂构造函数是提供者,它不创造,只负责把持有的实例返回给调用方。

它们三者完美配合,共同实现了 Dart 中既安全又高效的单例模式。希望这个解释能让你豁然开朗!

更多推荐