[Flutter]flutter基础之Dart语言基础(一)

一、概述

Dart 是 Google 公司在2011年10月发布的一种编程语言,主要是将其作为一种结构化的 Web 开发语言。可以用在Web、服务器、移动应用和物联网等领域的开发,是宽松开源许可证(修改的BSD证书)下的开源软件。官方网站:https://dart.dev/

在 Dart 中,所有东西都是对象,变量,数字,函数等都是对象类型,都是类的实例,所有对象都继承自 Object 类。

在进行 Dart 开发前,需要安装 Dart SDK,安装方法官网网站有介绍,地址为:https://dart.dev/get-dart 。如直接进行 Flutter 开发,也可不用下载此 SDK ,直接安装 Flutter 即可。Flutter开发工具包中包含 Dart。

二、语法基础

1. 入口方法

Dart 语言文件格式为 .dart ,并以 main 方法作为程序的入口点,我们使用 VSCode 创建 demo1.dart 文件,代码如下:

main(){
  print('hello dart!');  //输出结果:hello dart!
}

Dart 语言有很多简写形式,以上的 main 方法就是简写,完整的写法如下:

void main(List<String> args){
  print('hello dart!');
}

参数为泛型的写法,List 为列表,也就是其他语言中的数组。 main 方法可以接收由命令行输入的参数,可以通过 args 参数在程序中获取。如下代码:

void main(List<String> args){
  print('hello dart!');
  print(args);
}

通过命令行的方式运行 dart 文件,首先 cd 到目标文件夹,运行如下命令: dart demo1.dart arg1,arg2 ,输出结果如下:

hello dart!
[arg1,arg2]

一般情况下,使用省略方式即可。

在 Dart 中,print 方法不支持多参数打印,其函数原型如下:

void print(Object object);

只支持单参数打印,但是可以利用字符串插值的方式进行多参数打印,使用 $ 美元符号,比如打印 a, b两个变量,如下:

print("$a $b");

以上是利用字符串插值进行多参数打印,但实质上依然是单参数,只不过利用了字符串的插值特性输出多个参数而已。当然依然可以利用多个 print 打印多个参数。

2. 数据类型

Dart 中所有类型都是对象,基础数据类型也是对象,都有自己的方法和属性。Dart 支持自动类型推断,所以在定义数据类型的时候,可以通过两种方式进行定义,直接使用具体数据类型定义和使用 var 关键字进行定义。当使用 var 定义时,编译器会根据具体的赋值数据类型推断出定义的变量或常量的数据类型。

可以使用 runtimeType 来查看对象的运行时类型。

因为 Dart 中所有的类型都是对象,所以定义一个数据类型以后,如果未对其进行初始化操作,则其默认值为null

Dart 中,变量是区分大小写的。

**2.1.1 数值类型 (Numbers) **

Dart 中数值类型有两种,整数( int )和小数( double),他们继承自 num 类。使用直接数据类型定义方式如下:

main(){
  int a = 10;
  double b = 20.1;
  print(a);  //输出10
  print(b);  //输出20.1
  print(a+b);////输出30.1
}

也可以使用如下方式定义:

main(){
  var a = 10;
  var b = 20.1;
  print(a);
  print(b);
  print(a+b);
  print(a.runtimeType);  //输出int
}

输出结果与上面相同,下面的方式编译器会自动推断出具体的数据类型。

Dart 中可以使用十进制和十六进制进行整数的赋值,不能使用其他进制。

2.1.2 字符串类型 (Strings)

使用关键字 String 来声明字符串类型,字符串使用单引号或双引号包起来,Dart 中字符串是UTF-16编码,如下:

main(){
  String str1 = 'hello dart!';
  var str2 = "hello world!";
  print('$str1 $str2');  //输出 hello dart! hello world!
}

也可以在定义一个变量的同时直接赋值多个字符串,如下:

