一、Hello World

第一个 Hello World 程序,很多程序入口都是从 main 函数,Dart 也是如此

  1. void main() {
  2. debugPrint("hello world");
  3. }
  1. 表达式以分号结尾;
  2. 如果函数内部只有一个表达式,可以省略大括号,使用”=>”箭头函数;
    1. void main() => debugPrint("hello world");

    二、定义变量

    明确声明变量的方式, 格式如下:
    1. 变量类型 变量名称 = 变量值;
    示例代码:
    1. String name = "zhang san";
    2. int age = 20;
    3. double height = 1.8;
    4. debugPrint('$name, $age, $height'); //zhang san, 20, 1.8

    三、数据类型

    3.1 布尔类型(bool)

    Dart提供了一个bool的类型, 取值为true和false
    1. bool isShow = true;
    2. debugPrint("$isShow");//true

    3.2 数字类型(num、int、double)

    在 dart 中 num、int、double 都是类,而int、double 继承 num 抽象类
    1. double p = 3.14;
    2. int width = 100;
    3. int height = 500;
    4. debugPrint("$p, $width, $height");//3.14, 100, 500

    3.3 字符串类型(String)

    在 Dart 中使用单引号、双引号、三引号创建一个字符串。字符串和其他变量或表达式拼接: 使用${expression}, 如果表达式是一个标识符, 那么{}可以省略
    1. String str1 = 'Hello Dart';//单引号
    2. String str2 = "Hello Dart";//双引号
    3. String str3 = """Hello Dart1 Hello Dart2 Hello Dart3""";//三引号
    4. debugPrint("$str1, $str2, $str3");//Hello Dart, Hello Dart, Hello Dart1 Hello Dart2 Hello Dart3
    dart 中也有很多字符串操作的方法,比如字符串拆分、截取、大小写、长度、替换等
    1. String url = "https://www.baidu.com";
    2. debugPrint("${url.split("://")}");//字符串分割split方法 [https, www.baidu.com]
    3. debugPrint(url.substring(8, 11));//字符串截取 www
    4. debugPrint(url.toUpperCase());//大写 HTTPS://WWW.BAIDU.COM
    5. debugPrint(url.toLowerCase());//小写 https://www.baidu.com
    6. debugPrint("${url.indexOf("baidu")}");//获取指定字符的索引位置 12
    7. debugPrint("${url.contains("http")}");//字符串是否包含指定字符 true
    8. debugPrint("${url.length}");//获取字符串长度 21
    9. debugPrint(url.replaceAll("w","W"));//全部替换 https://WWW.baidu.com

    3.4 Object 类型

    Dart 中所有东西都是对象,都继承于 Object,可以使用 Object 可以定义任何的变量,而且赋值后类型也可以更改。
    runtimeType用于获取变量当前的类型
    1. Object age = 18;
    2. debugPrint("$age ${age.runtimeType}"); //18 int
    3. age = "28";
    4. debugPrint("$age ${age.runtimeType}"); //28 String

    3.5 dynamic 类型

    Dart 中 dynamic 类型和 Object 类型非常类似,可以像 Object 一样可 以改变类型,dynamic 类型一般用于无法确定具体类型,但是在开发中,通常情况下不使用dynamic,因为类型的变量会带来潜在的危险
    Object 和 dynamic 区别在于: Object 会在编译阶段检查类型,而 dynamic 不会在编译阶段检查类型。
    1. dynamic age = 18;
    2. debugPrint("$age ${age.runtimeType}"); //18 int
    3. age = "28";
    4. debugPrint("$age ${age.runtimeType}"); //28 String

    3.6 var类型

    Dart 中可以使用 var 来替代具体类型的声明,会自动推导变量的类型。
    注意: var 声明的变量未赋值的时,变量的值可以改变,同时它的类型也可以被修改;如果 var 声明的变量初始化赋值后, 类型就会确定,后续不能被改变。 ``` var name = “zhang san”; debugPrint(“$name, ${name.runtimeType}”); //zhang san, String

var age; debugPrint(“$age ${age.runtimeType}”); //null Null age = 18; debugPrint(“$age ${age.runtimeType}”); //18 int age = “28”; debugPrint(“$age ${age.runtimeType}”); //28 String

var width = 100; //声明时并赋值,width的类型已推导出为int类型 width = “150”; //错误,A value of type ‘String’ can’t be assigned to a variable of type ‘int’.

  1. <a name="OGXxT"></a>
  2. ### 3.7 const&final类型
  3. Dart 中声明常量可以使用 const 或 final 两个关键字,定义之后值都不可以修改

const age = 18; age = 28; // 错误 Constant variables can’t be assigned a value.

const width; // 错误 The constant ‘width’ must be initialized.

final name = “zhang san”; name = “li si”; // 错误 The final variable ‘name’ can only be set once.

  1. const&final两者的区别:
  2. - const在赋值时,赋值的内容必须是在编译期间确定下来
  3. - final在赋值时,可以动态获取,比如赋值一个函数

const age1 = getAge(); //错误 Const variables must be initialized with a constant value.

final age2 = getAge(); debugPrint(“$age2 ${age2.runtimeType}”); // 18 int

int getAge() { return 18; }

  1. <a name="YVLDP"></a>
  2. ### 3.8 类型检查(is 和 is!)和强制类型转换(as)
  3. Dart 通过 is 关键字来对类型进行检查以及使用 as 关键字 对类型进行强制转换,如果判断不是某个类型 dart 中使用 is! 。

int width = 100; double height = 90.5; num age = 18; debugPrint(“${width is double}”); //false debugPrint(“${height is! int}”); //true debugPrint(“${age as int}”); //18

  1. <a name="nyIEn"></a>
  2. ## 四、合集(List、Set、Map)
  3. <a name="tycq3"></a>
  4. ### 4.1 List
  5. - 初始化

//空集合 List arr1 = []; debugPrint(“$arr1 ${arr1.runtimeType}”); //[] List

//明确类型 List arr2 = [“a”, “b”, “c”]; debugPrint(“$arr2 ${arr2.runtimeType}”); //[a, b, c] List

List arr3 = [“a”, “b”, “c”]; debugPrint(“$arr3 ${arr3.runtimeType}”); //[a, b, c] List

//类型推导 var arr4 = [1, 2, 3]; debugPrint(“$arr4 ${arr4.runtimeType}”); //[1, 2, 3] List

  1. - 常用函数

List arr = [“a”, “b”, “c”, “d”]; arr.add(“e”); //添加一个新的元素 debugPrint(“$arr”); //[a, b, c, d, e]

arr.insert(1, “f”); //在集合指定index位置插入指定的元素 debugPrint(“$arr”); //[a, f, b, c, d, e]

arr.removeAt(2);//移除集合指定的index元素 debugPrint(“$arr”); //[a, f, c, d, e]

debugPrint(“${arr.sublist(1, 3)}”); //截取子集合 [f, c]

debugPrint(arr.join(“-“)); //拼接元素 a-f-c-d-e

debugPrint(arr[2]); //使用数组下标形式访问元素 c

debugPrint(“${arr.length}”); //获取集合的长度 5

debugPrint(“${arr.contains(“d”)}”); //是否包含某个元素 true

arr.clear(); //清除所有元素 debugPrint(“${arr.isEmpty}”); //true debugPrint(“$arr”); //[]

  1. <a name="vQEj1"></a>
  2. ### 4.2 Set
  3. - Set和List的区别在于集合中的元素是不能重复的
  4. - 初始化

//空集合 Set arr1 = {}; debugPrint(“$arr1 ${arr1.runtimeType}”); //{} _CompactLinkedHashSet

//明确类型 Set arr2 = {“a”, “b”, “c”}; debugPrint(“$arr2 ${arr2.runtimeType}”); //{a, b, c} _CompactLinkedHashSet

Set arr3 = {“a”, “b”, “c”}; debugPrint(“$arr3 ${arr3.runtimeType}”); //{a, b, c} _CompactLinkedHashSet

//类型推导 var arr4 = {1, 2, 3}; debugPrint(“$arr4 ${arr4.runtimeType}”); //{1, 2, 3} _CompactLinkedHashSet

  1. - 常用函数

Set arr = {“a”, “b”, “c”, “d”}; arr.add(“e”); //添加一个新的元素 debugPrint(“$arr”); //{a, b, c, d, e}

arr.remove(“c”); //删除指定元素 debugPrint(“$arr”); //{a, b, d, e}

debugPrint(arr.join(“-“)); //拼接元素 a-b-d-e

debugPrint(“${arr.length}”); //获取集合的长度 4

debugPrint(“${arr.contains(“d”)}”); //是否包含某个元素 true

arr.clear(); //清除所有元素 debugPrint(“${arr.isEmpty}”); //true debugPrint(“$arr”); //{}

  1. <a name="g0zz7"></a>
  2. ### 4.3 Map
  3. - Map为key-value 形式存储,并且Map中key是不能重复的
  4. - 初始化

//空集合 Map tempMap1 = {}; debugPrint(“$tempMap1 ${tempMap1.runtimeType}”); //{} _InternalLinkedHashMap

//明确类型 Map tempMap2 = {“name”:”zhang san”, “age”: 18}; debugPrint(“$tempMap2 ${tempMap2.runtimeType}”); //{name: zhang san, age: 18} _InternalLinkedHashMap

Map tempMap3 = {“name”:”zhang san”, “age”: 18}; debugPrint(“$tempMap3 ${tempMap3.runtimeType}”); //{name: zhang san, age: 18} _InternalLinkedHashMap

//类型推导 var tempMap4 = {“name”:”zhang san”, “age”: 18}; debugPrint(“$tempMap4 ${tempMap4.runtimeType}”); //{name: zhang san, age: 18} _InternalLinkedHashMap

  1. - 常用函数

var tempMap = {“name”:”zhang san”, “age”: 18}; //根据key获取value debugPrint(“${tempMap[“name”]}”); //zhang san

//获取所有的entries debugPrint(‘${tempMap.entries} ${tempMap.entries.runtimeType}’); //(MapEntry(name: zhang san), MapEntry(age: 18)) MappedIterable>

//获取所有的keys debugPrint(‘${tempMap.keys} ${tempMap.keys.runtimeType}’); //(name, age) _CompactIterable

//获取所有的values debugPrint(‘${tempMap.values} ${tempMap.values.runtimeType}’); //(zhang san, 18) _CompactIterable

//判断是否包含某个key或者value debugPrint(‘${tempMap.containsKey(“age”)} ${tempMap.containsValue(18)}’); //true true

//获取key的List debugPrint(“${tempMap.keys.toList()}”);//[name, age]

//获取value的List debugPrint(“${tempMap.values.toList()}”);//[zhang san, 18]

//根据key删除元素 tempMap.remove(“name”); debugPrint(“$tempMap”); //{age: 18}

//根据key修改value tempMap[“age”] = 28; debugPrint(“$tempMap”); //{age: 28}

  1. <a name="XbVg6"></a>
  2. ## 五、运算符
  3. <a name="I7UyJ"></a>
  4. ### 5.1 算术运算符
  5. | **名称** | **运算符** | **举例** |
  6. | --- | --- | --- |
  7. | 加 | + | var result1 = 5 + 3; //8 |
  8. | 减 | - | var result2 = 5 - 3; //2 |
  9. | 乘 | * | var result3 = 5 * 3; //15 |
  10. | 除 | / | var result4 = 5 / 3; //1.6666666666666667 |
  11. | 整除 | ~/ | var result5 = 5 ~/ 3; //1 |
  12. | 取模 | % | var result6 = 5 % 3; //2 |
  13. <a name="u3qmN"></a>
  14. ### 5.2 条件运算符
  15. | **名称** | **运算符** | **举例** |
  16. | --- | --- | --- |
  17. | 大于 | > | debugPrint("${5 > 3}"); //true |
  18. | 小于 | < | debugPrint("${5 < 3}"); //false |
  19. | 等于 | == | debugPrint("${5 == 3}"); //false |
  20. | 不等于 | != | debugPrint("${5 != 3}"); //true |
  21. | 大于等于 | >= | debugPrint("${5 >= 3}"); //true |
  22. | 小于等于 | <= | debugPrint("${5 <= 3}"); //false |
  23. <a name="FLqdD"></a>
  24. ### 5.3 逻辑运算符
  25. | **名称** | **运算符** | **举例** |
  26. | --- | --- | --- |
  27. | 与 | && | debugPrint("${5 > 3 && 1 < 2}"); //true |
  28. | 或 | &#124;&#124; | debugPrint("${5 < 3 &#124;&#124; 1 > 2}"); //false |
  29. | 非 | ! | debugPrint("${!(5 == 3)}"); //true |
  30. <a name="cQPpp"></a>
  31. ### 5.4 三目运算符
  32. condition ? expr1 : expr2

bool isShow = true; var result = isShow ? “Dart” : “Flutter”; debugPrint(result); //Dart

  1. <a name="qVdcr"></a>
  2. ### 5.5 空安全运算符
  3. - result = expr1 ?? expr2
  4. 如果发现 expr1 为 null,就返回 expr2 的值,否则就返回 expr1 的值。

var temp = “zhang san”; // var temp = null; var name = temp ?? “li si”; debugPrint(name); //zhang san

  1. - expr1 ??= expr2
  2. 等价于 expr1 = expr1 ?? expr2

var name = “zhang san”; // var name = null; debugPrint(name); //zhang san name ??= “li si”; debugPrint(name); //zhang san

  1. - result = expr1?.value
  2. 如果 expr1 不为 null 就返回 expr1.value,否则就会返回 null
  3. <a name="zR11K"></a>
  4. ### 5.6 级联操作符
  5. 级联操作符是 ..,可以让你对一个对象中字段进行链式调用操作

var p1 = Person(); p1.name = “zhang san”; p1.run(); //zhang san 跑步

var p2 = Person() ..name = “li si” ..run(); //li si 跑步

class Person { String? name; int? age; void run () { debugPrint(“$name 跑步”); } }

  1. <a name="S7QUJ"></a>
  2. ## 六、流程控制
  3. <a name="v4dgl"></a>
  4. ### 6.1 if-else
  5. if后面跟一个可选的else块。如果if块测试的布尔表达式求值为false,则执行else块。<br />条件控制语句,不支持非空即真或者非0即真,必须有明确的bool类型

bool isShow = true; if (isShow) { debugPrint(“条件成立”); } else { debugPrint(“条件不成立”); }

  1. <a name="jlRJ0"></a>
  2. ### 6.2 for 循环
  3. for循环是一个确定循环的实现,用于执行代码块指定的次数。<br />for...in循环用于循环对象的属性,在每次迭代中,对象的一个属性分配给变量名称,并且此循环继续,直到对象的所有属性都用完为止。

var names = [“zhang san”, “li si”, “wang wu”]; for (var i = 0; i < names.length; i++) { debugPrint(names[i]); //zhang san li si wang wu }

for (var name in names) { debugPrint(name); //zhang san li si wang wu }

  1. <a name="VYRNJ"></a>
  2. ### 6.3 while 循环
  3. while(<条件>) <语句>;<br />每次指定的条件求值为true时,while循环都会执行指令。在执行代码块之前评估条件。

var names = [“zhang san”, “li si”, “wang wu”]; var index = 0; while (index < names.length) { debugPrint(names[index++]);//zhang san li si wang wu }

  1. <a name="n8ULn"></a>
  2. ### 6.4 do-while 循环
  3. do <语句> while(<条件>);<br />do...while循环类似于while循环,只是do...while循环不会在第一次循环执行时评估条件。

var names = [“zhang san”, “li si”, “wang wu”]; var index = 0; do { debugPrint(names[index++]); //zhang san li si wang wu } while (index < names.length);

  1. <a name="noqrS"></a>
  2. ### 6.5 break 和 continue
  3. break语句用于将控件从构造中取出。在循环中使用break会导致程序退出循环。<br />continue语句跳过当前迭代中的后续语句,并将控制权带回循环的开头。

var names = [“zhang san”, “li si”, “wang wu”]; for (var i = 0; i < names.length; i++) { if (names[i] == “li si”) { continue; } if (names[i] == “wang wu”) { break; } debugPrint(names[i]); //zhang san }

  1. <a name="QKafr"></a>
  2. ### 6.6 switch-case
  3. switch语句计算表达式,将表达式的值与case子句指定的值匹配,然后执行与该case相关的语句。<br />注意:每一个case语句,默认情况下必须以一个break结尾

var name = “zhang san”; switch (name) { case “zhang san”: debugPrint(“名字叫$name”); break; case “li si”: debugPrint(“名字叫$name”); break; case “wang wu”: debugPrint(“名字叫$name”); break; default: debugPrint(“其他名字”); break; }

  1. <a name="It3HG"></a>
  2. ## 七、异常
  3. Dart 中的异常捕获方使用的是 try-catch-finally

int a = 18; int result = 0; try { result = a ~/ 0; } catch (error) { debugPrint(“$error”); // 抛出异常 IntegerDivisionByZeroException } finally { debugPrint(“执行完成”); // 抛出异常后也会执行 }

  1. <a name="y7aT2"></a>
  2. ## 八、函数
  3. <a name="NcoTo"></a>
  4. ### 8.1 函数的基本用法
  5. 函数的定义方式:

返回值 函数的名称(参数列表) { 函数体 return 返回值 }

  1. void关键字表示该函数不向调用者返回任何值。<br />示例代码:

void test() { debugPrint(“test”); }

  1. <a name="mz0u1"></a>
  2. ### 8.2 函数的参数
  3. 参数是一种将值传递给函数的机制。<br />函数的参数可以分成两类: 必须参数和可选参数。
  4. <a name="DCNZt"></a>
  5. #### 8.2.1 必须参数

int sum(int a, int b) { return a + b; }

  1. <a name="ltPe8"></a>
  2. #### 8.2.2 可选参数
  3. 可选参数可以分为 命名可选参数 和 位置可选参数<br />定义方式:

命名可选参数: {param1, param2, …} 位置可选参数: [param1, param2, …]

  1. 命名可选参数示例:<br />命名可选参数,可以指定某个参数是必传的,在参数前面添加required即可<br />命名可选参数,可以设置参数默认值

printUserInfo1(“zhang san”, age: 18, height: 1.8); //name=zhang san age=18 height=1.8 void printUserInfo1(String name, {int? age, double? height}) { debugPrint(“name=$name age=$age height=$height”); }

printUserInfo2(“li si”, age: 28); //name=li si age=28 height=null void printUserInfo2(String name, {required int age, double? height}) { debugPrint(“name=$name age=$age height=$height”); }

printUserInfo3(“wang wu”); //name=wang wu age=null height=2.26 void printUserInfo3(String name, {int? age, double height = 2.26}) { debugPrint(“name=$name age=$age height=$height”); }

  1. 位置可选参数示例:<br />位置可选参数,可以设置参数默认值

printUserInfo4(“zhao liu”); //name=zhao liu age=null height=2.26 printUserInfo4(“zhao liu”, 38, 1.98); //name=zhao liu age=38 height=1.98 void printUserInfo4(String name, [int? age, double height = 2.26]) { debugPrint(“name=$name age=$age height=$height”); }

  1. <a name="oMWY8"></a>
  2. ### 8.3 高阶函数
  3. Dart中函数类型为Function,函数可以作为变量定义或者其他函数的参数或者返回值。

//函数传给变量 var fn1 = run; fn1(“zhang san”);

//函数作为另一个函数的参数 test2(run);

//函数作为另一个函数的返回值 var fn2 = getFunc(); fn2(“wang wu”); //wang wu 跑步

void run(String name) { debugPrint(“$name 跑步”); }

void test2(Function fn) { fn(“li si”); }

Function getFunc() { return run; }

  1. <a name="qZ3pg"></a>
  2. ### 8.4 箭头函数
  3. 如果函数中只有一个表达式,可以使用箭头函数

返回值 函数的名称(参数列表)=>函数体

  1. 箭头函数示例:

printHelloFlutter(); //Hello Flutter void printHelloFlutter() => debugPrint(“Hello Flutter”);

  1. <a name="ODiPs"></a>
  2. ### 8.5 匿名函数
  3. Dart 中可以创建一个没有函数名称的函数,称为匿名函数,或者lambda 函数或者闭包函数。

var names = [“zhang san”, “li si”, “wang wu”]; //有名字的函数 void printElement(element) { debugPrint(element); } names.forEach(printElement); //zhang san li si wang wu

//匿名函数 names.forEach((element) { debugPrint(element); //zhang san li si wang wu });

//箭头 names.forEach((element) => debugPrint(element)); //zhang san li si wang wu

  1. <a name="shjKt"></a>
  2. ### 8.6 词法作用域
  3. Dart中的词法有自己明确的作用域范围,根据代码的结构({})来决定作用域范围的<br />变量查找的规则是从当前作用域查找,如果没找到再到父级作用域中查找,一层层往外查找,如果在全局作用域还没找到的话,就会报错。

String name = “li si”; void testDomain(){ // String name = “zhang san”; debugPrint(name); } testDomain(); //li si

  1. <a name="gDEXk"></a>
  2. ### 8.7 返回值问题
  3. - 所有函数都返回一个值。如果没有指定返回值,则语句返回null。
  4. - return类型可以是任何有效的数据类型。
  5. - 返回值的数据类型必须与函数的返回类型匹配。
  6. - 函数最多可以返回一个值,每个函数只能有一个return语句。
  7. 注意:Dart中可以忽略掉函数的返回类型,忽略后仍能正常调用,被省略的函数返回值不是 void。

debugPrint(testReturnValue()); //null testReturnValue() { debugPrint(“testReturnValue”); }

  1. <a name="pTwCq"></a>
  2. ## 九、类和对象
  3. <a name="CKnr8"></a>
  4. ### 9.1 类的定义
  5. <a name="Und9R"></a>
  6. #### 9.1.1 类的声明
  7. Dart是一种面向对象的语言,使用class关键字声明一个类。<br />类定义以关键字class开头,后跟类名,并且由一对花括号包围的类体。<br />类定义可包括以下内容:
  8. - 成员变量是类中声明的任何变量,表示与对象有关的数据。
  9. - setters和getters允许程序初始化和检索类成员变量的值。
  10. - 构造函数负责为类的对象分配内存。
  11. - 函数表示对象可以采取的操作,它们有时也称为方法。
  12. 注意: 方法中使用属性有命名冲突时,属性前面加this。

class 类名 { 类型 成员名; 返回值类型 方法名(参数列表) { 方法体 } }

  1. 示例:

class Person { String? name; int? age; void run() { debugPrint(“$name 跑步”); } }

  1. <a name="w2nLD"></a>
  2. #### 9.1.2 实例变量
  3. 实例变量为类的成员变量或者称为成员属性,当声明一个实例变量时,每一个对象实例都会有这个属性。<br />如果要表示实例私有属性,直接在属性名前面加下划线_,如_name 和_age,后面使用getter和setter的时候使用的到。
  4. <a name="UnKay"></a>
  5. #### 9.1.3 类成员与方法
  6. static关键字可以应用于类的数据成员,即字段和方法。<br />静态变量保留其值,直到程序完成执行。<br />静态成员由类名引用。

Person.name = “zhang san”; Person.age = 18; Person.run(); //zhang san 跑步

class Person { static String? name; static int? age; static void run() { debugPrint(“$name 跑步”); } }

  1. <a name="kE9T4"></a>
  2. ### 9.2 构造方法
  3. <a name="F9XgP"></a>
  4. #### 9.2.1 普通构造方法
  5. 构造函数是类的特殊函数,负责初始化类的变量。<br />当类中没有定义构造方法时,将默认拥有一个无参的构造方法。<br />当类中自定义了构造方法时,默认的构造方法将会失效。<br />语法:

类名(参数列表) { 函数体 }

  1. 示例:<br />在实现构造方法时,通常是通过参数列表给属性赋值,Dart提供了一种简洁的语法糖,如下:

class Person { String? name; int? age; // Person(String? name, int? age) { // this.name = name; // this.age = age; // } Person(this.name, this.age); }

  1. <a name="hTZia"></a>
  2. #### 9.2.2 命名构造方法
  3. 开发中希望实现多个构造方法,但Dart不支持方法(函数)的重载,不能创建相同名称的构造方法。所以Dart提供了命名构造函数,以此来定义多个构造函数。<br />语法:

类名.构造名(参数列表){ 函数体 }

  1. 示例:

var p1 = Person(name: “zhang san”, age: 18); debugPrint(“$p1”); //name:zhang san age:18 var p2 = Person.withArguments(name: “li si”, age: 28); debugPrint(“$p2”); //name:li si age:28

class Person { String? name; int? age; Person({this.name, this.age}); Person.withArguments({this.name, this.age});

@override String toString() { return “name:$name age:$age”; } }

  1. <a name="hO4Wb"></a>
  2. #### 9.2.3 初始化列表
  3. Dart 中使用 final 修饰变量,表示该变量初始化后不能再被修改。<br />final 修饰的变量只有 getter 访问器函数,没有 setter 函数。<br />final 修饰的变量要在构造方法中进行初始化。<br />有时候我们希望通过调用不同的构造函数来初始化某一个属性,需要在构造函数后加冒号“:”,然后再设置属性的值,称之为初始化列表

var p1 = Person.withName1(name: “zhang san”, age: 18); debugPrint(“$p1”); //name:zhang san age:18 height:1.83 var p2 = Person.withName2(name: “li si”, age: 28); debugPrint(“$p2”); //name:li si age:28 height:2.26

class Person { final String? name; final int? age; final double height; Person.withName1({this.name, this.age}) : height = 1.83; Person.withName2({this.name, this.age}) : height = 2.26;

@override String toString() { return “name:$name age:$age height:$height”; } }

  1. <a name="LsTlD"></a>
  2. #### 9.2.4 重定向构造方法
  3. 如果需要在一个构造函数调用同一个类中的另一个构造函数, 可以使用重定向构造方法,重定向构造函数的主体为空,构造函数的调用出现在冒号(:)之后,使用this调用

var p = Person.withName(name: “zhang san”); debugPrint(“$p”); //name:zhang san age:18

class Person { String? name; int? age; Person({this.name, this.age}); Person.withName({String? name}) : this(name: name, age: 18);

@override String toString() { return “name:$name age:$age”; } }

  1. <a name="i9joO"></a>
  2. #### 9.2.5 常量构造方法
  3. 如果调用构造函数,在传入相同值,希望返回同一个对象,可以使用常量构造方法。构造的对象使用identical(Object? a, Object? b)函数来判断两个对象是否是同一个对象。常量构造需要注意如下几点:
  4. - 常量构造函数需以const关键字修饰
  5. - 常量构造方法的类中,所有的成员变量必须是final修饰
  6. - 构建常量实例必须使用定义的常量构造函数
  7. - 如果实例化时不加const修饰符,即使调用的是常量构造函数,实例化的对象也不是常量实例

var p1 = const Person(name: “zhang san”); var p2 = const Person(name: “zhang san”); debugPrint(“${identical(p1, p2)}”); //true

class Person { final String? name; const Person({this.name});

@override String toString() { return “name:$name”; } }

  1. <a name="SIQ2u"></a>
  2. #### 9.2.6 工厂构造方法
  3. 通常构造函数总是会创建一个新的实例对象,但有时候不是每次都需要创建新的实例,可能需要使用缓存。需要使用factory 修饰的工厂构造函数,可以从缓存中的返回已经创建实例或者返回一个新的实例。<br />工厂构造函数也是一种构造函数,与普通构造函数不同,不会自动生成实例,而是通过代码来决定返回的实例对象。<br />工厂构造函数类似于 static 静态成员,无法访问 this 关键字;同时需要依赖其他类型构造函数。

var p1 = Person(“zhang san”); var p2 = Person(“zhang san”); var p3 = Person(“li si”); var p4 = Person._(“li si”); debugPrint(“${identical(p1, p2)}”); //true debugPrint(“${identical(p1, p3)}”); //false debugPrint(“${identical(p3, p4)}”); //false

class Person { final String name;

static final Map _cache = {};

factory Person(String name) { bool isContain = cache.containsKey(name); Person? tempP = _cache[name]; if (isContain && tempP != null) { return tempP; } else { final p = Person.(name); _cache[name] = p; return p; } }

Person._(this.name); }

  1. 工厂构造函数可以实现单例

var p1 = Person(“zhang san”); var p2 = Person(“li si”); var p3 = Person(“wang wu”); var p4 = Person._(“zhao liu”); debugPrint(“${identical(p1, p2)}”); //true debugPrint(“${identical(p1, p3)}”); //true debugPrint(“${identical(p3, p4)}”); debugPrint(“$p1”); //zhang san debugPrint(“$p2”); //zhang san debugPrint(“$p3”); //zhang san debugPrint(“$p4”); //zhao liu

class Person { final String name; // 静态属性存放实例化对象 static Person? instance; factory Person(String name) { return Person._instance ??= Person.(name); } Person._(this.name);

@override String toString() { return “name:$name”; } }

  1. <a name="OXHjO"></a>
  2. ### 9.3 getter和setter
  3. getter和setter(也称为访问器和更改器)允许程序分别初始化和检索类字段的值。<br />使用get关键字定义getter或访问器,getter没有参数,但返回一个值。<br />使用set关键字定义Setter或存取器,setter只有一个参数但不返回值。<br />在给实例属性赋值或获取值时,实际上内部都是对 setter 和 getter 函数的调用。<br />如果需要监控类的属性被访问的过程,可以使用setter和getter。<br />注意:成员属性必须是下划线“_”私有属性,然后使用control+enter快捷键,选择生成getter和setter方法

var p = Person(name: “zhang san”); debugPrint(“$p”); //name:zhang san p.name = “li si”; //setName debugPrint(“$p”); //name:li si

class Person { late String _name;

String get name { debugPrint(“getName”); return _name; }

set name(String value) { debugPrint(“setName”); _name = value; }

Person({required String name}) { _name = name; }

@override String toString() { return “name:$name”; } }

  1. <a name="VIIHO"></a>
  2. ### 9.4 类的继承
  3. Dart支持继承的概念,它是程序从现有类创建新类的能力。<br />一个类使用extends关键字从另一个类继承,子类中使用super来访问父类。<br />子类继承除父类的构造函数之外的所有属性和方法。<br />注意:Dart不支持多重继承。<br />语法:

class 子类名 extends 父类名

  1. 示例:

var p = Person(); p.name = “zhang san”; debugPrint(“${p.name}”); //zhang san p.run(); //跑步

class Animal { String? name; void run() { debugPrint(“跑步”); } }

class Person extends Animal {

}

  1. 子类可以拥有自己的成员变量, 并且可以对父类的方法进行重写:

var p = Person(); p.name = “zhang san”; p.age = 18; debugPrint(“${p.age}”); //18 p.run(); //zhang san 在跑步

class Animal { String? name; void run() { debugPrint(“跑步”); } }

class Person extends Animal { int? age;

@override void run() { debugPrint(“$name 在跑步”); } }

  1. 子类中可以调用父类的构造方法,对某些属性进行初始化:
  2. - 子类的构造方法在执行前,将隐含调用父类的无参默认构造方法(没有参数且与类同名的构造方法)。
  3. - 如果父类没有无参默认构造方法,则子类的构造方法必须在初始化列表中通过super显式调用父类的某个构造方法。

var p = Person(“zhang san”); p.run(); //zhang san 在跑步

class Animal { String? name; Animal(this.name); void run() { debugPrint(“跑步”); } }

class Person extends Animal { int? age;

Person(String? name) : super(name);

@override void run() { debugPrint(“$name 在跑步”); } }

  1. <a name="MBVKb"></a>
  2. ### 9.5 抽象类
  3. 当父类本身不需要对某些方法进行具体的实现,所以父类中定义的方法可以定义为抽象方法。抽象方法是声明一个方法而不提供它的具体实现。而抽象方法必须存在于抽象类中,抽象类是使用abstract声明的类。

var p = Person(name: “zhang san”, age: 18); p.run(); //zhang san 在跑步

abstract class Animal { void run(); void fly() {

} }

class Person extends Animal { String? name; int? age; Person({this.name, this.age});

@override void run() { debugPrint(“$name 在跑步”); }

}

  1. 注意事项:
  2. - 抽象类不能实例化。
  3. - 抽象类中的抽象方法必须被子类实现,抽象类中的已经被实现方法,可以不被子类重写。
  4. <a name="Vf7WO"></a>
  5. ### 9.6 隐式接口(implements)
  6. Dart中不支持多继承,如果想要类似的多继承该如何实现呢?<br />Dart中默认情况下定义的每个类都相当于默认声明了一个接口,可以由其他的类来实现。<br />如果有⼀个类 A,你想让类B拥有AAPI,但⼜不想拥有A⾥的实现,那么你就应该把A当做接⼝,类B implements A。<br />在通过implements实现某个类时:
  7. - 类中所有的方法都必须被重新实现,无论这个类原来是否已经实现过该方法,在子类实现的时候要加@override
  8. - 类中的成员变量也需要在子类里重新实现。在成员变量前加@override

var m = SuperMan(); m.name = “zhang san”; m.run(); //超人在奔跑 m.fly(); //超人在飞 m.jump(); //超人 zhang san 在跳

abstract class Runner { run(); }

abstract class Flyer { fly(); }

class Jumper { String? name; jump() { debugPrint(“超人在跳”); } }

class SuperMan implements Runner, Flyer, Jumper { @override run() { debugPrint(“超人在奔跑”); }

@override fly() { debugPrint(“超人在飞”); }

@override jump() { debugPrint(“超人 $name 在跳”); }

@override String? name;

}

  1. <a name="c4oRt"></a>
  2. ### 9.7 Mixin混入
  3. 如果一个类只直接调用多个类中的原有某些方法,该怎么实现呢?
  4. - 使用继承吗?Dart只支持单继承,只能复用一个类的实现。
  5. - 隐式接口吗?需要实现所有的方法和属性。
  6. - Dart提供了Mixin混入的方案
  7. 我们可以通过class定义类之外,也可以通过mixin关键字来定义一个类。通过mixin定义的类用于被其他类混入使用,需要通过with关键字进行混入。

var m = SuperMan(); m.name = “zhang san”; m.run(); //超人在奔跑 m.fly(); //超人在飞 m.jump(); //超人 zhang san 在跳

mixin Runner { void run() { debugPrint(“超人在奔跑”); } }

mixin Flyer { fly() { debugPrint(“超人在飞”); } }

mixin Jumper { String? name; jump() { debugPrint(“超人 $name 在跳”); } }

class SuperMan with Runner, Flyer, Jumper {

}

  1. <a name="peDxb"></a>
  2. ### 9.8 枚举类型
  3. 使用enum关键字来进行定义:

enum 枚举名 { 枚举值1, 枚举值2, 枚举值3, … }

  1. 枚举类型中有两个比较常见的属性:
  2. - index: 用于表示每个枚举常量的索引, 0开始。
  3. - values: 包含每个枚举值的List
  4. 示例:

debugPrint(“${MyColors.red}”); //MyColors.red debugPrint(“${MyColors.red.index}”); //0 debugPrint(“${MyColors.green.index}”); //1 debugPrint(“${MyColors.blue.index}”); //2 debugPrint(“${MyColors.values}”); //[MyColors.red, MyColors.green, MyColors.blue]

enum MyColors { red, green, blue } ```