Dart 语法学习目录
第一节: Dart 语法了解,认识变量,常量,数据类型
第二节: Dart 操作符(运算符)
第三节: Dart 中流程控制,条件判断以及循环语句
第四节: Dart 中常用集合 List/Map
第五节: Dart 函数使用,声明函数/匿名函数/可选参数/作用域/闭包
第六节: Dart 面向对象/构造函数/静态属性
第七节: Dart中抽象类abstract/接口implements/混入Mixin
第八节: Dart中泛型/泛型定义与使用
第九节: Dart 中的库/自定义库/内置库/第三方库
1. dart的基本用法了解
1.1 dart入口函数与输出
dart运行需要一个入口函数为main,
通过下面的示例,先简单了解dart基本使用
在dart语法中输出控制台为print() 方法
// 有返回值的dart
main(){
// 打印
print("hello dart")
}
结果会在控制台输出hello dart
我们也可以将main 入口函数定义为没有返回值的函数,
// 没有返回指定入口
void main(){
print("你好 dart")
}
void 是限定函数的返回来写为空
1.2 dart语言中的注释
// 单行注释
/// 单行注释
/*
多行注释
*/
2. Dart 语言重要概念的理解
- 任何保存在变量中的都是一个 对象 , 并且所有的对象都是对应一个 类 的实例。 无论是数字,函数和
null
都是对象。所有对象继承自Object
类。 - 尽管 Dart 是强类型的,但是 Dart 可以推断类型,所以类型注释是可选的。
- Dart 支持泛型,如
List <int>
(整数列表)或List <dynamic>
(任何类型的对象列表) - Dart 支持顶级函数(例如
main()
), 同样函数绑定在类或对象上(分别是 静态函数 和 实例函数 )。 以及支持函数内创建函数 ( 嵌套 或 局部函数 ) 。 - Dart 支持顶级 变量 , 同样变量绑定在类或对象上(静态变量和实例变量)。 实例变量有时称为字段或属性。
- 与 Java 不同,Dart 没有关键字 “public” , “protected” 和 “private” 。 如果标识符以下划线(_)开头,则它相对于库是私有的。
- 标识符 以字母或下划线(_)开头,后跟任意字母和数字组合。
- Dart 语法中包含 表达式( expressions )(有运行时值)和 语句( statements )(没有运行时值)。 例如,条件表达式
condition ? expr1 : expr2
的值可能是expr1
或expr2
。 将其与if-else
语句相比较,if-else 语句没有值。 一条语句通常包含一个或多个表达式,相反表达式不能直接包含语句。 - Dart 工具提示两种类型问题:警告和错误。 警告只是表明代码可能无法正常工作,但不会阻止程序的执行。 错误可能是编译时错误或者运行时错误。 编译时错误会阻止代码的执行; 运行时错误会导致代码在执行过程中引发 [异常]
这些重要的概念先有第一定的了解,读一读, 不理解什么意思没有关系,继续往后学,等学完dart以后在会来来理解这些概念
3. Dart语言中的关键字
关键字先看一眼,混个眼熟, 不用刻意的去背,随着学习的深入,大部分关键词都会接触到,或者在自己的代码示例中出现,用多了就熟了
关键字就不在这里罗列了, 了解请移步官网关键字
4. dart中的变量
4.1 dart 标识符命名规则
- 变量的名称必须有数字,字母,下划线和美元符($)组成
- 标识符不能以数字开头
- 标识符不能是保留字或关键字
- 变量的名称是区分大小写的, 如果,age和 Age不是同一个变量
- 标识符(变量)一定要见名知意,
4.2 变量定义
使用
var
声明变量,此时没有明确类型,编译的时候根据值明确类型dart变量定义时可以明确类型,来帮助 Dart 去捕获异常以及 让代码运行的更高效
变量定义为初始化赋值,变量默认值为null
如果对象不限于单一类型(没有明确的类型),请使用Object或dynamic关键字
根据上面变量定义的描述,我们来好好通过示例来看看变量定义的描述
4.2.1 不明确类型的定义变量
是有var定义变量,不明确变量的类型,dart自己会有类型检查, 会根据var关键字在定义变量时所赋予的初值的类型明确变量的类型
void main(){
// 定义变量
// var 定义变量, 值是什么类型,变量就是什么类型
var str = "hello wrod";
var num = 1234;
// 打印变量
print(str);
print(num);
// 判断str num 变量的类型
print(str is String); // true
print(num is int); // true
}
通过示例的打印结果,我们知道了str
和num
两个变量的数据类型
所以var 定义变量的类型是根据第一次赋值变量的时候推到出来的类型, 也就是说在赋予初始值的时候,变量的类型就已经确定了,
那么问题来了,类型确定了之后的变量还能赋予其他类型的值吗?
看示例:
void main(){
var str2 = '';
strs = 123;
print(str2);
}
// 这样写就会报错, str2 是字符串类型, 赋值数字报错
我们发现一旦变量类型确定在赋值其他类型的变量就会报错.
但是如果通过var 定义变量 并没有赋初值, 那么这个变量不就没办法确定类型
那么此时的类型应该还是默认的类型Null
,这样的话,我们是否可以自由给变量赋予任何数据类型的值呢
所有后面可以自由赋值任何数据类型的值,并且可以更改, 当然不建议这么使用
void main(){
var num;
// var 定义变量没有赋予初始值, 类型为Null
print(num is Null); // true
num = 123;
print(num); // 123
// 当重新给变量赋值时,类型也会发生变化
print(num is int); // true
print(num is Null); // false
num = "str";
print(num); // 'str'
// 还可以自由赋予不同数据类型的值
print(num is String); // true
print(num is Null); // false
}
示例中的代码不会报错, 可以正常输出结果.这样我们就了解了
在Dart
中,如果使用var
关键词定义变量但是没有赋予初始,就好像dart没法限定变量为某一种类型,此时就会像JavaScript
变量一样, 可以赋予任何数据类型的值, 而此时变量的类型将有赋予的值的类型来决定
注意:
使用var定义变量但未赋予初始值. 那么变量的值为null
,类型为Null
void main(){
var num;
print(num == null); // true
print(num is Null); // true
}
总结:
4.2.2 通过指定类型定义变量
当然了dart
也可以在定义变量时限定变量的类型,变量的类型一但被限定, 将不能给变量赋予不同类型的值
示例:
void main(){
// 定义变量
// 确定str的类型是字符串类型的变量, str只能赋值字符串,如果赋值其他类型值将会报错
String str = "hello wrod";
// 限定变量num是数字类型
int num = 1234;
// 打印变量
print(str);
print(num);
}
注意, var 定义变量时,var 后面就不要在写类型了, 限定了变量类型就不要在使用var了
现在我们对于数据类型还不是特别了解,先认识一下示例中所有的数据类型,后面会详细探讨.
4.3 动态类型dynamic
没有指定类型的变量的类型为 dynamic
,
void main(){
dynamic num = 123;
print(num);
num = '456';
print(num);
print(num is dynamic); // true
print(num is String); // true
}
此时并未指定num
变量的类型,类型是有值推导出来的, 所以我们在判断num
类型的时候,dynamic
和String
都为true
dynamic
一般在使用泛型时使用
示例:
void main(){
var list = new List<dynamic>(); //泛型,会在后面总结
list.add("hello");
list.add(123);
print(list); // [hello, 123]
}
此时表示集合list里可以放任意数据类型的值
泛型是什么?先不用在意,我们会进行总结
5. Dart 中定义常量
5.1 常量定义的基本用法
dart中使用final 和const 关键字定义常量
对于Dart定义常量的了解
- 被
final
和const
修饰的变量只能赋值一次,不可更改变量值(定义常量) - 一个 final 变量只能被初始化一次; const变量是一个编译时常量,(Const变量是隐式的final),如果 const 常量在类中,请定义为 static const。
- 被final或者const修饰的变量,变量类型可以省略,建议指定数据类型。
- 使用关键字定义常量时必须在第一次声明的时候就需要初始化,否则会提示错误:
5.1.1 定义常量
常量就是定义之后,不能再修改值的变量.
void main(){
// 定义常量
// const 定义常量
const str = "不可修改的值";
print(str); // 不可修改的值
// final 定义常量
final num = 123;
print(num); //123
}
示例中,可以正常打印出数值, 但是这些常量已经固定, 如果改变这些常量的值,就会报错
如下错误的写法
void main(){
// 定义常量
const str = "不可修改的值";
print(str);
str = '错误的'; // 会报错,常量不可修改
print(str);
}
通过示例,我们发现常量就算你修改为一样类型的数值也会报错
5.1.2 指定常量类
final
和const
定义常量,其实就是定义变量,这两个关键字在修饰变量是,不允许变量被重新赋值而已, 这两个关键字在修饰常量时,可以省略数据类型,
如果在final
和const
修饰常量时没有指定类型,那么变量的类型将有初始赋值的类型决定.
示例:
定义常量时不限定类型
void main(){
// 定义常量
// const 定义常量
const str = "不可修改的值";
print(str); // 不可修改的值
print(str is String); // true
// final 定义常量
final num = 123;
print(num); //123
print(num is int); // true
}
通过示例,发现定义的常量类型就是赋值的数据类型
定义变量时限定类型
void main(){
// 定义常量 限定类型
// 在修饰常量的关键字和变量之前添加修饰变量的类型
// const 定义常量
const String str = "不可修改的值";
print(str); // 不可修改的值
print(str is String); // true
// final 定义常量
final int num = 123;
print(num); //123
print(num is int); // true
}
我们发现提前修饰变量的类型也没有任何问题
如果你修改时的变量类型和赋值的类型不一致就会报错
void main(){
// 定义常量 限定类型
// 在修饰变量的类型和赋值的类型不一致
// const 定义常量
const int str = "不可修改的值";
print(str);
print(str is String);
}
此时不会打印任何内容,会直接报错,因为修饰str
变量的类型是int
,可是赋予的值的类型是String
,所以会直接报错
5.1.3 声明常量初始化赋值,
声明常量时必须在初始化赋值, 否则就会报错
void main(){
// 定义常量 声明时必须初始化赋值
// const 定义常量
// const str ;
// str = "不可修改的值"
final num;
num = 20;
}
无论final
还是const
在声明时未初始化赋值都会报错;
5.2 const 和final 定义常量的区别
5.2.1 常量固定下来的时间不同
Const 变量在编译时就已经固定,最高级 final 变量或类变量在第一次使用时被初始化。
final不仅有const 的编译时常量的特性,最重要的是他运行时常量, final是惰性初始化, 即在运行时第一次使用前才初始化;
void main(){
// 因为 const 声明常量时 在定义时就初始化,所以这么写是错误的
// 所以在const 声明变量在编译时就已经固定了,所以此时赋予的是不能是表达式,而应该是一个确定的值.
// 所以如下写法是会报错了\
// 写法一: 实例化
// const aa = new DateTime.now();
// 写法二:计算
// var num = 10;
// const aa = 123 * num;
// print(aa);
// final 是在第一次使用前才初始化
// 所以在声明常量赋值是并没有固定下来,因此初始赋值可以为表达式
// 下面两种写法完全没问题
// 写法一: 实例化
// final aa = new DateTime.now();
// 写法二: 计算
var num = 10;
final aa = 123 * num;
print(aa);
}
5.2.2 修饰类的实例
注意修饰类实例的变量可以是final类型 ,但不能是const类型
class Person{
// 类属性
String name = '你好';
// 类方法
getInfo(){
print(name);
}
}
void main(){
// 类实例化
// final 修饰类实例化变量
final person = new Person();
person.getInfo(); // 你好
// const就是就会报错
// const person = new Person(); // 报错
}
因为const是在声明时固定的,所以不能用来修饰类实例化的常量
5.2.3 修饰变量值
任何一个变量都可以拥有一个常量的值
const关键字不只是声明常数变量,您也可以使用它来创建常量值,以及声明创建常量值的构造函数,任何变量都可以有一个常量值。
例如:
void main(){
var foo = [1,2,3];
foo.add(6);
print(foo); // [1, 2, 3, 6]
}
这样改变值 没有任何关系
但是如果把值限定为常量就会报错
void main(){
var foo = const [1,2,3];
foo.add(6);
print(foo); // Cannot add to an unmodifiable list
}
因为此时被const
修饰的集合[1,2,3]
是常量值,不能修改.
但是要注意的是尽管你是用了const
限定了常量值,让集合不能添加内容,但是foo
确实变量,所以可以直接替换为非常量值
void main(){
var foo = const [1,2,3];
// 将变量修改为非const值
foo = [4,5,6];
// 此时就可以修改二两
foo.add(7);
print(foo); // [4, 5, 6, 7]
}
final
关键值不能修饰常量值,
如果使用final修饰常量值就会报错
void main(){
var foo = final [1,2,3]; // 直接过错
}
6. dart的数据类型
6.1 类型的了解
常用的数据类型
-
Numbers( 数值)
int
double
-
Strings (字符串)
String
-
Booleans (布尔)
bool
-
List (集合,也被称为数组)
在Dart中,数组是列表对象, 所以大多数人只是称他们为列表
-
Map(字典)
通常来说 Map 是一个键值对相对的对象, 键和值可以是任何类型的对象,每个键
不常用的类型
- Set
- Rune
- Symbol
不常用的方法在这里就不罗列了, 之后有用到咱们在说.
这些类型都可以被初始化为字面量,因为在 Dart 所有的变量终究是一个对象(一个类的实例), 所以变量可以使用 构造涵数 进行初始化。一些内建类型拥有自己的构造函数。
实例:
void main(){
String str = "字符串";
print(str); // 字符串
}
6.2 数值类型
Dart 语言的 Number 有两种类型:
一种是int ,一种是double
- int:其取值通常位于 -253和 253-1 之间
- double:64-bit (双精度) 浮点数,符合 IEEE 754 标准。
int
和 double
都是 num
的亚类型。
void main(){
// 定义数值类型
// 整数类型不包含小数点
int num1 = 23; // init 只能是整型,如果赋值浮点型就报错
double num2 = 22.5; // double可以是整型也可以是浮点型
num2 = 21;
print(num1);
print(num2);
}
6.3 字符串类型
字符串的创建
- 使用单引号
''
,双引号""
- 使用三个 引号
'''
或双引号"""
创建多行字符串 - 使用
r
创建原始raw
字符串
6.3.1 定义单行字符串
void main(){
// 单行字符串
var str1 = '这是单行字符串';
var str2 = "这是单行字符串";
String str3 = '这是单行字符串';
String str4 = "这是单行字符串";
print(str1);
print(str2);
print(str3);
print(str4);
}
6.3.2 定义多行字符串
定义多行字符串的字面量是三个单引号或三个双引号
语法
var str = ''' ''';
或
var str = """ """;
void main(){
// 单行字符串
var str1 = '''
这是多行字符串
这是多行字符串
''';
var str2 = """
这是多行字符串
这是多行字符串
""";
String str3 = '''
这是多行字符串
这是多行字符串
''';
String str4 = """
这是多行字符串
这是多行字符串
""";
print(str1);
print(str2);
print(str3);
print(str4);
}
6.3.3 使用r创建字符串
void main(){
String str = r"Hello World";
print(str); // Hello World
}
了解即可
6.3.4 字符串的拼接
void main(){
String str1 = '你好';
String str2 = "dart";
// 1. 使用加好拼接字符串
print(str1 + ' ' + str2 ); // 你好 dart
// 2. 使用$ 拼接字符串
print("$str1 $str2"); // 你好 dart
// 3. 如果需要运算可以使用${}语法
int num = 20;
int num2 = 30;
print("运算结果:${num + num2}"); // 运算结果:50
}
6.3.5 字符串和数字相互转换的方
void main(){
// 字符串转为数字类型
var one = int.parse("123");
print(one); // 123
var point = double.parse("123.11");
print(point); // 123.11
// 数字类型转字符串类型
var str = 123.toString();
print(str); // 123
var strpoint = 123.111.toString();
print(strpoint); // 123.111
// double转字符串先得小数位数
var strpoint2 = 123.161.toStringAsFixed(2);
print(strpoint2); // 123.11
}
6.4 布尔类型
在dart中定义布尔类型的关键字是bool
布尔值只有个true
,false
,它们都是编译时常量。
void main(){
// 定义布尔类型
bool flag1 = true;
bool flag2 = false;
print(flag1);
print(flag2);
}
添加判断判断
void main(){
// 定义布尔值
bool flag = true;
// 条件判断
if(flag){
print("真");
}else{
print("假");
}
}
要注意dart条件判断没有隐式类型转换
void main(){
var num = 123;
var num2 = '123';
if(num == num2){
print("num和num2的类型和值都相等");
}else{
print("num和num2的类型或者值不相等");
}
}
// 答应不相等, == 不会进行类型转换
6.5 集合类型 (List)
集合就像我们js里面的数组,只是叫法不一样
定义集合
字面量定义集合
void main(){
// 定义集合
// 1. 字面量的方式定义集合
var list = ["张三","李四",'王五', 123];
// 打印集合
print(list);
// 打印集合长度
print(list.length);
// 打印集合中的某一个值
print(list[1]);
}
通过List 构造函数创建集合
void main(){
// 定义集合
// 2. 通过List 构造函数创建集合
List list = new List();
// 给集合添加内容
list.add("张三");
list.add(123);
list.add(true);
print(list);
}
指定集合内容的类型
void main(){
// 定义集合
// 3. 指定集合内容类型
List list = new List<String>();
// <String> 指定集合list里每一项只能为字符串类型
list.add("张三");
// 给list添加了一个不是字符串类型的值会报错
list.add(123);
print(list);
}
6.6 Maps类型
Map 通常也被称之为 字典或者 hash ,也是一个无序的集合,里面 包含一个 key-value 对。map 把 key 和 value 关联起来可以 方便获取数据。
有点类似于JS中的JSON对象, JS中的普通对象,属性可以不用加引号, dart中Maps类型属性一定要加引号(单引双引都可以)
所以注意, JavaScript 对象Object 不是 Maps。
void main(){
// 定义Maps类型;
// 1. 字面量定义map
var map = {
"name":"wuwei",
'age': 18,
"work": ["程序员", "外卖员"]
};
print(map);
// map操作
// map的操作不能使用点语法,只能使用中括号语法
// 中括号语法单引双引都可以
print(map['name']);
print(map["age"]);
}
除了字面量定压力Map外,还可以采用构造函数定义Map
void main(){
// 定义Maps类型;
// 2. 构造函数定义Map
Map map = new Map();
map['name'] = 'wuwei';
map["work"] = ["程序员", "外卖员"];
print(map);
}
注意点:
- 键和值都可以是任何类型的对象。
- 每个 键 只出现一次, 而一个值则可以出现多次
7. 判断数据类型
在dart中可以使用is关键字来判断数据类型
void main(){
// 判断数据类型
// 通过 is关键字
// var str = '123';
var str = 123;
if(str is String){
print("String 类型");
}else if(str is int){
print("int 类型");
}else{
print("其他 类型");
}
}