main(){  
  String str = 'hello dart! ' 'this is'
   " a string";
  print(str);  //输出 hello dart! this is a string
}

可以看到上面的代码,我们定义了一个变量,但是赋值了三个字符串,编译器会将三个字符串合并为一个字符串进行处理。但是虽然我们分两行书写的字符串,但是在输出时并没有分行,也就是定义与输出的格式并不一样,如果需要定义与输出一样,可以使用如下方式:

main(){  
  //1.通过转义字符\n进行换行操作
  String str = "hello dart! \nthis is a string ";
  print(str);

  //2.通过```或"""将字符串包裹起来为多行字符串,使用此方式时,需注意字符串的对齐方式,字符的前面的空格会包含在内,左边界为编译器的行头
  var str1 = '''hello dart! 
  this is a string''';
  print(str1);
}

输出结果如下:

hello dart! 
this is a string 
hello dart! 
  this is a string

可以使用加号和插值的方式进行拼接,如下:

main(){  
  String a = "hello";
  String b = " dart!";
  print(a + b);  //输出 hello dart!
  print("$a$b"); //输出 hello dart!
}

使用字符串插值的方式进行拼接时,如果 $ 后为一个标识符则可直接使用,如果为表达式则需要使用 {} 将表达式包含起来,如下:

main(){  
  String a = "hello";
  String b = " dart!";
  print(a + b);
  print("${a.runtimeType} ${b.runtimeType}"); //此处需要使用{}将表达式包含起来作为一个标识符使用
}

在 Dart 中,字符串也是一种集合类型,可以通过如下方式获取字符串中的单个字符:

main(){  
  String str = "hello dart!";
  print(str[0]);  //输出 h
  print(str[4]);  //输出 o
}

下标从0开始。

也可以通过 * 运算符对字符串进行拷贝操作,如下:

main(){  
  String str = "hello dart!";
  print(str*2);  //输出 hello dart!hello dart!
}

使用如下方式原样输出字符串,包括里面的转义字符:

main(){  
  var str = r"this is a \n string!";
  print(str);  //输出 this is a \n string!
}

2.1.3 布尔类型 (Booleans)

Dart 中,布尔类型只有两个字面量值:truefalse

main(){  
  bool a = true;
  var b = false;
  print("$a $b");  //输出 true false
  print(b.runtimeType);  //输出 bool
}

上面说过,Dart中所有的类型都是对象,如果在定义 bool 类型时,没有进行初始化,则其默认值为 null,在进行条件判断时会报异常,如下:

main(){  
 bool c;
 if(c) {   
   print(c);
 }
}

上面的代码编译不会报错,运行时会报如下异常:

Unhandled exception:
Failed assertion: boolean expression must not be null

所以在使用 bool 类型进行条件判断时,如果有为空的可能应先进行是否为 null 的判断,并且 Dart 中的条件判断必须使用 bool 类型,其他类型会编译错误。

2.1.4 集合类型

列表 (Lists)

列表用来存放一组数据,在很多其他编程语言中,列表也被称为数组,所以与其他语言一样,下标从0开始,最后一个数据元素的下标为 lenght-1 。列表的具体数据类型由其中的元素类型决定,使用方式如下:

main(){  
  var arr  = [1, 2, 3];
  var arr1 = [1, 2, 3, "string"];
  List arr2 = [1, 2, 3];
  List arr3 = [1, 2, 3, "string"];

  print("$arr \t\t\t ${arr.runtimeType}");
  print("$arr1 \t ${arr1.runtimeType}");
  print("$arr2 \t\t\t ${arr2.runtimeType}");
  print("$arr3 \t ${arr3.runtimeType}");
}

输出结果如下:

[1, 2, 3]                    List<int>
[1, 2, 3, string]    List<Object>
[1, 2, 3]                    List<dynamic>
[1, 2, 3, string]    List<dynamic>

