【Dart 入门日记】第2篇:常量声明详解——const 与 final 到底有什么区别?
作者:灰灰勇闯IT 时间:2026年1月 系列定位:零基础记录 Dart 学习过程,适合编程新手、Flutter 初学者 本文重点:深入理解 const(编译时常量)与 final(运行时常量)的使用场景与本质区别
目录
- 1. 为什么需要常量?
- 2. const:编译期就确定的“绝对不变”值
- 2.1 场景引入:圆周率 π 不可更改
- 2.2 const 基本语法与代码示例
- 2.3 ❌ 常见错误:const 表达式中不能包含变量
- 3. final:运行时才确定的“一次赋值”变量
- 3.1 场景引入:获取当前时间作为唯一操作时间戳
- 3.2 final 基本语法与代码示例
- 4. 核心对比:const vs final —— 编译时 vs 运行时
- 5. 深入理解:为什么 Dart 要区分这两种常量?
- 6. 实战建议:什么时候用 const,什么时候用 final?
- 7. 常见误区与调试技巧
- 8. 小结 & 下期预告
1. 为什么需要常量?
在编程中,有些值从诞生起就不该被修改。比如:
- 数学常数:π(3.14159…)、e(2.718…)
- 配置项:API 地址、版本号
- 时间戳:用户点击按钮的精确时刻
如果这些值被意外修改,程序逻辑就会出错。因此,Dart 提供了两种“不可变”机制:const 和 final。
🌟 我的思考: 昨天学了 var,今天学“不能变的变量”,感觉像是在给数据上锁——有的锁在写代码时就焊死了(const),有的锁在程序跑起来那一刻才扣上(final)。
2. const:编译期就确定的“绝对不变”值
2.1 场景引入:圆周率 π 不可更改
计算圆的周长公式是:C = 2 × π × r。 其中,π 是一个数学常数,永远不变。这种值就应该用 const 声明。
2.2 const 基本语法与代码示例
语法:
const 常量名 = 值;
示例:计算半径为 5 的圆的周长
void main() {
const pi = 3.1415926; // 编译时常量
var radius = 5.0;
var circumference = 2 * pi * radius;
print('半径为 $radius 的圆,周长是 $circumference');
}
输出:
半径为 5.0 的圆,周长是 31.415926
](https://i-blog.csdnimg.cn/direct/932ad3c1d3ac4d22b724ccacb0101011.png#pic_center)](https://www.171host.com/wp-content/uploads/2026/01/20260123115025-6973608168b75.png)
✅ 特点:
- 值在编译时就必须确定
- 所有 const 对象在内存中是单例(相同值只存一份)
- 可用于注解、元数据等需要编译期常量的场景
2.3 ❌ 常见错误:const 表达式中不能包含变量
const 的值必须是“字面量”或由其他 const 组成的表达式,不能依赖运行时才能知道的变量。
错误示例:
void main() {
var radius = 5.0;
// ❌ 错误!radius 是变量,无法在编译时确定
const area = 3.1415926 * radius * radius;
}
报错信息类似:
Error: Constant expressions can't refer to non-constant variables.
](https://www.171host.com/wp-content/uploads/2026/01/20260123115026-69736082932af.png)
💡 正确做法:改用 final,或者把整个表达式移到运行时计算。
3. final:运行时才确定的“一次赋值”变量
3.1 场景引入:获取当前时间作为唯一操作时间戳
假设我们要记录用户点击按钮的精确时间。这个时间只有在程序运行到那一行代码时才能知道,但它一旦获取,就不应再变。
这就是 final 的典型用武之地。
3.2 final 基本语法与代码示例
语法:
final 变量名 = 值;
示例:获取当前年份
import 'dart:core';
void main() {
final now = DateTime.now(); // 运行时获取当前时间
final currentYear = now.year;
print('当前年份是:$currentYear');
}
输出(假设今天是 2026 年):
当前年份是:2026
](https://www.171host.com/wp-content/uploads/2026/01/20260123115027-69736083d2187.png)
✅ 特点:
- 值在运行时第一次赋值后锁定
- 可以使用变量、函数返回值等动态内容初始化
- 每次运行程序,final 的值可能不同(如时间、随机数)
4. 核心对比:const vs final —— 编译时 vs 运行时
| 初始化时机 | 编译时 | 运行时 |
| 值是否可变 | 绝对不可变 | 初始化后不可变 |
| 能否使用变量初始化 | ❌ 不行 | ✅ 可以 |
| 内存优化 | 相同值共享同一对象(单例) | 每次创建新对象 |
| 适用场景 | 数学常量、配置字面量 | 时间、用户输入、API 返回值 |
代码对比示例:
void main() {
// ✅ const:编译期确定
const pi = 3.1415926;
const appName = "My Flutter App";
// ✅ final:运行期确定
final timestamp = DateTime.now().millisecondsSinceEpoch;
final randomId = DateTime.now().microsecondsSinceEpoch % 1000;
print('App: $appName, π ≈ $pi');
print('时间戳: $timestamp, 随机ID: $randomId');
}
5. 深入理解:为什么 Dart 要区分这两种常量?
这背后是 性能 与 灵活性 的权衡:
-
const 用于极致优化 因为值在编译时已知,Dart 可以:
- 将相同 const 对象合并(节省内存)
- 在 AOT 编译(如 Flutter Release 模式)中直接内联值
- 支持用作 Widget 构造函数的 const 参数(提升 UI 渲染性能)
-
final 保证运行时安全 当值依赖外部输入(如网络、传感器、时间),final 确保“只读一次”,防止后续逻辑意外修改。
📌 Flutter 开发提示: 在构建 Widget 时,尽量用 const 修饰那些不会变的子组件,例如:
const Text('欢迎来到我的 App', style: TextStyle(fontSize: 18));
这能显著减少重建开销!
6. 实战建议:什么时候用 const,什么时候用 final?
✅ 用 const 当:
- 值是字面量(数字、字符串、布尔值)
- 值由其他 const 组合而成
- 用于配置、枚举、UI 静态文本
const double gravity = 9.8;
const List<String> weekdays = ['Mon', 'Tue', 'Wed'];
✅ 用 final 当:
- 值来自函数调用(如 DateTime.now())
- 值来自参数或用户输入
- 值在构造函数中初始化(类成员)
class User {
final String name;
final int loginTime;
User(this.name) : loginTime = DateTime.now().millisecondsSinceEpoch;
}
7. 常见误区与调试技巧
❌ 误区1:“final 和 const 一样,都是常量”
→ 不完全对!final 是运行时常量,const 是编译时常量,性能和使用限制完全不同。
❌ 误区2:“const 可以放在函数里动态计算”
→ 不行!const 必须在编译时就能算出结果。
✅ 调试技巧:
- 在 VS Code 中,悬停 const 变量会显示 “compile-time constant”
- 尝试给 const 赋变量值,编译器会立即报错,这是好事!
- 在 Flutter 中,开启 “Performance Overlay” 可观察 const Widget 是否被跳过重建
8. 小结 & 下期预告
✅ 本篇收获:
- 掌握了 const 的使用:适用于编译期已知的绝对常量(如 π)
- 理解了 final 的价值:适用于运行时确定但后续不变的数据(如时间戳)
- 明确了二者的核心区别:编译时 vs 运行时
- 避开了“在 const 中使用变量”的经典错误
🎯 动手练习: 尝试写一个 Circle 类,用 const 存储 π,用 final 存储半径,并提供计算面积和周长的方法。
➡️ 下期预告: 《【Dart 入门日记】第3篇:Dart 的基础数据类型全解析——int、double、String、bool 与 null 安全》 我们将系统学习每一种内置类型,并揭开 Dart 空安全(Null Safety)的神秘面纱!
💬 互动时间: 你在项目中更常用 const 还是 final?有没有因为混淆两者而踩过坑?欢迎在评论区分享你的故事!如果你觉得这篇文章帮你理清了概念,别忘了 点赞 + 收藏 + 关注,你的支持是我持续更新的最大动力!
📎 附:所有代码均可直接运行 环境要求:Dart SDK 3.0+,推荐使用 VS Code + Dart/Flutter 插件

