1. 定义和使用变量
尽管Dart是静态语言,但仍然拥有动态特性。所以定义变量也有如下2种方式。
- 使用数据类型定义变量
- 使用var定义变量
在Dart语言中,数据类型放在变量前面,格式如下:
代码语言:javascript复制数据类型 变量名;
在Dart语言中,每条语句后面必须跟分号(;),所以在定义变量时后面也要加分号。如下面的代码定义了一个整数类型的变量和一个字符串类型的变量。
代码语言:javascript复制int num; // 整数类型的变量
String s; // 字符串类型的变量
在定义变量时可以同时为变量赋值,也称为初始化,所以可以用下面的代码为num和s赋值。
代码语言:javascript复制int num = 20;
String s = "hello world";
如果在定义变量时指定了数据类型,那么就意味着变量的数据类型不可以改变,例如,上面代码中已经将num变量定义为int类型,那么num就永远是int类型,不可以为该变量赋其他类型的值。
由于Dart语言中一切皆为对象,所以如果在定义变量时没有为其初始化,那么变量的默认值就是null。
另外一种定义变量的方法是使用var关键字,代码如下:
代码语言:javascript复制var num = 20; // num被自动识别为int类型的变量
var value; // 由于value在定义时没有被初始化,所以value的数据类型是dynamic
上面两行代码分别使用var定义了两个变量,但这两个变量是有区别的。num变量由于在定义时已经初始化了,所以num的数据类型会根据初始化的值自动识别,很显然,20属于int类型的值,所以num变量的数据类型就是int,而且与直接指定变量的数据类型一样,num变量的数据类型将永久固定,后期是不可以改变的。但value就不同了。由于在定义value变量是没有为其初始化,也没有指定数据类型,所以value的数据类型就被识别为dynamic。这是Dart语言中的一种特殊数据类型,有了dynmaic,Dart就可以很容易动态化。如果Dart编译器遇到dynamic数据类型的变量,就不会对该变量进行任何类型检测,相当于在JavaScript中使用变量一样。不过如果调用了变量中不存在的成员(如属性、方法等),在运行时会抛出异常。但dynamic提供了解决方案来避免这种异常,关于dynamic的详细用法,在后面的章节会深入介绍。
综上所述,在Dart语言中,如果为变量指定了数据类型,那么变量就是静态的,变量的数据类型永远不可以改变。如果变量的数据类型是dynamic,变量就是动态的。可以为该变量赋任何值。让变量是静态类型和动态类型分别由如下2种方式。
(1)静态类型的变量
(1)显式指定变量的数据类型,如int num;
(2)使用var定义变量,但在定义时初始化了变量,并且初始化变量的表达式不是dynamic类型。如var value = 20;
(2)动态类型的变量
- 使用var定义变量,而且在定义时未初始化变量。即使在后面为变量赋了值,该变量仍然是dynamic类型的变量,如var value;
- 直接使用dynamic类型定义变量,如dynamic value;
本例演示了各种定义和使用变量的方式。
代码语言:javascript复制void main() {
var name = "Bill"; // String类型变量
var value1 = 20; // int类型变量
int value2 = 30; // int类型变量
print(name); // 输出Bill
print(value1 value2); // 输出50
// 在Dart中一切都是对象,如果未初始化变量,变量的默认值就是null
var value; // dynamic类型变量
int value3; // int类型变量
print(value); // 输出null
print(value3); // 输出null
String s;
// 条件为true
if(s == null) {
print("NULL");
}
}
运行结果如图1所示。
图1定义和使用变量
2. 定义和使用常量
常量的定义方式与变量类似,只是需要使用const或final来定义常量。这两个关键字相当于var对于变量的作用。下面是定义常量的基本方法。
代码语言:javascript复制final username = '李宁';
const value = 1234;
在上面的代码中,username和value是两个常量,这两个常量都没有指定数据类型,不过由于Dart语言要求常量必须在定义时初始化,所以所有定义的常量右侧都会有一个初始化表达式,Dart编译器会利用这个初始化表达式自动识别常量的数据类型。上面的例子中,username被识别为String类型,value被识别为int类型。
在定义常量时,也可以显式地为其指定数据类型,代码如下:
代码语言:javascript复制
final String username = '李宁';
const int value = 1234;
常量只能在定义时被初始化一次,不能在使用的过程中赋值,所以下面的代码是无法编译通过的。
代码语言:javascript复制username = '小明'; // 无法给常量赋值,会出现编译错误
value = 4321; // 无法给常量赋值,会出现编译错误
那么可能有的读者会问,为什么定义常量要提供两个关键字(const和final)呢?其实用这两个关键字定义常量是有一定区别的。const是编译时常量,而final是运行时常量。下面解释一下什么是编译时常量,什么是运行时常量。
- 编译时常量:在Dart编译器在编译时会自动计算的常量,也就是说,不管常量的初始化时是一个值,还是一个表达式,Dart编译器都会将这个表达式计算成一个值。换句话说,这种常量不管初始化表达式是什么,最终在内存中都是一个值。所以使用这种常量的效率特别高。
- 运行时常量:这种常量与变量类似,都会在每次使用常量时现技术常量初始化表达式的值,所以使用这种变量的效率较低。
既然编译时常量在编译代码时会自动计算初始化表达式的值,那么就意味着初始化表达式中的每一个部分都必须在编译时可以获得具体的值。这就要求初始化表达式只能由值或其他编译时常量组成,不能由变量、运行时常量、函数组成。因为这些元素的值只有在程序运行时才可以获得,而编译时程序还没有运行,所以编译时常量的初始化表达式不能由这些元素组成。
运行时常量的初始化表达式与变量的初始化表达式类似。
const和final的用法见下面的代码。
代码语言:javascript复制const m = 20; // 编译时常量
const n = m 30; // 编译时常量
final k = 40; // 运行时常量
const w = k 20; // 编译错误,因为编译时常量的初始化表达式只能由值和编译时常量组成
3. 常量列表和列表常量
常量同样也可以用来定义列表。列表是Dart语言的内建数据类型,关于列表的详细用法,会在后面的章节深入介绍。本节只介绍使用final和const定义常量列表和列表常量。那么这两个列表有什么区别呢?
- 常量列表:指列表中的每一个元素都是常量,但列表本身可能是一个常量,也可能是一个变量。如果列表本身是一个变量,而列表元素都是常量,那么就意味着不能修改列表的每一个元素,但可以再次为列表变量赋值。
- 列表常量:指列表本身是一个常量。对于这样的常量,列表中每一个元素同样也是一个常量。对于这种列表,列表本身与列表中的元素都不可以改变。
在定义常量列表时,可以在列表初始化值前面加const(不能使用final),也可以不加。在定义列表常量时,可以使用const,也可以使用final。
本例演示了常量的定义和使用方法,以及常量列表和列表常量的定义与使用方法。
代码语言:javascript复制void main() {
final username = '李宁';
const password = '1234';
// username = "abc"; // 编译错误,不能为常量赋值
// password = "1234"; // 编译错误,不能为常量赋值
const n = 20;
final x1 = n *2 20;
const x2 = n * 2 20; // 自动计算,x2实际上存储的是60
const x3 = x2 * 2;
print(x2);
print(x3);
var values1 = const [1,2,3]; // 定义一个常量列表
print(values1);
values1 = [5,6,7]; // 列表本身是变量,所以可以重新设置列表的值
print(values1[1]);
// values[1] = 4; // 不会有编译错误,但会抛出运行时异常
final values2 = const["a","b"]; // 定义列表常量
// values2 = ["b","c"]; // 编译错误
// values2[1] = "x"; // 运行时错误
const values3 = [5,6]; // 定义一个列表常量
// values3[1] = 4; // 运行时错误
}
运行结果如图2所示。
图2常量与列表