以上方式直接创建 List 并进行初始化操作,定义空列表,使用 var arr = []; 即可。需要注意如果不进行初始化,其数据类型为 null ,即便使用 List arr; 定义,其 runtimeType 类型也为 Null

也可使用构造方法创建数组,如下:

main(){  
  var arr = new List();   //创建空列表
  var arr1 = new List(3); //创建长度为3的列表,使用null填充
  var arr2 = new List.filled(3, 1); //创建长度为3的列表,并使用整形数据1填充
  var arr3 = new List.filled(3, "a"); //创建长度为3的列表,并使用字符串a填充

  print("$arr \t ${arr.runtimeType}");
  print("$arr1 \t ${arr1.runtimeType}");
  print("$arr2 \t ${arr2.runtimeType}");
  print("$arr3 \t ${arr3.runtimeType}");
}

输出如下:

[]                                      List<dynamic>
[null, null, null]    List<dynamic>
[1, 1, 1]                     List<int>
[a, a, a]                     List<String>

在 Dart 中,可是省略 new 关键字,如下:

main(){  
  var arr = List(2);
  arr[0] = 10;
  arr[1] = "str";
  print("$arr \t ${arr.runtimeType}");   //输出 [10, str]      List<dynamic>
  print(arr.length);  //输出 2
}

以上创建的列表都是可存放任意类型的数据的,如果想存放固定类型的数据,可以使用泛型进行数据类型约束,使用方式如下:

main(){  
  List<String> arr = ["str1", "str2"];
  print(arr);  //输出 [str1, str2]

  var arr1 = List<int>();
  arr1.add(1);
  arr1.add(2);
  print(arr1);  //输出 [1, 2]
}

创建固定数据类型的列表,如果添加其他数据类型数据编译器会报错。

集合 (Sets)

SetList 类似,但是 Set 为无序列表,所以不能通过下标的方式进行访问。 Set 中不能有重复数据,如果存在重复数据,只会保留一份数据。依靠此特性,一般使用 Set 来对列表进行去重处理。

main(){  
  Set set = {1, 2, 3, 4, 2};
  print(set);   //输出 {1, 2, 3, 4}

  var set1 = {"str1", "str2", "str1", 1, 2, 3, 2};
  print(set1);  //输出 {str1, str2, 1, 2, 3}

  var set2 = Set();
  set2.add(1);
  set2.add(2);
  set2.add(2);
  set2.add("str");
  print(set2);  //输出 {1, 2, str}

  Set<int> set3 = Set();
  set3.add(1);
  // set3.add("str");  //错误
  print(set3);  //输出 {1}

  Set set4 = Set<String>();
  print(set4);  //输出 {}
}

数组去重处理,如下:

main(){  
  var arr = [1, 2, 3, 4, 5, 6, 3, 4];
  Set set = Set.from(arr);
  var arr1 = List.from(set);
  print(arr1);  //输出 [1, 2, 3, 4, 5, 6]
}

字典 (Maps)

字典是一组键值对的集合,可以通过键完成对值的修改、查找、添加或删除操作,其中,键( key ) 必须是唯一的。创建 Map 类型,键 (key) 与值 (value) 要成对出现。一般情况下,键都是字符串类型,但是 Dart 中并没有严格约束键的数据类型,所以键可以为任意类型,值也可以为任意类型。如果需要创建固定类型的键值,可通过泛型进行约束。

main(){  
  var map1 = {
    "name" : "zhangsan",
    "age" : 20
  };

  var map2 = {
    1 : 20,
    2 : 30.2
  };

  Map map3 = {
    "name" : "lisi",
    1 : [1, 2, 3]
  };

  Map map4 = new Map();

  Map map5 = Map.from(map1);

  Map<String, int> map6 = {
    "number" : 1,
    "age" : 20
  };
  
  Map map7 = Map<String, dynamic>();
  map7["name"] = "wanger";
  map7["age"] = 20;

  print(map1);  //输出 {name: zhangsan, age: 20}
  print(map2);  //输出 {1: 20, 2: 30.2}
  print(map3);  //输出 {name: lisi, 1: [1, 2, 3]}
  print(map4);  //输出 {}
  print(map5);  //输出 {name: zhangsan, age: 20}
  print(map6);  //输出 {number: 1, age: 20}
  print(map7);  //输出 {name: wanger, age: 20}
}

