一、变量
1.1、变量的声明
下面是声明变量并赋值的示例:
var name = 'Bob';
名为name的变量包含对字符串对象的引用,值为“Bob”。var是关键字会根据赋予的数值自动推断类型。
当然你也可以显示的声明类型:
String name = 'Bob';
style guide recommendation 推荐使用var来声明局部变量。
但是var本身并不是一种类型,var声明的变量在赋值的那一刻,就已经决定了它是什么类型了。如果你这样使用,就会有编译错误:
var num = 44;
num = 'hello';//编译错误 Error: A value of type 'dart.core::String' can't be assigned to a variable of type 'dart.core::int'.
如果对象不限于单一类型(没有明确的类型),请使用Object或dynamic关键字。
Object num = 44;
num = 'hello'; //允许动态变更类型
dynamic num2 = 44;
num2 = 'hello'; //允许动态变更类型
Object和dynamic的区别
Object之所以能够被赋值为任意类型的原因,其实都知道,因为所有的类型都派生自Object,它可以赋值为任何类型。由于Object是一个类,并不支持某些数学运算符的操作,因此下面的代码在编译时会报错:
Object num = "44";
num++; //编译错误
而dynamic 表示没有一个类型可以表达你所期望的对象 。它无法在编译时候确定实际类型的, 而是在运行时。所以下面的代码是能够通过编译的,但是会在运行时报错:
dynamic num = "44";
num++; //运行时错误
1.2、默认值
没有初始化的变量自动获取一个默认值为 null。即使是基本类型(int、long、short)如何没有初始化其值也是 null,不要忘记了基本类型也是对象:
int lineCount;
if(lineCount == null){
print(lineCount);//打印null
}
1.3、Final 和 const(常量)
如果你以后不打算修改一个变量,使用 final 或者 const 修饰,来代替var。 一个 final 变量只能赋值一次;一个 const 变量是编译时常量(const 变量同时也是final 变量) 。被final修饰的顶级变量或类变量在第一次声明的时候就需要初始化。
注意:实例变量可以为 final 但是不能是 const 。
这里有一个创建和设置final变量的例子:
//const、final变量需要在声明时赋值进行初始化
const name1; // 错误
final name2; //错误
final name = 'Bob';
final String nickname = 'Bobby';
//无法更改final变量的值:
name = 'Alice'; // Error: a final variable can only be set once.
flnal 或者 const 不能和 var 同时使用,但允许和 具体类型 同时使用:
const var outSideName = 'Bill';//Error
final var name = 'Lili'; //Error
const String outSideName = 'Bill';//Correct
final String name = 'Lili'; //Correct
const 变量为编译时常量。 如果 const 变量定义在类中,请定义为 static const,实际上也只有静态变量才能声明const,实例变量则不行:
static const String name3 = 'Tom';
我们可以直接定义 const 和其值,也可以定义一个 const 变量使用其他 const 变量的值来初始化其值:
const a = 4 + 5;
const bar = 1000000; // Unit of pressure (dynes/cm2)
const double atm = 1.01325 * bar; // Standard atmosphere
const不仅能修饰变量名,而且还能够【修饰值】,表示其变量中的【常量值】不能发生改变。您可以在创建集合时使用它,例如const [1,2,3]
,以及构造对象(代替new),比如const Point(2,3)
。这里,const意味着对象的整个深度状态可以在编译时完全确定,并且对象将被冻结并完全不可变。
//[] 表示list集合
const foo1 = [1,2,3];
foo1[0] = 1; //运行时错误
foo1 = [1,2,3];//编译时错误,const修饰变量,表示其指向的引用不能发生改变
var foo2 = const [1,2,3];
foo2[1] = 1;//运行时错误,const创建的常量值不能发生改变
foo2 = [1,2,3];//正确
//允许同时声明变量名 和 变量值 效果和 const foo1一样
const foo3 = const[1,2,3];
特别注意的一点是const foo1 = [] 的效果 跟 const foo1 = const[] 是等价的,其变量名和变量值都不能进行改变。
与const不一样,final不能修饰变量值,只能修饰变量名:
final foo1 = [1,2,3];
foo1[0] = 1; //正确
foo1 = [1,2,3];//编译时错误
二、Built-in types(内置的类型)
Dart 内置支持下面这些类型:
- numbers
- strings
- booleans
- lists (也被称之为 arrays )
- maps
- runes (用于在字符串中表示 Unicode 字符)
- symbols
您可以使用字面量初始化任何这些特殊类型的对象。例如 'this is a string'
是一个字符串字面量, true 是一个布尔字面量。因为Dart中的每个变量都指向一个对象——一个类的实例——你通常可以使用构造函数来初始化变量。有些内置类型有自己的构造函数。例如, 可以使用 Map()
构造函数来创建一个 map, 就像这样 new Map()
。
2.1、Numbers(数值)
Dart数值类型有两种类型 int 和 double。
int类型
根据平台的不同,整数值不会超过64位。在Dart VM上,值可以从-2^63到2^63 - 1。编译成JavaScript的Dart使用JavaScript代码,允许值从-2^53到2^53 - 1。
double类型
64位(双精度)浮点数,遵循IEEE 754标准。
int和double都是num类的子类。在源码中我们明显的可以看到这两个子类:
num类型定义了基本的操作符,如+、-、/和***** 等,还有自带了abs()、ceil()和floor()等方法:
而对于位运算符>> 、<<、|、^ 等操作,则是在int类中定义:
如果num及其子类型没有您要查找的内容,那么请参考dart:math 库。
整数是不带小数点的数字。下面是一些定义整数的方式:
var x = 1;
var hex = 0xDEADBEEF;
如果一个数带小数点,则其为 double, 下面是定义 double 的一些方式:
var y = 1.1;
var exponents = 1.42e5;
从Dart 2.1开始,在变量类型为double的情况下,会自动把整数字面量转成双精度字面量:
double z = 1; // Equivalent to double z = 1.0.
注意:在Dart 2.1之前,上面的写法是错误的。
以下是如何将字符串转换成数字的方法,反之亦然:
// String -> int
var one = int.parse('1');
assert(one == 1);
// String -> double
var onePointOne = double.parse('1.1');
assert(onePointOne == 1.1);
// int -> String
String oneAsString = 1.toString();
assert(oneAsString == '1');
// double -> String
String piAsString = 3.14159.toStringAsFixed(2);
assert(piAsString == '3.14');
整数类型支持传统的位移操作符,(<<, >>), AND (&), 和 OR (|) 。例如:
assert((3 << 1) == 6); // 0011 << 1 == 0110
assert((3 >> 1) == 1); // 0011 >> 1 == 0001
assert((3 | 4) == 7); // 0011 | 0100 == 0111
数字字面量为编译时常量(compile-time constants)。 很多算术表达式只要其操作数是常量,则表达式结果也是编译时常量:
const msPerSecond = 1000;
const secondsUntilRetry = 5;
const msUntilRetry = secondsUntilRetry * msPerSecond; //msUntilRetry在编译时已经确定了其值
2.2、Strings(字符串)
Dart字符串是UTF-16编码的字符序列(这点跟Java一样)。您可以使用单引号或双引号创建一个字符串:
var s1 = 'Single quotes work well for string literals.';
var s2 = "Double quotes work just as well.";
var s3 = 'It\'s easy to escape the string delimiter.';
var s4 = "It's even easier to use the other delimiter.";
可以在字符串中使用 ${expression} 表达式。同时,如果在字符串中表达式是一个标识符,是可以省略{}
var a = 'abc';
print("123${a}456"); //123abc456 由于$a456 不是一个存在的变量标识符所以不能省略{}
print("123${a.toUpperCase()}456");//123ABC456
print("123$a,456");//省略{} 123abc,456
字符串表达式也可以用在常量字符串中,但是要求其表达式中变量也是常量类型,但常量类型只限于bool、num、String、null,例如list常量就不行,见下面的例子:
// These work in a const string.
const aConstNum = 0;
const aConstBool = true;
const aConstString = 'a constant string';
// These do NOT work in a const string.
var aNum = 0;
var aBool = true;
var aString = 'a string';
const aConstList = [1, 2, 3];
//输出 : 0 true a constant string
const validConstString = '$aConstNum $aConstBool $aConstString';//OK
//编译错误, 非常量类型 和 非bool、num、String、null类型的常量不能用于常量表达式
const invalidConstString = '$aNum $aBool $aString $aConstList';
字符串允许拆分成多行,拼接成字符串行与行之间用空格表示。同时也支持+运算符连接字符串:
var s1 = 'String '
'concatenation'
" works even over line breaks.";
print(s1);//String concatenation works even over line breaks.
var s2 = 'The + operator ' + 'works, as well.';
print(s2); //The + operator works, as well.
另一种创建多行字符串的方法:使用带有单引号或双引号的三重引号:
var s1 = '''
You can create
multi-line strings like this one.
''';
print(s1);
var s2 = """This is also a
multi-line string.""";
print(s2);
利用三重引用创建的多行字符串,行与行之间是有换行的:
你可以用r前缀创建一个“原始”字符串,既字符串中的元素不受转义字符的影响:
var s = r"In a raw string, even \n isn't special.";
print(s);//In a raw string, even \n isn't special.
有关如何在字符串中表示Unicode字符的详细信息,请参见[Runes]。
使用字符串的更多信息请参考: 字符串和正则表达式。
2.3、Booleans(布尔值)
Dart中表示布尔值是bool类型,只有两个对象具有bool类型:布尔字面量true和false,它们都是编译时常量。
Dart值是类型安全的,意味着你不能直接使用非bool类型放在 if 或 assert 之类的代码中:
var name = 'Bob';
if(name){//编译错误。非bool类型,不能直接放在这
print('You have a name!');
}
如果在 JavaScript 中运行,则会打印出 “You have a name!”,在 JavaScript 中 name 是非 null 对象所以认为是 true。但是在 Dart 中将会编译不通过。Dart 这样设计布尔值,是为了避免奇怪的行为,在写代码的时候你应该显式的判断变量是否为布尔值类型。例如:
//Check for an null string
var name = null;
assert(name == null);
// Check for an empty string.
var fullName = '';
assert(fullName.isEmpty);
// Check for zero.
var hitPoints = 0;
assert(hitPoints <= 0);
// Check for null.
var unicorn;
assert(unicorn == null);
// Check for NaN.
var iMeantToDoThis = 0 / 0;
assert(iMeantToDoThis.isNaN);
2.4、Lists(列表)
也许几乎所有编程语言中最常见的集合就是数组或有序对象组。在Dart中,数组是List对象,所以通常我们都称之为 lists,Dart中用[ ] 来初始化Lists。
Dart list 字面量和 JavaScript 的数组字面量类似。下面是一个 Dart list 的示例:
var list = [1, 2, 3];
注意:编译器推断该列表具有List
Lists 的下标索引从 0 开始,第一个元素的索引是 0, 最后一个元素的索引是list.length - 1。 访问 list 的长度和元素与 JavaScript 中的用法一样:
var list = [1, 2, 3];
assert(list.length == 3);
assert(list[1] == 2);
list[1] = 1; //通过=运算符 允许直接赋值
assert(list[1] == 1);
Dart中的list来自于List类对象,因此支持add、remove等集合的操作:
var list = [1, 2, 3];
list.add(4);
list.removeLast();
list.remove(1);
assert(list[0]==2);
列表类型有许多便于操作列表的方法。 更多信息参考 泛型 和 集合。
要创建一个编译时常量列表,请在列表字面量之前添加const:
var constantList = const [1, 2, 3];
// constantList[1] = 1; // Uncommenting this causes an error.
2.5、Maps
通常来说,Map 是一个键值对相关的对象。 键和值可以是任何类型的对象。每个键只出现一次, 而一个值则可以出现多次。Dart 通过 map 字面量 和 Map 类型支持 maps。
创建Map方式一: 直接声明,用{}表示,key和value用:隔开,每组键值对中间用逗号隔开:
var gifts = {
// Key: Value
'first': 'partridge',
'second': 'turtledoves',
'fifth': 'golden rings'
};
var nobleGases = {
2: 'helium',
10: 'neon',
18: 'argon',
};
print(nobleGases);//{2: helium, 10: neon, 18: argon}
注意:编译器推断gifts的类型为Map
创建Map方式二:使用Map构造函数创建对象:
var gifts = Map(); //等价于new Map();
gifts['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';
print(gifts);//{first: partridge, second: turtledoves, fifth: golden rings}
var nobleGases = Map();
nobleGases[2] = 'helium';
nobleGases[10] = 'neon';
nobleGases[18] = 'argon';
print(nobleGases);//{2: helium, 10: neon, 18: argon}
注意:在大多数的程序员的印象中应该用new Map()而不是使用Map()来创建一个对象,但是在Dart 2中,new关键字是可选的(可省略的)。更多详细信息参见[使用构造函数]。
往 map 中添加新的键值对、根据键获取值和在 JavaScript 中的用法一样:
var gifts = {'first': 'partridge'};
// Map中添加键值对,中括号中是Key,这里可不是数组
gifts['fourth'] = 'calling birds'; // Add a key-value pair
//获取 map 中的对象也和 JavaScript 的用法一样:
assert(gifts['first'] == 'partridge');
//如果所查找的键不存在,则返回 null:
assert(gifts['fifth'] == null);
map中key和value都允许为null:
var gifts = {};
gifts[null] = null;
assert (gifts[null] == null);
使用 .length 来获取 map 中键值对的数目:
var gifts = {'first': 'partridge'};
gifts['fourth'] = 'calling birds';
assert(gifts.length == 2);
要创建一个编译时常量的map需要在map的字面量前加const关键字:
//const constantMap = {...} 也是同样的效果
final constantMap = const {
2: 'helium',
10: 'neon',
18: 'argon',
};
constantMap[2] = 'Helium';//运行错误
关于 Map 的更多信息请参考 泛型 和 Maps。
2.6、Runes(字符)
在 Dart 中,runes 代表字符串的 UTF-32编码(4个字节)。Unicode 为每一个字符、标点符号、表情符号等都定义了 一个唯一的数值。 由于 Dart 字符串是 UTF-16字符编码(2个字节), 所以在字符串中表达 32-bit 编码 值就需要 新的语法了。
通常使用 \uXXXX 的方式来表示 Unicode code point, 这里的 XXXX 是4个 16 进制的数。 例如,心形符号 (♥) 是 \u2665。 对于非 4 个数值的情况, 把编码值放到大括号中即可。 例如,笑脸 emoji () 是 \u{1f600}。
String 类 有一些属性可以提取 rune 信息。 codeUnitAt 和 codeUnit 属性返回 16-bit code units。 使用 runes 属性来获取字符串的 runes 信息。
下面是示例演示了 runes、 16-bit code units、和 32-bit code points 之间的关系:
var clapping = '\u{1f44f}';
print(clapping);
print(clapping.codeUnits);
print(clapping.runes.toList());
print("===========");
Runes input = new Runes(
'\u2665 \u{1f605} \u{1f60e} \u{1f47b} \u{1f596} \u{1f44d}');
print(input);
print(new String.fromCharCodes(input));
运行效果如下:
2.7、Symbols(符号)
备注:这部分内容大家可以再后续章节中出现时结合了解就行,只看概念很难理解的。
一个 Symbol对象 代表 Dart 程序中声明的操作符或者标识符。 你也许从来不会用到 Symbol,但是该功能对于通过名字来引用标识符的情况 是非常有价值的,特别是混淆后的代码, 标识符的名字被混淆了,但是 Symbol 的名字不会改变。
使用 Symbol 字面量来获取标识符的 symbol 对象,也就是在标识符 前面添加一个 # 符号:
#radix
#bar
Symbol 字面量定义是编译时常量。
欢迎关注我的公众号【不喝咖啡的程序员】,最新的文章会在上面发布: