运行时常量和编译时常量区别及什么时候使用
运行时常量 和 编译时常量区别及什么时候使用
运行时常量和编译时常量区别及什么时候使用
运行时常量 和 编译时常量区别及什么时候使用
核心区别
| 特性 | 编译时常量 (const) | 运行时常量 (final) |
|---|---|---|
| 确定时间 | 编译时(代码转换成机器指令之前) | 运行时(程序运行过程中) |
| 值来源 | 必须在代码中直接明确指定 | 可以在运行时计算或赋值 |
| 内存分配 | 在内存中只有一份实例( canonicalized) | 每次初始化都会创建新实例 |
| 性能 | 更高(编译时即确定,无运行时开销) | 一般(有运行时赋值开销) |
| 使用场景 | 值在编译时就能确定且不变 | 值在运行时才能确定,但确定后不变 |
详细解释与示例
1. 编译时常量 (const)
定义:在代码编译阶段(即程序运行前)值就必须完全确定且不可变的常量。
特点:
- 值必须是字面量或由其他编译时常量构成的计算表达式
- 可以使用
const构造函数创建编译时常量对象 - 在内存中是单例的(相同的
const值只会存在一份)
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 正确:字面量常量
const int maxCount = 100;
const String appName = 'MyApp';
const List<int> fixedNumbers = [1, 2, 3]; // const 列表
const Map<String, int> config = {'timeout': 30, 'retries': 3};
// 正确:由其他常量组成的表达式
const double total = maxCount * 1.5;
const Duration timeout = Duration(seconds: 30); // 使用 const 构造函数
// 错误:值在运行时才能确定
const currentTime = DateTime.now(); // ❌ 编译错误
const randomValue = Random().nextInt(10); // ❌ 编译错误
// const 构造函数的使用
class MyPoint {
final double x;
final double y;
const MyPoint(this.x, this.y); // const 构造函数
}
const origin = MyPoint(0, 0); // 编译时常量对象
2. 运行时常量 (final)
定义:在程序运行时赋值,但一旦赋值后就不可改变的变量。
特点:
- 值可以在运行时通过计算、函数调用、构造函数等方式确定
- 每次初始化都会创建新的实例
- 更加灵活,适用于大多数”赋值后不变”的场景
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 基本用法
final int userId = 123;
final String userName = 'John Doe';
// 运行时计算的值
final double calculatedValue = calculateSomeValue(); // 调用函数
final DateTime currentTime = DateTime.now(); // ✅ 运行时确定
final int randomNumber = Random().nextInt(100); // ✅ 运行时确定
// 在构造函数中初始化
class UserProfile {
final String name;
final int age;
UserProfile(this.name, this.age); // 普通构造函数
}
final user = UserProfile('Alice', 25); // 运行时创建的对象
3. 结合使用 (static const)
在类中定义共享的编译时常量:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class AppConstants {
// 类级别的编译时常量
static const String appName = 'MyFlutterApp';
static const double defaultPadding = 16.0;
static const Duration animationDuration = Duration(milliseconds: 300);
// 错误:不能在类中这样声明
// final String version = '1.0.0'; // ❌ 需要 static final
static final String buildTime = DateTime.now().toString(); // ✅
}
// 使用
Text(
AppConstants.appName,
style: TextStyle(fontSize: AppConstants.defaultPadding),
);
什么时候使用哪种?
使用 const 当:
值是字面量且在编译时已知
- dart
1
2
const apiKey = 'abc123'; // 如果确实是固定值
const maxRetries = 3;
创建不可变的集合
- dart
1
2
const supportedLanguages = ['en', 'es', 'fr']; // 不可变列表
const defaultSettings = {'theme': 'light', 'notifications': true}; // 不可变映射
使用 const 构造函数提高性能
- dart
1
2
3
const EdgeInsets.all(16.0); // 而不是 EdgeInsets.all(16.0)
const BorderRadius.circular(8.0);
const Color(0xFF4285F4);
定义全局或类的常量值
- dart
1
2
3
4
class AppColors {
static const primary = Color(0xFF6200EE);
static const accent = Color(0xFF03DAC5);
}
使用 final 当:
值在运行时才能确定
- dart
1
2
final currentUser = fetchUserFromAPI(); // API调用
final deviceSize = MediaQuery.of(context).size; // 依赖上下文
类的实例变量(大多数情况下)
- dart
1
2
3
4
5
6
class User {
final String id;
final String email;
User(this.id, this.email); // 这些值在运行时通过构造函数传入
}
需要计算的值
- dart
1
2
final totalPrice = quantity * unitPrice; // 运行时计算
final fullName = '$firstName $lastName'; // 运行时拼接
Flutter 中的实践建议
在 Flutter 开发中,正确使用 const 可以显著提高性能:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Good: 使用 const 构造函数
Widget build(BuildContext context) {
return const Text( // const Widget
'Hello World',
style: TextStyle(fontSize: 16), // 但 TextStyle 不是 const
);
}
// Better: 尽可能多的使用 const
Widget build(BuildContext context) {
return const Text(
'Hello World',
style: TextStyle(fontSize: 16), // 这个还不是 const
);
}
// 将样式也提取为 const
static const _textStyle = TextStyle(fontSize: 16);
Widget build(BuildContext context) {
return const Text(
'Hello World',
style: _textStyle, // 现在整个 Widget 树都是 const 了
);
}
总结:
- 用
const优化性能:用于编译时已知的常量值、集合和 Widget - 用
final保证安全性:用于运行时确定的、赋值后不变的值 - 记住:所有**** **
**const****变量隐含也是****final******的,但反过来不成立**
本文由作者按照 CC BY 4.0 进行授权