2.1.5 泛型

泛型,即通用类型,使 Dart 中的类型更加动态,提高了代码的重用率。Dart 中使用 <T> 的方式来定义泛型,T 为标识符或关键字,一般用 T 表示。上面的例子中也使用了泛型来约束数据类型,例如列表:

main(){  
  var arr = List<String>();
  arr.addAll(["str1", "str2", "str3"]);
  print(arr);  //输出 [str1, str2, str3]
}

2.1.6 符文 (Runes)

符文(runes)是字符串的UTF-32编码点,要在字符串中表示32位的Unicode值,可使用 \uxxxx 形式,xxxx 为四位十六进制值,如多于或少于四位,则需要使用 {} 包装。

main(){  
  var value = '\u{1F362}';
  print(value);  //输出 
}

2.1.7 符号类型 (Symbols)

Symbol 表示运算符或标识符,使用相同名称创建的符号是相等的,有如下两种方式创建符号:

main(){  
  var symb = Symbol("s");  //通过构造方法创建符号
  var symb1 = #s;                //通过#创建符号
  print(symb.runtimeType);  //输出 Symbol
  print(symb1.runtimeType); //输出 Symbol

  if(symb == symb1) {
    print("相同");
  }
}

2.1.8 常量

以上声明的变量都是可变量,也就是变量,有时候需要一些数据是不可变的,称为常量。在 Dart 中,定义常量可以通过关键字 constfinal 来定义,如下:

main(){
  const a = 10;  //声明具体数据类型和var关键字
  final String b = 'hello dart!';
  print("$a $b");  //输出 hello world! hello dart!
  print("${a.runtimeType} ${b.runtimeType}");!  //输出 String String
}

可以看出,在声明常量时,可以省略其数据类型,编译器会自动进行推断。无论使用 const 还是 final 声明的常量都是不可以改变的,并且在定义常量时,在定义的同时就需要对其进行初始化操作,否则会报错。如果试图对常量进行重新复制,也会报错。

上面的代码中,使用 constfinal 声明的常量,其输出的最终数据类型都为 String ,那么具体他们之间的区别是什么呢?

const 定义的常量在定义时必须赋值为一个常量值,不可以通过表达式或函数进行赋值操作(常量表达式可以),而 final 定义的常量则可以通过表达式或函数赋值,也就是说 const 常量为编译时常量,在编译阶段就必须是可知的,而 final 常量是只能对其进行一次赋值,但是可以在运行时进行设置。 如下:

main(){  
  const a = 10;
  final b = 20.5;
  const c = a ;
  final d = a;
  // const e = a+b;  //错误
  final f = a + b;
    const g = a * 10;  //正确,因为a为常量,常量表达式在编译期就可以确定值
  
  print(a);  //输出 10
  print(b);  //输出 20.5
  print(c);  //输出 10
  print(d);  //输出 10
  print(f);  //输出30.5
  print(g);  //输出 100
}

const 还可以用来创建常量值以及声明创建常量值的构造函数(后期文章会介绍到),创建常量值如下:

main(){  
  List a = const [1, 2, 3];
  List b = const [1, 2, 3];

  if(a == b) {
    print("相同");   //会输出 相同
  }
}

如果不用 const 修饰,则a与b为不同,如下:

main(){  
  List a = [1, 2, 3];
  List b = [1, 2, 3];

  if(a == b) {
    print("相同");
  }else print("不同");  //输出 不同
}

