const构造器作用、两种使用方式差异及性能测量方法
Dart中const构造器与不同使用方式的分析
先给出基础代码:
class Bird { const Bird(); void fly() {} }
两种使用方式:
方式1:
const bird = Bird(); void doIt() { bird.fly(); }
方式2:
void doIt2() { const bird = Bird(); bird.fly(); }
问题1:const构造器的作用是什么?上述两个示例中是否都能避免对象创建?
const构造器的核心作用:
- 生成编译时常量对象:这类对象在程序编译阶段就被创建并存入常量池,运行时不会再执行对象内存分配逻辑
- 实例复用:相同参数的const构造器调用,会复用常量池中的同一个实例,彻底避免重复创建对象的内存开销
- 强制不可变性:使用const构造器的类,成员变量必须是
final或const,保证对象状态不可修改,避免意外的状态变更
两个示例是否都能避免对象创建?
是的,两者都不会在运行时创建新的Bird对象:
- 方式1中的全局
bird是编译时就确定的常量实例,每次调用doIt()都是直接引用这个已存在的实例 - 方式2中函数内的
const bird,同样会复用常量池中的同一个Bird实例,每次调用doIt2()时不会新建对象,只是在函数内引用常量池里的实例
问题2:两种使用方式在const特性上存在哪些差异?
主要差异集中在作用域、可见性和使用场景上:
- 作用域与可见性
- 方式1的
bird是全局作用域变量,整个代码库中的函数、类都可以直接访问这个常量实例 - 方式2的
bird是函数局部作用域变量,仅在doIt2()函数内部可见,外部无法直接引用该变量
- 方式1的
- 实例复用的访问范围
- 全局const实例可以被多个不同的函数、类共享引用,最大化复用价值
- 局部const实例虽然本质上也是常量池中的同一个实例,但只能在当前函数内直接使用,外部无法通过变量名访问
- 初始化时机感知
- 全局const在程序启动前(编译阶段)就完成初始化,程序运行时直接可用
- 局部const同样是编译阶段确定,但它的变量引用仅在函数执行时可见,不过实例本身早已存在于常量池
注:无论哪种方式,
const Bird()调用生成的都是同一个实例,Dart的常量池会全局复用相同的const表达式结果
问题3:如何测量这两种方式的性能差异?
方法1:使用Dart官方基准测试包benchmark_harness
这是最规范的性能测试方式,能消除环境波动带来的误差:
- 添加依赖到
pubspec.yaml:dev_dependencies: benchmark_harness: ^2.2.0 - 编写基准测试代码:
import 'package:benchmark_harness/benchmark_harness.dart'; class Bird { const Bird(); void fly() {} } const bird = Bird(); void doIt() { bird.fly(); } void doIt2() { const bird = Bird(); bird.fly(); } class DoItBenchmark extends BenchmarkBase { DoItBenchmark() : super('doIt'); @override void run() { doIt(); } } class DoIt2Benchmark extends BenchmarkBase { DoIt2Benchmark() : super('doIt2'); @override void run() { doIt2(); } } void main() { DoItBenchmark().report(); DoIt2Benchmark().report(); } - 运行测试:
dart run benchmark.dart,会输出每次调用的平均耗时
方法2:手动计时(快速验证)
用Stopwatch类直接统计循环调用的耗时:
void main() { const loopCount = 1000000; final stopwatch = Stopwatch(); // 测试doIt stopwatch.start(); for (var i = 0; i < loopCount; i++) { doIt(); } stopwatch.stop(); print('doIt 循环$loopCount次耗时: ${stopwatch.elapsedMicroseconds} 微秒'); // 测试doIt2 stopwatch.reset(); stopwatch.start(); for (var i = 0; i < loopCount; i++) { doIt2(); } stopwatch.stop(); print('doIt2 循环$loopCount次耗时: ${stopwatch.elapsedMicroseconds} 微秒'); }
注意事项:
- 多次运行取平均值,避免单次运行的系统波动影响结果
- 生产环境下Dart编译器会做深度优化,两者的性能差异极小;若要观察差异,可关闭优化运行:
dart run --no-optimize - 内存验证:通过Dart DevTools的内存分析工具,查看两种方式下Bird实例的数量,确认是否都没有重复创建对象
内容的提问来源于stack exchange,提问作者Daniel Gomez Rico
相关产品推荐
相关产品推荐

