You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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. 作用域与可见性
    • 方式1的bird是全局作用域变量,整个代码库中的函数、类都可以直接访问这个常量实例
    • 方式2的bird是函数局部作用域变量,仅在doIt2()函数内部可见,外部无法直接引用该变量
  2. 实例复用的访问范围
    • 全局const实例可以被多个不同的函数、类共享引用,最大化复用价值
    • 局部const实例虽然本质上也是常量池中的同一个实例,但只能在当前函数内直接使用,外部无法通过变量名访问
  3. 初始化时机感知
    • 全局const在程序启动前(编译阶段)就完成初始化,程序运行时直接可用
    • 局部const同样是编译阶段确定,但它的变量引用仅在函数执行时可见,不过实例本身早已存在于常量池

注:无论哪种方式,const Bird()调用生成的都是同一个实例,Dart的常量池会全局复用相同的const表达式结果


问题3:如何测量这两种方式的性能差异?

方法1:使用Dart官方基准测试包benchmark_harness

这是最规范的性能测试方式,能消除环境波动带来的误差:

  1. 添加依赖到pubspec.yaml:
    dev_dependencies:
      benchmark_harness: ^2.2.0
    
  2. 编写基准测试代码:
    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();
    }
    
  3. 运行测试: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 10:01:15