并且 使用如下方式声明创建的常量值是可以改变的:

main(){  
  List a = const [1, 2, 3]; //可改变
  const b = [1, 2, 3]; //不可改变

  a = [4, 5, 6];
  print(a);
}

但是对于 const [1, 2, 3] 中的单个数据元素是不可修改的,修改会抛出异常。对于 a 变量来说是可变的,因为未使用 const 修饰,所以对 a 重新赋值 [4, 5, 6] 以后,a 对象相当于如下定义:

var a = [4, 5, 6];

通过以下代码验证:

main(){  
  var a = const [1, 2, 3];
  var b = const [4, 5, 6];
  a = [4, 5, 6];
  if(a == b) {
    print("相同");
  }else{
    print("不同");   //输出不同
  }
}

以下代码为对 const [1, 2, 3] 单个元素修改抛出异常的示例:

main(){  
  var a = const [1, 2, 3];
  a[0] = 10;   //错误,不可修改
}

2.1.9 动态数据类型

在确定数据类型以后再赋值其他数据类型则会报错,如下:

main(){
  var a = 10;
  a = 20.1;   //错误
}

上面的代码会报错如下错误:

A value of type 'double' can't be assigned to a variable of type 'int'.
Try changing the type of the variable, or casting the right-hand type to 'int'.dart(invalid_assignment)

如果希望变量在赋值一个数据类型以后再赋值其他数据类型,可以使用关键字 dynamic 定义,如下:

main(){
  dynamic a = 10;
  print(a.runtimeType);  //输出int
  a = 20.1;
  print(a);  //输出20.1
  print(a.runtimeType);  //输出double
}

dynamic 的最终数据类型为最后赋值的数据的类型。

2.2.0 枚举 (enum)

enum Week{
  Monday,
  Tuesday,
  Wednesday
}

main(){  
  var week = Week.Tuesday;
  if(week == Week.Monday){
    print("星期一");
  }else if(week == Week.Tuesday) {
    print("星期二");
  }else if(week == Week.Wednesday) {
    print("星期三");
  }
  
  print(Week.values); //输出所有枚举值
  print(Week.Monday.index);  //输出索引值
  print(Week.Tuesday.index);
  print(Week.Wednesday.index);
}

输出结果:

星期二
[Week.Monday, Week.Tuesday, Week.Wednesday]
0
1
2
3. 类型转换
main(){  
  var a = 10;
  var b = 20.5;
  var c = a + b.toInt();  //浮点型转整型
  var d = a.toDouble() + b;  //整型转浮点型
  String e = b.toString();  //浮点型转字符串
  double f = double.parse(e);  //字符串转浮点,使用此方法应确定字符串可转换为浮点型,否则会报异常,如不确定是否可以转换应使用tryParse方法,无法转换会返回null

  print("$c $d $e $f");
}
4. Objectdynamic 的区别

因为在 Dart 中,所有内容都是从 Object 类扩展而来,所以可以使用如下方式定义数据:

Object a = 10;

dynamic 对比,他们都可以定义一种数据类型以后再次赋值其他数据类型,如下:

main(){  
  Object a = 10;
  a = "string";
  print(a);  //输出 string

  dynamic b = 20;
  b = "string"; //输出 string
  print(b);
}

但是 Object 定义的对象在调用方法或属性是,可以在编译时就判断是否存在指定的方法或属性,如不存在,编译阶段就会报错。而 dynamic 动态类型,调用不存在的方法或属性,在编译阶段不会报错,运行时才会抛出异常,如下:

main(){  
  Object a = 10;
  print(a);

  dynamic b = 20;
  print(b);

  // a.run; 调用不存在的属性run,编译时就会提示错误
  b.run;  //编译时不会提示错误,运行时才会抛出异常
}

PS:以上只是很小一部分内容,因为一切皆对象,所以每个类都有自己的很多属性与方法,具体可以参看代码文档。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容