Dart 基础 – final 和 const
版权归作者 ©刘龙宾 所有,本文章未经作者允许,禁止私自转载!
1. final 和 const 的共同点
无论是 final 还是 const,都可以用来定义那些不允许被修改的变量值。这一特点和其它语言中的“常量”类似。举个例子:
void main(List<String> args) {
// 1. 使用 final 定义常量 name 并赋值
final String name = 'zs';
print(name);
// 2. 使用 const 定义常量 nickname 并赋值
const String nickname = 'ls';
print(nickname);
// 3. 无论 name 还是 nickname 都无法使用 = 重新赋值,
// 所以下面的两行代码会编译失败
name = 'liulongbin';
nickname = 'escook';
}
因此,我们今后如果想定义常量并立即赋初值(注意:这里的初值特指明确的字面量值),无论使用 final 还是 const 都可以。
2. final 和 const 的区别
正所谓存在即合理,dart 既然提供了 final 和 const 这两个关键字,那么它们在某些特定场景下肯定存在差别,我们需要了解并掌握以下 3 点区别。
2.1 区别 1
const 表示编译时必须给出确切的最终值,final 表示运行时可以动态生成一个最终的值。
第一个区别是最重要的。掌握第一个区别的关键是能否正确区分什么叫“确切的最终值”,什么叫“动态生成最终值”。
- 确切的最终值:即字面量值,例如数字
1
,字符串'abc'
,布尔值true
,对象、数组等都是明确的值。 - 动态生成最终值:即值是动态生成的,例如随机数、动态获取系统的时间等。
例如,下面的代码动态生成了 10 以内的随机整数,并把随机生成的整数赋值给常量 age,此时定义常量 age 时不能使用 const,只能使用 final:
// 导入 核心类库 math
import 'dart:math';
void main(List<String> args) {
// 调用 Random() 得到一个随机数生成器
// 再调用随机数生成器的 nextInt(n) 函数,生成 0-n 之间的随机整数(包括0,但不包括n)
// 这里不能用 const,只能用 final。因为 Random().nextInt(10) 的值“是在程序运行时动态生成的”!
final int age = Random().nextInt(10);
print(age);
}
再例如,把当前时间赋值给常量 dt,在定义常量 dt 时不能使用 const,只能用 final。因为 dt 的值不是写死的字面量值,而是程序运行时动态生成的:
void main(List<String> args) {
// 这里只能用 final
final DateTime dt = DateTime.now();
print(dt);
}
最后,还有一个注意点需要特别提醒下:如果我们想定义一个空的常量,随后再给这个常量赋值,此时只能用 final。示例代码如下:
void main(List<String> args) {
// 1. 使用 final 先定义名为 name 的空白常量
final String name;
// 2. 在程序运行过程中,动态把常量 name 的值赋值为字符串 'liulongbin'
name = 'liulongbin';
print(name);
}
2.2 区别 2
const 定义的常量值在内存中会被复用;final 定义的常量哪怕值相同,在内存中也会被重复定义。
使用 const 定义两个字符串的数组,分别是 s1 和 s2。调用 identical(对象1, 对象2)
函数可以判断两个对象的内存地址是否相同。示例代码如下:
void main(List<String> args) {
// 使用 const 定义两个常量 s1 和 s2,分别指向“自己的”字面量数组
const List<String> s1 = ['a', 'b', 'c'];
const List<String> s2 = ['a', 'b', 'c'];
// identical() 函数用来检查两个对象是否指向同一个内存地址。
// 打印的结果是 true,
// 证明 s1 和 s2 指向同一个内存地址,s1 和 s2 共用1个 ['a', 'b', 'c'] 存储空间,
// 即 “const 定义的常量值在内存中会被复用”
print(identical(s1, s2));
}
如果把上述代码中的 const
替换成 final
,再次调用 identical(对象1, 对象2)
函数,发现打印的值变成了 false。示例代码如下:
void main(List<String> args) {
// 使用 final 定义两个常量 s1 和 s2,分别指向“自己的”字面量数组
final List<String> s1 = ['a', 'b', 'c'];
final List<String> s2 = ['a', 'b', 'c'];
// identical() 函数用来检查两个对象是否指向同一个内存地址。
// 打印的结果是 false,
// 证明 s1 和 s2 指向不同的内存地址,在内存中分别为 s1 和 s2 开辟了自己的 ['a', 'b', 'c'] 存储空间,
// 即 “final 定义的常量哪怕值相同,在内存中也会被重复定义”
print(identical(s1, s2));
}
2.3 区别 3
非 final,非 const 的变量是可以被修改的,即使这些变量曾经引用过 const 值。
变量的本质是“数据可变性”。如果变量曾经引用过其它常量的值,也不会影响我们后续修改这个变量的值。示例代码如下:
void main(List<String> args) {
// 1. 定义 const 常量 name 并赋初值
const name = 'liulongbin';
// 2. 把常量 name 的值赋值给变量 username
// ★变量 username 引用了常量 name 的值
var username = name;
// 3. 输出 username 的值是字符串 liulongbin
print(username);
// 4. 把变量 username 的值变更为字符串 escook
// ★虽然变量 username 曾经引用过常量 name 的值,
// ★但是,这不会影响我们为 username 重新赋值,因为 username 本质上是“变量”
username = 'escook';
// 5. 再次输出 username 的值,是字符串 escook
print(username);
}
3. 总结
- const 和 final 都可以用来定义那些不允许被修改的变量值。如果初始值是字面量,那么后续不能修改它们的值。
- 如果常量的值是运行时动态生成的,则只能使用 final 关键字来定义常量。
- const 定义的常量值在内存中会被复用,而 final 定义的常量值哪怕看上去一样,在内存中也会开辟不同的空间进行存储。
- 变量哪怕曾经引用过常量的值,也不影响我们后续给这个变量赋新值。
版权归作者 ©刘龙宾 所有,本文章未经作者允许,禁止私自转载!
4条评论
大大
你好,刘老师,这一个https://www.escook.cn/api/cart接口报错404,用您写好的代码也请求不到。
泥巴巴
是的,曾经的老旧接口已经停止维护了
大大
感谢老师的回复,想再问一下老师,这个接口大概什么时候修好,以后还会不会应用呢?
泥巴巴
我的服务器不再对外开放此接口啦