一、概述
Dart 是 Google 公司在2011年10月發(fā)布的一種編程語言,主要是將其作為一種結(jié)構(gòu)化的 Web 開發(fā)語言。可以用在Web、服務(wù)器、移動應(yīng)用和物聯(lián)網(wǎng)等領(lǐng)域的開發(fā),是寬松開源許可證(修改的BSD證書)下的開源軟件。官方網(wǎng)站:https://dart.dev/ 。
在 Dart 中,所有東西都是對象,變量,數(shù)字,函數(shù)等都是對象類型,都是類的實例,所有對象都繼承自 Object
類。
在進(jìn)行 Dart 開發(fā)前,需要安裝 Dart SDK,安裝方法官網(wǎng)網(wǎng)站有介紹,地址為:https://dart.dev/get-dart 。如直接進(jìn)行 Flutter 開發(fā),也可不用下載此 SDK ,直接安裝 Flutter 即可。Flutter開發(fā)工具包中包含 Dart。
二、語法基礎(chǔ)
1. 入口方法
Dart 語言文件格式為 .dart
,并以 main
方法作為程序的入口點,我們使用 VSCode 創(chuàng)建 demo1.dart
文件,代碼如下:
main(){
print('hello dart!'); //輸出結(jié)果:hello dart!
}
Dart 語言有很多簡寫形式,以上的 main
方法就是簡寫,完整的寫法如下:
void main(List<String> args){
print('hello dart!');
}
參數(shù)為泛型的寫法,List
為列表,也就是其他語言中的數(shù)組。 main
方法可以接收由命令行輸入的參數(shù),可以通過 args
參數(shù)在程序中獲取。如下代碼:
void main(List<String> args){
print('hello dart!');
print(args);
}
通過命令行的方式運行 dart
文件,首先 cd
到目標(biāo)文件夾,運行如下命令: dart demo1.dart arg1,arg2
,輸出結(jié)果如下:
hello dart!
[arg1,arg2]
一般情況下,使用省略方式即可。
在 Dart 中,print
方法不支持多參數(shù)打印,其函數(shù)原型如下:
void print(Object object);
只支持單參數(shù)打印,但是可以利用字符串插值的方式進(jìn)行多參數(shù)打印,使用 $
美元符號,比如打印 a, b兩個變量,如下:
print("$a $b");
以上是利用字符串插值進(jìn)行多參數(shù)打印,但實質(zhì)上依然是單參數(shù),只不過利用了字符串的插值特性輸出多個參數(shù)而已。當(dāng)然依然可以利用多個 print
打印多個參數(shù)。
2. 數(shù)據(jù)類型
Dart 中所有類型都是對象,基礎(chǔ)數(shù)據(jù)類型也是對象,都有自己的方法和屬性。Dart 支持自動類型推斷,所以在定義數(shù)據(jù)類型的時候,可以通過兩種方式進(jìn)行定義,直接使用具體數(shù)據(jù)類型定義和使用 var
關(guān)鍵字進(jìn)行定義。當(dāng)使用 var
定義時,編譯器會根據(jù)具體的賦值數(shù)據(jù)類型推斷出定義的變量或常量的數(shù)據(jù)類型。
可以使用 runtimeType
來查看對象的運行時類型。
因為 Dart 中所有的類型都是對象,所以定義一個數(shù)據(jù)類型以后,如果未對其進(jìn)行初始化操作,則其默認(rèn)值為null
。
Dart 中,變量是區(qū)分大小寫的。
**2.1.1 數(shù)值類型 (Numbers) **
Dart 中數(shù)值類型有兩種,整數(shù)( int
)和小數(shù)( double
),他們繼承自 num
類。使用直接數(shù)據(jù)類型定義方式如下:
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
}
輸出結(jié)果與上面相同,下面的方式編譯器會自動推斷出具體的數(shù)據(jù)類型。
Dart 中可以使用十進(jìn)制和十六進(jìn)制進(jìn)行整數(shù)的賦值,不能使用其他進(jìn)制。
2.1.2 字符串類型 (Strings)
使用關(guān)鍵字 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
}
可以看到上面的代碼,我們定義了一個變量,但是賦值了三個字符串,編譯器會將三個字符串合并為一個字符串進(jìn)行處理。但是雖然我們分兩行書寫的字符串,但是在輸出時并沒有分行,也就是定義與輸出的格式并不一樣,如果需要定義與輸出一樣,可以使用如下方式:
main(){
//1.通過轉(zhuǎn)義字符\n進(jìn)行換行操作
String str = "hello dart! \nthis is a string ";
print(str);
//2.通過```或"""將字符串包裹起來為多行字符串,使用此方式時,需注意字符串的對齊方式,字符的前面的空格會包含在內(nèi),左邊界為編譯器的行頭
var str1 = '''hello dart!
this is a string''';
print(str1);
}
輸出結(jié)果如下:
hello dart!
this is a string
hello dart!
this is a string
可以使用加號和插值的方式進(jìn)行拼接,如下:
main(){
String a = "hello";
String b = " dart!";
print(a + b); //輸出 hello dart!
print("$a$b"); //輸出 hello dart!
}
使用字符串插值的方式進(jìn)行拼接時,如果 $
后為一個標(biāo)識符則可直接使用,如果為表達(dá)式則需要使用 {}
將表達(dá)式包含起來,如下:
main(){
String a = "hello";
String b = " dart!";
print(a + b);
print("${a.runtimeType} ${b.runtimeType}"); //此處需要使用{}將表達(dá)式包含起來作為一個標(biāo)識符使用
}
在 Dart 中,字符串也是一種集合類型,可以通過如下方式獲取字符串中的單個字符:
main(){
String str = "hello dart!";
print(str[0]); //輸出 h
print(str[4]); //輸出 o
}
下標(biāo)從0開始。
也可以通過 *
運算符對字符串進(jìn)行拷貝操作,如下:
main(){
String str = "hello dart!";
print(str*2); //輸出 hello dart!hello dart!
}
使用如下方式原樣輸出字符串,包括里面的轉(zhuǎn)義字符:
main(){
var str = r"this is a \n string!";
print(str); //輸出 this is a \n string!
}
2.1.3 布爾類型 (Booleans)
Dart 中,布爾類型只有兩個字面量值:true
和 false
。
main(){
bool a = true;
var b = false;
print("$a $b"); //輸出 true false
print(b.runtimeType); //輸出 bool
}
上面說過,Dart中所有的類型都是對象,如果在定義 bool
類型時,沒有進(jìn)行初始化,則其默認(rèn)值為 null
,在進(jìn)行條件判斷時會報異常,如下:
main(){
bool c;
if(c) {
print(c);
}
}
上面的代碼編譯不會報錯,運行時會報如下異常:
Unhandled exception:
Failed assertion: boolean expression must not be null
所以在使用 bool
類型進(jìn)行條件判斷時,如果有為空的可能應(yīng)先進(jìn)行是否為 null
的判斷,并且 Dart 中的條件判斷必須使用 bool
類型,其他類型會編譯錯誤。
2.1.4 集合類型
列表 (Lists)
列表用來存放一組數(shù)據(jù),在很多其他編程語言中,列表也被稱為數(shù)組,所以與其他語言一樣,下標(biāo)從0開始,最后一個數(shù)據(jù)元素的下標(biāo)為 lenght-1
。列表的具體數(shù)據(jù)類型由其中的元素類型決定,使用方式如下:
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}");
}
輸出結(jié)果如下:
[1, 2, 3] List<int>
[1, 2, 3, string] List<Object>
[1, 2, 3] List<dynamic>
[1, 2, 3, string] List<dynamic>
以上方式直接創(chuàng)建 List
并進(jìn)行初始化操作,定義空列表,使用 var arr = [];
即可。需要注意如果不進(jìn)行初始化,其數(shù)據(jù)類型為 null
,即便使用 List arr;
定義,其 runtimeType
類型也為 Null
。
也可使用構(gòu)造方法創(chuàng)建數(shù)組,如下:
main(){
var arr = new List(); //創(chuàng)建空列表
var arr1 = new List(3); //創(chuàng)建長度為3的列表,使用null填充
var arr2 = new List.filled(3, 1); //創(chuàng)建長度為3的列表,并使用整形數(shù)據(jù)1填充
var arr3 = new List.filled(3, "a"); //創(chuàng)建長度為3的列表,并使用字符串a(chǎn)填充
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
關(guān)鍵字,如下:
main(){
var arr = List(2);
arr[0] = 10;
arr[1] = "str";
print("$arr \t ${arr.runtimeType}"); //輸出 [10, str] List<dynamic>
print(arr.length); //輸出 2
}
以上創(chuàng)建的列表都是可存放任意類型的數(shù)據(jù)的,如果想存放固定類型的數(shù)據(jù),可以使用泛型進(jìn)行數(shù)據(jù)類型約束,使用方式如下:
main(){
List<String> arr = ["str1", "str2"];
print(arr); //輸出 [str1, str2]
var arr1 = List<int>();
arr1.add(1);
arr1.add(2);
print(arr1); //輸出 [1, 2]
}
創(chuàng)建固定數(shù)據(jù)類型的列表,如果添加其他數(shù)據(jù)類型數(shù)據(jù)編譯器會報錯。
集合 (Sets)
Set
與 List
類似,但是 Set
為無序列表,所以不能通過下標(biāo)的方式進(jìn)行訪問。 Set
中不能有重復(fù)數(shù)據(jù),如果存在重復(fù)數(shù)據(jù),只會保留一份數(shù)據(jù)。依靠此特性,一般使用 Set
來對列表進(jìn)行去重處理。
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); //輸出 {}
}
數(shù)組去重處理,如下:
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 ) 必須是唯一的。創(chuàng)建 Map 類型,鍵 (key) 與值 (value) 要成對出現(xiàn)。一般情況下,鍵都是字符串類型,但是 Dart 中并沒有嚴(yán)格約束鍵的數(shù)據(jù)類型,所以鍵可以為任意類型,值也可以為任意類型。如果需要創(chuàng)建固定類型的鍵值,可通過泛型進(jìn)行約束。
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 中的類型更加動態(tài),提高了代碼的重用率。Dart 中使用 <T>
的方式來定義泛型,T
為標(biāo)識符或關(guān)鍵字,一般用 T
表示。上面的例子中也使用了泛型來約束數(shù)據(jù)類型,例如列表:
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 為四位十六進(jìn)制值,如多于或少于四位,則需要使用 {}
包裝。
main(){
var value = '\u{1F362}';
print(value); //輸出
}
2.1.7 符號類型 (Symbols)
Symbol
表示運算符或標(biāo)識符,使用相同名稱創(chuàng)建的符號是相等的,有如下兩種方式創(chuàng)建符號:
main(){
var symb = Symbol("s"); //通過構(gòu)造方法創(chuàng)建符號
var symb1 = #s; //通過#創(chuàng)建符號
print(symb.runtimeType); //輸出 Symbol
print(symb1.runtimeType); //輸出 Symbol
if(symb == symb1) {
print("相同");
}
}
2.1.8 常量
以上聲明的變量都是可變量,也就是變量,有時候需要一些數(shù)據(jù)是不可變的,稱為常量。在 Dart 中,定義常量可以通過關(guān)鍵字 const
或 final
來定義,如下:
main(){
const a = 10; //聲明具體數(shù)據(jù)類型和var關(guān)鍵字
final String b = 'hello dart!';
print("$a $b"); //輸出 hello world! hello dart!
print("${a.runtimeType} ${b.runtimeType}");! //輸出 String String
}
可以看出,在聲明常量時,可以省略其數(shù)據(jù)類型,編譯器會自動進(jìn)行推斷。無論使用 const
還是 final
聲明的常量都是不可以改變的,并且在定義常量時,在定義的同時就需要對其進(jìn)行初始化操作,否則會報錯。如果試圖對常量進(jìn)行重新復(fù)制,也會報錯。
上面的代碼中,使用 const
和 final
聲明的常量,其輸出的最終數(shù)據(jù)類型都為 String
,那么具體他們之間的區(qū)別是什么呢?
const
定義的常量在定義時必須賦值為一個常量值,不可以通過表達(dá)式或函數(shù)進(jìn)行賦值操作(常量表達(dá)式可以),而 final
定義的常量則可以通過表達(dá)式或函數(shù)賦值,也就是說 const
常量為編譯時常量,在編譯階段就必須是可知的,而 final
常量是只能對其進(jìn)行一次賦值,但是可以在運行時進(jìn)行設(shè)置。 如下:
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為常量,常量表達(dá)式在編譯期就可以確定值
print(a); //輸出 10
print(b); //輸出 20.5
print(c); //輸出 10
print(d); //輸出 10
print(f); //輸出30.5
print(g); //輸出 100
}
const
還可以用來創(chuàng)建常量值以及聲明創(chuàng)建常量值的構(gòu)造函數(shù)(后期文章會介紹到),創(chuàng)建常量值如下:
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("不同"); //輸出 不同
}
并且 使用如下方式聲明創(chuàng)建的常量值是可以改變的:
main(){
List a = const [1, 2, 3]; //可改變
const b = [1, 2, 3]; //不可改變
a = [4, 5, 6];
print(a);
}
但是對于 const [1, 2, 3]
中的單個數(shù)據(jù)元素是不可修改的,修改會拋出異常。對于 a
變量來說是可變的,因為未使用 const
修飾,所以對 a
重新賦值 [4, 5, 6]
以后,a
對象相當(dāng)于如下定義:
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 動態(tài)數(shù)據(jù)類型
在確定數(shù)據(jù)類型以后再賦值其他數(shù)據(jù)類型則會報錯,如下:
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)
如果希望變量在賦值一個數(shù)據(jù)類型以后再賦值其他數(shù)據(jù)類型,可以使用關(guān)鍵字 dynamic
定義,如下:
main(){
dynamic a = 10;
print(a.runtimeType); //輸出int
a = 20.1;
print(a); //輸出20.1
print(a.runtimeType); //輸出double
}
dynamic
的最終數(shù)據(jù)類型為最后賦值的數(shù)據(jù)的類型。
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);
}
輸出結(jié)果:
星期二
[Week.Monday, Week.Tuesday, Week.Wednesday]
0
1
2
3. 類型轉(zhuǎn)換
main(){
var a = 10;
var b = 20.5;
var c = a + b.toInt(); //浮點型轉(zhuǎn)整型
var d = a.toDouble() + b; //整型轉(zhuǎn)浮點型
String e = b.toString(); //浮點型轉(zhuǎn)字符串
double f = double.parse(e); //字符串轉(zhuǎn)浮點,使用此方法應(yīng)確定字符串可轉(zhuǎn)換為浮點型,否則會報異常,如不確定是否可以轉(zhuǎn)換應(yīng)使用tryParse方法,無法轉(zhuǎn)換會返回null
print("$c $d $e $f");
}
4. Object
與 dynamic
的區(qū)別
因為在 Dart 中,所有內(nèi)容都是從 Object
類擴(kuò)展而來,所以可以使用如下方式定義數(shù)據(jù):
Object a = 10;
與 dynamic
對比,他們都可以定義一種數(shù)據(jù)類型以后再次賦值其他數(shù)據(jù)類型,如下:
main(){
Object a = 10;
a = "string";
print(a); //輸出 string
dynamic b = 20;
b = "string"; //輸出 string
print(b);
}
但是 Object
定義的對象在調(diào)用方法或?qū)傩允牵梢栽诰幾g時就判斷是否存在指定的方法或?qū)傩裕绮淮嬖冢幾g階段就會報錯。而 dynamic
動態(tài)類型,調(diào)用不存在的方法或?qū)傩裕诰幾g階段不會報錯,運行時才會拋出異常,如下:
main(){
Object a = 10;
print(a);
dynamic b = 20;
print(b);
// a.run; 調(diào)用不存在的屬性run,編譯時就會提示錯誤
b.run; //編譯時不會提示錯誤,運行時才會拋出異常
}
PS:以上只是很小一部分內(nèi)容,因為一切皆對象,所以每個類都有自己的很多屬性與方法,具體可以參看代碼文檔。