1. Dart 概述
Dart 语言官网: dart.dev/
Dart 是由谷歌开发的计算机编程语言,它可以被用于 web、服务器、移动应用 和物联网等领域的开发。Dart 诞生于2011年,号称要取代JavaScript。但是过去的几年中一直不温不火。直到 Flutter 的出现在被人们重新重视。
1.1 开发环境
- 要在本地开发 Dart 程序首先需要安装 Dart SDK: dart.dev/get-dart
- 在计算机上安装 Dart SDK, 直接参考官方文档即可。
- 安装完成后,检查 Dart 安装的版本:
$ dart --version
-
下载 VSCode: code.visualstudio.com/
-
安装 Markdown 插件: Markdown All in One
-
安装 Dart 插件,为了让代码可以高亮显示,Dart 插件可以使 VSCode 支持 Dart 编程,最明显的功能便是支持 Dart 语法高亮。
- 安装
Code Runner插件,可以使 VSCode 通过快捷键方式执行脚本,最明显的便是可以在脚本编辑窗口点击运行按钮来直接执行脚本。
1.2 语言特点
-
静态类型系统: Dart 是一种
静态类型语言,通过在编译时检查类型,提高了代码的稳健性和可维护性。 -
面向对象: Dart 支持类和对象,采用经典的
面向对象编程范式,使代码更有结构和组织性。 -
异步编程: Dart 支持
单线程的异步编程,通过async和await简化异步任务的处理,提高应用性能。 -
热重载: Dart 提供
热重载功能,允许开发者在不重新启动应用的情况下即时查看代码变更的效果,提高开发效率。 -
空安全: 引入了
空安全的概念,通过 ? 和 ! 符号来标记可空和非空类型,减少了空指针异常的风险。 -
跨平台开发: Dart 主要用于移动应用开发,与 Flutter 框架结合,实现了
跨平台应用的一体化开发。
1.3 AOT 与 JIT
AOT 编译(Ahead-of-Time): AOT 编译是在应用程序部署之前将 Dart 代码编译成机器码的过程。这意味着在运行时,应用程序的执行不再需要 Dart 解释器,而是直接运行编译后的机器码。AOT 编译可以提高应用程序的启动性能和整体执行速度,适用于需要更高性能的场景,如移动应用开发。Flutter 应用在发布时通常使用 AOT 编译。
JIT 编译(Just-in-Time): JIT 编译是在运行时将 Dart 代码编译成机器码的过程。这种方式使得开发者在开发过程中能够利用热重载等功能,同时也可以更灵活地进行调试。JIT 编译可以在开发阶段提供更快的开发迭代速度,但可能在启动性能方面略逊于 AOT 编译。
在 Dart 中,开发者通常在开发时使用JIT编译,而在发布应用时使用AOT编译以获取更高的性能。这种灵活的编译方式使得 Dart 在开发过程中既能提供敏捷性,又能在生产环境中提供高性能。
2. 第一个 Dart 程序
/**
* main函数是入口函数
*/
void main(List<String> args) {
// print是打印函数
// 每一条语句使用分号结束
print("Hello Dart!");
// 单行注释
/* 多行注释*/
/// 文档注释
/**
* 文档注释
*/
// 在终端运行 dart 100 "hello" true
print("命令行参数: $args"); // 命令行参数: [100, hello, true]
}
- dart 的入口是
main函数 - dart 使用
分号作为语句的结束标记 - dart 和其它语言一样有单行注释、多行注释及文档注释
- 执行 dart 程序
$ dart xxx.dart
3. 关键字
关键字是有特殊含义的标示符,表示特定的作用。
abstract dynamic implements show
as else import static
assert enum in super
async export interface switch
await extends is sync
break external library this
case factory mixin throw
catch false new true
class final null try
const finally on typedef
continue for operator var
covariant Function part void
default get rethrow while
deferred hide return with
do if set yield
4. 标示符的规则
-
标识符可以由字母(大小写均可)、数字、下划线 _ 和美元符号 $ 组成。
-
不能以
数字开头: 标识符不能以数字开头,但可以在之后的位置包含数字。 -
不能是 Dart 的
关键字: 不能使用关键字 int 或 double 作为标识符。 -
严格
区分大小写: Dart 是区分大小写的语言,因此大小写不同的标识符被视为不同的实体。 -
标示符命名一定要
见名知意:变量名称建议使用名词,方法名称建议使用动词。
5. 变量
Dart 中所有的东西都是对象,无论是变量、数字、函数、null 等都是对象。
所有对象都 Object 类的子类,它们的默认值都是null。
Dart 是强类型语言,支持类型推导。
5.1 变量的定义
- 如果使用 var 来声明一个变量,
没有赋初始化,则推导为dynamic类型,其值为 null, 可以赋任何类型的值。
void main(List<String> args) {
var num;
print("num = $num, 类型: ${num.runtimeType}"); // null, Null
num = 100;
print("num = $num, 类型: ${num.runtimeType}"); // 100, int
num = true;
print("num = $num, 类型: ${num.runtimeType}"); // true , bool
}
- 如果使用 var 来声明一个变量,并且
初始化,那么初始化的值是什么类型,则此变量即被锁定为什么类型,不可以赋其它类型的值。
void main(List<String> args) {
var num = 100;
print("num = $num, 类型: ${num.runtimeType}"); // null, Null
num = 200;
print("num = $num, 类型: ${num.runtimeType}"); // 100, int
// 错误, 不是相同类型
// A value of type 'bool' can't be assigned to a variable of type 'int'.
// num = true;
// print("num = $num, 类型: ${num.runtimeType}"); // true , bool
}
- 如果使用
具体类型来定义变量,则必须在使用前赋值,否则不能正确使用(这一点和 Java 是相同的)。
void main(List<String> args) {
int num;
// 报错 The non-nullable local variable 'num' must be assigned before it can be used.
// print("num = ${num}, 类型: ${num.runtimeType}");
num = 100;
print("num = ${num}, 类型: ${num.runtimeType}");
}
- 注意定义变量时,
不能同时使用 var 和具体类型来定义
5.2 变量的类型
- 查看变量的类型技巧: 在 VSCode 中将鼠标放在变量上面,可以看到具体的类型。
- 使用变量的
runtimeType可以查看具体的类型
void main(List<String> args) {
var phone = "iPhone 13";
print("phone的类型: ${phone.runtimeType}"); // phone的类型 String
}
5.3 Object 和 dynamic 的区别
Object 是 Dart 中所有类的基类
dymamic 表示一种特殊的类型,与 Object不是父子关系。
dynamic 和 var 一样是关键字,用来声明变量。
Object 类型定义的变量在编译阶段会进行检查,在使用前必须初始化。
使用 var 和 dynamic 定义变量的效果是一样的,使用编译阶段不会进行任何的检查。
有一点需要注意,dynamic 可以作为函数的返回值类型,而 var 是不可以的。
void main(List<String> args) {
var num;
print("num = $num, 类型: ${num.runtimeType}");
// 调用函数
// num.test(); // 直接运行崩溃
dynamic d;
print("d = $num, 类型: ${d.runtimeType}");
// d.test(); // 直接运行崩溃
Object obj;
// print("obj = $obj, 类型: ${obj.runtimeType}"); // 编译报错,必须赋初值
// obj.test(); // 编译报错
// 几个赋值 没有什么差异
Object b = 0;
b = 100;
b = true;
d = 200;
d = "Hello"
num = false;
num = "java";
}
为什么 dynamic 声明的变量,获取 runtimeType 时会是 Null?
- dymaic 是编译的概念,表示变量可以是任意类型。
- 变量此地没有赋值,就是 null 值
- Null 是一个类型,此时值为 null,则它的运行时类型为 Null,而不是 dynamic.
6. 常量
6.1 常量的基本使用
常量是指值恒定不变的量
Dart 中的常量使用final或const来定义
不管是 const 或者 final 定义的变量都只能赋值一次。
void main(List<String> args) {
final f = 100;
const c = 200;
print("f = $f, c = $c");
// 常量只有赋值一次
// f = 200; // 错误
// c = 200; // 错误
}
final 和 const 可以分别和具体类型一起组合使用且常量关键字必须在最前面,这和 var 是不同的,但是 final 和 const 两个关键字不能同时使用。
void main(List<String> args) {
const String name = "jack";
// String const phone = "jack"; // const 要在最前面
// int final i = 100; // final要在前面
final int age = 18;
}
6.2 常量的注意点
- const 是
编译时常量,final 是运行时常量。final 不仅有 const 的编译时常量的特性,最重要的它是运行时常量,并且 final 是惰性初始化,即在运行时第一次使用前才初始化。
void main(List<String> args) {
// const c; // 错误,声明时必须赋值
final f; // 正确
// print(f); // 错误, 没有值
f = DateTime.now().toLocal().toString();
print(f);
// f = 200; // 错误,常量只能赋值一次
}
- final 可以使用 const 类型的常量来初始化,但反过来不行。因为 const 在编译时需要确定常量值。
void main(List<String> args) {
const c1 = 100;
final f1 = 200;
const c2 = c1;
// const c3 = f1; // 错误
final f2 = f1;
const f3 = c2;
}
- const 修饰的对象只有一个,且不可修改,但 final 可以修改。const 修饰的
对象具有传递性。
void main(List<String> args) {
const list1 = [1, 2, 3];
const list2 = [1, 2, 3];
print(list1 == list2); // true
print(identical(list1, list2)); // true
// 修改会崩溃
// list1[0] = 100;
final list3 = [1, 2, 3];
final list4 = [1, 2, 3];
print(list3 == list4); // false
print(identical(list3, list4)); // false
list3[1] = 200; // 可以修改
// 传递性
const list6 = list1;
// list[6] = 0; // 会崩溃
}
- 可以更改非 final、非 const 变量的值,即使曾经具有 const 值。const 修饰的非对象不具备传递性。
void main(List<String> args) {
const c = 100;
var d = c;
d = 200;
print("c = $c, d = $d");
}