一、Dart中的變量
1.1、變量
var :解釋器自動推斷變量的類型。注意:再次賦值的類型不一樣會報錯
dynamic:變量類型可動態改變
final:最終變量,不可變的變量。在聲明時,需要對變量進行賦值
const:常量,不可變的變量。在聲明時,需要對變量進行賦值
注意:無論什么類型的變量,如果不對其進行賦值,那么它的默認值都是 null。
// 以下代碼會報錯:
var name = "變量";
name = 1;
// 以下代碼不會報錯
dynamic age = 26;
age = "26";
進行推斷
1.2 變量的數據類型
Dart 中內置了 7 類特殊的數據類型:
numbers:數值類型
strings:字符串類型
booleans:布爾類型
lists:列表類型
maps:圖類型
runes:字符類型
symbols:符號類型
1.2.1 數值類型
在 Dart 中,數值類型有兩種,分別為 int 和 double。
如果使用 0x 開頭定義數值,就表示使用十六進制數值。
如果一個數值包含小數,就需要將其定義為 double 類型。
e 符號表示科學計數法。
整型數值是可以直接賦值給浮點型變量的。
int a = 99;
int b = 0xA1;
print(a);//99
print(b);//161
double c = 3.14;
double d = 1.4e2;
print(c);//3.14
print(d);//140.0
double e = 1;
print(e);//1.0
數值類型常用屬性
var count1 = 1;
var count2 = 1.1;
//runtimeType 屬性獲取運行時類型
print(count1.runtimeType);//int
print(count2.runtimeType);//double
//獲取當前數值是否為有限值,返回 true 或者 false
print(count1.isFinite);
//獲取當前數值是否為無限值,返回 true 或者 false
print(count1.isInfinite);
//獲取當前數值是否為 NaN,NaN 描述非數值
print(count1.isNaN);
//獲取當前數值是否為負數
print(count1.isNegative);
//獲取當前數值的符號。若返回 1,則表示為正數;若返回-1,則表示為負數;若返回 0,則表示當前數值為 0
print(count1.sign);
//獲取存儲當前數值需要的最少位數,int 類型獨有的屬性
print(count1.bitLength);
//獲取當前數值是否為偶數,int 類型獨有的屬性
print(count1.isEven);
//獲取當前數值是否為奇數,int 類型獨有的屬性
print(count1.isOdd);
數值類型常用方法
var count1 = -1;
var count2 = 1.1;
//返回當前數值的絕對值
print(count1.abs());
//返回不小于當前數值的最小整數
print(count2.ceil());//2
//返回不小于當前數值的最小整數,返回數值為浮點類型
print(count2.ceilToDouble());//2.0
//返回指定范圍內離當前數值最近的數值,如果當前數值在范圍內,就直接返回
print(count1.clamp(1,10));
//將當前數值與傳入的參數進行比較:如果大于傳入的參數,就返回 1;如果小于傳入的參數,就返回-1;如果等于傳入的參數,就返回 0
print(count1.compareTo(0));
//返回不大于當前數值的最大整數
print(count2.floor());//1
//返回不大于當前數值的最大整數,返回數值為浮點類型
print(count2.floorToDouble());//1.0
//獲取當前數值除以參數后的余數
print(count1.remainder(5));
//獲取離當前數值最近的整數,四舍五入
print(count2.round());
//獲取離當前數值最近的整數,四舍五入,返回浮點數
print(count2.roundToDouble());
//將當前數值轉換成浮點數返回
print(count1.toDouble());
//將當前數值轉換成整型數返回
print(count2.toInt());
//將當前數值轉換成字符串返回
print(count1.toString());
//將當前數值的小數部分丟棄后返回整數值
print(count2.truncate());
//將當前數值的小數部分丟棄后返回整數值,浮點類型
print(count2.truncateToDouble());
int類型獨有的方法
var num1 = 35;
//獲取當前數與傳入參數的最大公約數
print(num1.gcd(7));//7
//求模逆運算
print(num1.modInverse(6));
num1 = 2;
//對當前數值進行冪運算,之后進行取模運算
print(num1.modPow(3,5));//2 的 3 次方除以 5 取余數
1.2.2 字符串類型
可以使用單引號或者雙引號來創建字符串
$來進行字符串的格式化
字符串也支持直接使用“+”運算符來進行拼接
即使不使用運算符,相鄰的字符串也會自行進行拼接
使用 3 對單引號或者 3 對雙引號可以進行多行字符串的創建
反斜杠進行字符轉譯
var name = "變量";
var str3 = "Hello ${name} $name";
var num1 = 3;
var num2 = 4;
var str4 = "3+4=${num1+num2}";
print(str3);//Hello 變量
print(str4);//3+4=7
// “+”運算符來進行拼接
var str1 = "Hello";
var str2 = 'World';
print(str1+str2);//HelloWorld
// 相鄰的字符串自行進行拼接
print('hello''world');//helloworld
// 3 對單引號或者 3 對雙引號創建多行字符串
var str5 = '''第一行
第二行
第三行''';
var str6 = """第一行
第二行
第三行""";
print('hello \'變量\'');//hello '變量'
常用屬性和方法
//獲取字符串的字符碼集合
print(str8.codeUnits);//[97, 98, 99]
//獲取當前字符串是否為空字符串,返回布爾值
print("".isEmpty);//true
//獲取當前字符串是否為非空,返回布爾值
print("".isNotEmpty);//false
//獲取當前字符串長度
print(str8.length);//3
//獲取類型
print(str8.runtimeType);//String
//通過下標獲取某個字符串中某個字符的 code 碼,下標從 0 開始
print("hello".codeUnitAt(0));//104
//進行字符串比較,逐個字符進行 code 碼的比較
print("hello".compareTo('a'));//1
//獲取當前字符串是否包含參數字符串
print("hello".contains('l'));//true
//判斷當前字符串是否以某個字符串結尾
print("hello".endsWith("llo"));//true
//判斷當前字符串是否以某個字符串開頭
print("hello".startsWith('h'));//true
//獲取要進行匹配的字符串在當前字符串中的位置,如果沒找到,就返回-1
print("hello".indexOf("l"));//2
//獲取要進行匹配的字符串在當前字符串中的位置,逆向查找,如果沒找到,就返回-1
print("hello".lastIndexOf("l"));//3
//在左邊進行字符串位數補齊
print("hello".padLeft(10,"*"));//*****hello
//在右邊進行字符串位數補齊
print("hello".padRight(10,"&"));//hello&&&&&
//進行字符串替換,將匹配到的字符串替換成指定的字符串
print("hello".replaceAll("o","p"));//hellp
//將指定范圍內的字符串進行替換,左閉右開區間
print("hello".replaceRange(0,3,"000"));//000lo
//使用指定字符串作為標記對原字符串進行分割,結果會放進列表返回
print("hello".split('e'));//[h, llo]
//進行字符串截取,左閉右開區間
print("hello".substring(1,3));//el
//將字符串全部轉為小寫
print("Hello".toLowerCase());//hello
//將字符串全部轉為大寫
print("hello".toUpperCase());//HELLO
//將字符串首尾的空格去掉
print(" hello ".trim());
//將字符串首部的空格去掉
print(" hello".trimLeft());
//將字符串尾部的空格去掉
print("hello ".trimRight());
// 對字符串進行拷貝,使用*運算符
print("hello"*2);//hellohello
// 使用中括號來獲取集合內的某個元素
print("hello"[0]);//h
1.2.3 布爾類型
一種簡單的數據類型,其只有兩個字面量值:true 和 false。
使用 runtimeType 來獲取類型
調用 toString 方法來將布爾值轉換成字符串
bool a = true;
bool b = false;
print(a.runtimeType);//bool
bool a = true;
print(a.toString());//true
1.2.4 列表類型
列表用來存放一組數據,也被稱為數組。在 Dart 中,列表具體的數據類型由其中的元素類型決定。
想在列表中存放不同類型的數據,則可以將列表聲明成動態類型的。
列表類型可以通過構造方法來創建
下標超出了列表元素的個數,就會產生溢出異常。
List<int> list = [1,2,3,4];
List<dynamic> list = [1,2,3,4,"5"];
//創建長度為 5 的列表,默認使用 null 填充
var list2 = new List(5);//new 可以省略 [null, null, null, null, null]
//創建指定長度的列表,并使用指定的值作為默認值
var list3 = List.filled(3,1);//[1,1,1]
//通過另一個集合類型的數據來創建列表
var list4 = List.from(list3);//[1,1,1]
//獲取列表的第一個元素
print([1,2].first);//1
//獲取列表中的最后一個元素
print([1,2].last);//2
//獲取列表的長度
print([1,2].length);//2
print(["a","b","c","d"][3]);//d
print([1,2]+[2,3]);//[1, 2, 2, 3]
var data = [1,2,3];
data[2] = 4;
print(data);//[1, 2, 4]
List 類中封裝了大量的實例方法,這些方法可以極大地提高開發者的工作效率
var l = [];
//向列表中增加元素
l.add(1);
//向列表中增加一組元素
l.addAll([2,3]);
//將列表對象映射成字典對象,下標為鍵,元素為值
print(l.asMap());//{0: 1, 1: 2, 2: 3}
//將列表中某個范圍的元素進行覆蓋
l.fillRange(0,2,"a");//[a, a, 3]
//獲取列表某個范圍內的元素集合
print(l.getRange(0,3));
//獲取列表中某個元素的下標,從前向后找,如果沒有,就返回-1
print(l.indexOf('a'));
//獲取列表中某個元素的下標,從后向前找,如果沒有,就返回-1
print(l.lastIndexOf("a"));
//向列表中的指定位置插入一個元素
l.insert(0,'s');//[s, a, a, 3]
//向列表的指定位置插入一組元素
l.insertAll(0,["a","b","c"]);//[a, b, c, s, a, a, 3]
//刪除列表中的指定元素,從前向后找到第一個刪除
l.remove("a");
//刪除列表中指定位置的一個元素
l.removeAt(0);
//刪除列表中的最后一個元素
l.removeLast();
//刪除列表中指定范圍內的元素
l.removeRange(0,2);
//將列表中指定范圍的元素進行替換,替換為集合參數中的元素
l.replaceRange(0,2,[1,2,3,4]);
//截取列表中范圍內的元素返回新的列表
print(l.sublist(0,3));
//判斷列表中是否包含指定元素
print(l.contains(2));
//使用指定拼接符將列表拼接為字符串
print(l.join("-"));//1-2-3-4
//將列表轉換為字符串
print(l.toString());//[1, 2, 3, 4]
print(l);
//刪除列表中所有的元素
l.clear();
1.2.5 字典類型
字典是一組鍵值對的集合,通過鍵可以完成對值的修改、
查找、添加或刪除。在 Dart 中,字典類型叫作 Map
//鍵為字符串類型、值為整數類型的字典
Map<String,int> map2 = {"1":1,"2":2};
//使用構造方法創建字典
var map3 = Map();
//新增鍵值對
map3["name"] = "琿少";
print(map3["name"]);//琿少
//修改鍵值
map3["name"] = "Lucy";
print(map3["name"]);//Lucy
//不存在的鍵值將返回 null
print(map3["age"]);//null
//將某個鍵的值置為 null,并不會將此鍵值對刪除
map3["name"] = null;
print(map3["name"]);//null
Map 對象中常用的屬性列
//判斷 Map 是否為空
print({"1":1,"2":2}.isEmpty);//false
//判斷 Map 是否為非空
print({"1":1,"2":2}.isNotEmpty);//true
//獲取 Map 所有的鍵
print({"name":"Lucy","age":25}.keys);//(name, age)
//獲取 Map 所有的值
print({"name":"Lucy","age":25}.values);//(Lucy, 25)
//獲取 Map 中鍵值對的個數
print({"name":"Lucy","age":25}.length);//2
//獲取類型
print({"name":"Lucy"}.runtimeType);//_InternalLinkedHashMap<String, String>
Map 類中的實例方法
var map = {};
//向 Map 中追加鍵值對
map.addAll({"name":"Lucy","age":28});//{name: Lucy, age: 28}
//判斷 Map 中是否包含某個鍵
print(map.containsKey("name"));//true
//判斷 Map 中是否包含某個值
print(map.containsValue("Lucy"));//true
//刪除某個鍵值對
map.remove("name");
//將 Map 轉換成字符串
print(map.toString());
//清空 Map 中的鍵值對
map.clear();
二、Dart中的運算符
2.1 算數運算符
算數運算符通常用來進行簡單的數據運算,例如加、減、乘、除等。
print(1+2);//3
print("1"+"2");//12
print(3-1);//2
print(-(-1));//1
print(2*4);//8
print(10/2);//5.0
// 整除
print(9~/2);//4
// 取模運算符
print(9%2);//1
// 自加和自減
var a = 3;
a++;
print(a);//4
++a;
print(a);//5
2.2 比較運算符
比較運算符的作用是進行兩個值的比較。雖然在 Dart 中,比較運算符的操作數可以是任意類型的值,但是對于 List、Map 或 String 對象,一般會使用函數來進行比較,比較運算符更多用于數值之間的比較。可用的比較運算符如下表所示。
2.3 類型運算符
Dart 中的類型運算符有 3 種:as、is 和 is!。
as 運算符用來進行類型的“轉換”,需要注意,這里的類型轉換并不是真正意義上的轉換,其并不會真正修改數據的類型,而是告訴 Dart將當前數據當成某個類型的數據來進行處理。在面向對象開發中,這個運算符非常有用,后面我們會介紹。
is 運算符用來判斷數據是否屬于某個類型:如果屬于,就返回布爾值 true;如果不屬于,就返回布爾值 false。
is!運算符的作用則與 is 剛好相反,它用來判斷數據是否不屬于某個類型,示
例如下:
var a = 1;
var b = "2";
print(a is int);//true
print(b is! String);//false
2.4 復合運算符
簡單理解,復合運算符是多種簡單運算符的復合。復合運算符通常也叫作賦值復合運算符,因為其總是一種運算符與賦值運算符的組合。
2.5 邏輯運算符
邏輯運算符是針對布爾值進行運算的運算符。我們知道,布爾值只有兩種:true 和 false。邏輯運算符所適用的操作數也只有 true 或者 false。
“!”被稱為邏輯非運算符,進行邏輯非運算,它是一個前置運算符,且只有一個操作數,當操作數為布爾值 true 時,運算結果為布爾值 false,當操作數為布爾值 false 時,運算結果為布爾值true。
“||”被稱為邏輯或運算符,進行邏輯或運算。邏輯或運算遵守下面的運算規則:
(1)當兩個操作數中至少有一個操作數為 true 時,運算結果為 true。
(2)當兩個操作數都為 false 時,運算結果才為 false。
“&&”被稱為邏輯與運算符,進行邏輯與運算。邏輯與運算遵守下面的運算規則:
(1)當兩個操作數中至少有一個操作數為 false 時,運算結果為 false。
(2)當兩個操作數都為 true 時,運算結果為 true。
print(!false);//true
print(!true);//false
print(false||false);//false
print(false||true);//true
print(true||false);//true
print(true||true);//true
print(false&&false);//false
print(true&&false);//false
print(false&&true);//false
print(true&&true);//true
2.6 位運算符
位運算符是對二進制位進行操作的運算符。在計算機中,所有的數據存儲實際上采用的都是二進制。
“&”用來進行按位與運算。所謂按位與運算,是指將兩個運算符的每一個二進制位分別進行與運算,即若兩個對應二進制位都為 1,則運算結果為 1,否則為 0。
“|”用來進行按位或運算。與按位與運算一樣,按位或運算將兩個運算數的每個二進制位分別進行或運算,若兩個對應二進制位有一個為 1,則運算結果為 1,否則運算結果為 0。
“~”用來進行按位非運算。按位非運算是一個前置的單元運算符,其只有一個操作數,對操作數的每一個二進制位進行取反,即為 0 的位運算后結果為 1,為 1 的位運算后結果為 0。
“^”用來進行按位異或運算。關于按位異或運算,只需要牢記進行運算的兩個數位相同時,運算結果為 0,否則運算結果為 1 即可,即兩個二進制位都為 0 或者都為 1 時,運算結果為 0,否則運算結果為 1。
“<<”用來進行按位左移運算,即將每一個二進制位向左移動指定位數。對于二進制數據,
一個很重要的特點是每左移一位,會使原數值進行乘 2 操作
“>>”用來進行按位右移操作
var a = 10; //二進制 1010
var b = 3; //二進制 0010
print(a&b);//2 即二進制 0010
var c = 10; //二進制 1010
var d = 4; //二進制 0100
print(c|d);//14 即二進制 1110
var e = 4; //00000100
print(~e); //11111011 以補碼表示 原碼為 00000101 且為負數 即-5
var f = 3; // 0011
var g = 5; // 0101
print(f^g);// 0110 十進制 6
var h = 3;//0011
print(h<<1);//0110 十進制 6
var i = 4;//0100
print(i>>1);// 0010 十進制 2
2.7 條件運算符
條件運算符與流程控制語句中的條件語句作用很像.
“?:”是一個三元的運算符,其有 3 個操作數,第一個操作數可以是一個布爾值或者運算結果為布爾值的表達式,
當這個操作數為 true 時,條件運算的結果為第二個操作數的值,當第一個操作數為 false 時,條件
運算的結果為第三個操作數的值。
“??”是 Dart 中的空條件運算符,其有兩個操作數,若第一個操作數為 null,則運算后的值為第二個操作數的值,若第一個操作數為非 null 值,則運算后的值為第一個操作數的值。這個運算符最大的作用是保證運算的結果不為 null 值,通常用來進行安全保證。
var a = 3;
var b = 5;
var res = a>b ? "a>b" : "a<=b";
print(res);//a<=b
var c = null;
print(c==null?"無作為":"額外操作 a:$c");
var c = 3;
print(c??"無作為");
var c = null;
c ??= 0;//與 c = c??0;意義完全一樣
2.8 級聯運算符
級聯運算符使用“..”表示,級聯運算符是 Dart 中比較高級的一種運算符,它可以讓開發者對某個對象連續地進行一系列操作。這樣的好處是可以減少中間變量的生成,并且讓開發者更暢快地體驗 Dart 編碼的樂趣。
var p =People()..name="琿少"..age=26;
print("name:${p.name},age:${p.age}");
2.9 點運算符
點運算符用來對對象的屬性和方法進行操作。
class People {
String name;
int age;
void printSelf(){
print("name:${name},age:${age}");
}
}
main() {
var p = People();
p.name = "琿少";
p.age = 26;
p.printSelf();//name:琿少,age:26
}
補充,條件成員訪問運算符“?.”。這個運算符的作用是,如果所調用的對象是非 null 值,就會正常進行訪問,否則返
回 null,但是不會產生錯誤
var c = null;
print(c?.a);//null
3 Dart 中的流程控制語句
條件語句:if-else
循環語句:while,do-while,for,for-in
中斷語句:break,continue
多分支條件語句:if-else if-else,switch-case
異常處理:throw,try-catch-finally
補充:
while 語句會首先進行循環條件的判定,如果不滿足,
就不再執行循環體,滿足條件才會進行循環;
do-while 語句則是首先執行一次循環體中的代碼,之后進行循環條件的判定,如果滿足,就繼續執行循環體,如果不滿足,就跳出循環
break 語句會直接跳出本層循環,執行循環后面的代碼,而 continue 語句則是跳過本次循環后,還會進行循環條件的判定
3 Dart 高級進階
3.1構造方法
一旦重寫了構造方法,默認的無參構造方法將不再可用。
構造方法的實質是將對象屬性的賦值過程由外界封裝到類的內部。
構造方法的編寫還有一個小技巧,一般情況下,我們可以直接將構造方法定義成如下模樣,Dart 會自動進行參數和屬性的匹配,進行賦值,非常方便.
main() {
var circle = new Circle(6,1,1);//使用參數構造圓形對象
print(circle.radius);//6.0
}
class Circle {
//半徑
double radius = 0;
//圓心 X
double centerX = 0;
//圓心 Y
double centerY = 0;
//構造方法
Circle(this.radius,this.centerX,this.centerY);
}
一個類需要有多個構造方法,比如自定義的圓形類,很多時候需要快速創建出單位圓(圓心為坐標原點、半徑為 1 的圓)。這時,就可以定義一個便捷的構造方法幫助我們直接生成單位圓,這類構造方法也被稱為命名構造方法.
main() {
var circle2 = Circle.standard();
print(circle2.radius);//1
}
class Circle {
//半徑
double radius = 0;
//圓心 X
double centerX = 0;
//圓心 Y
double centerY = 0;
//構造方法
Circle(this.radius,this.centerX,this.centerY);
//命名構造方法,單位圓
Circle.standard(){
this.radius = 1;
this.centerX = 0;
this.centerY = 0;
}
}
命名構造方法通常用來快速地創建標準對象,同樣,命名構造方法也可以有參數,并且只要參數名與類中定義的屬性名一致,也可以使用 Dart 自動匹配賦值的特性。
3.2實例方法
類封裝了屬性和方法,屬性用來存儲描述類的數據,方法用來描述類的行為。
方法的用法和函數一樣,只是在調用時需要用對象來調用,并且方法中會自動將當前對象綁定到 this 關鍵字上。也就是說,在方法中可以通過 this 關鍵字獲取對象的屬性信息,也可以調用其他方法。方法也需要通過點語法來進行調用。
Setters 方法與 Getters 方法
Setters 方法用來設置對象屬性,Getters 方法用來獲取對象屬性。其實當我們使用點語法訪問對象屬性信息時,調用的就是 Setters方法或 Getters 方法,在定義屬性時,Dart 會自動生成默認的 Setters 方法和 Getters 方法。Setters方法和 Getters 方法的另一大作用是定義附加屬性,附加屬性也可以理解為計算屬性,即這些數據通常不是描述對象的最原始數據,而是通過計算得來的,例如下面的description就是附加屬性:
main() {
var teacher = Teacher("琿少",1101,"Dart");
teacher.sayHi("小明"); //Hello 小明,我是琿少老師!編號為1101
teacher.teaching(); //琿少老師正在進行 Dart 教學
print(teacher.description); //琿少:Dart
teacher.description = "Lucy:JavaScript";
teacher.teaching(); //Lucy 老師正在進行 JavaScript 教學
}
class Teacher {
String name;
int number;
String subject;
Teacher(this.name,this.number,this.subject);
void sayHi(String toName){
print("Hello ${toName},我是${this.name}老師!編號為${this.number}");
}
void teaching(){
print("${this.name}老師正在進行${this.subject}教學。");
}
String get description{
return "${this.name}:${this.subject}";
}
set description(String value){
this.name = (value.split(":") as List)[0];
this.subject = (value.split(":") as List)[1];
}
}
上面的代碼中,description 就是附加屬性,其并沒有真正占用內存空間進行存儲,而是通過其他屬性計算而來的
3.3 抽象類與抽象方法
抽象類,是面向對象開發中較為難理解的一點。在 Dart 中,抽象類中可以定義抽象方法。
抽象方法,是指只有定義卻沒有實現的方法,抽象是面向接口開發的基礎。
抽象類實際上就是一個接口,接口中定義了未實現的方法告訴調用者:如果有類實現了這個接口,這個類就擁有接口所描述的功能。例如,我們可以為教師類定義一個接口,示例如下:
abstract class TeacherInterface {
void teaching();
}
上面的 TeacherInterface 接口中只定義了一個抽象方法,Teacher 類可以對這個接口進行實現,示例代碼如下:
abstract class TeacherInterface {
void teaching();
}
class Teacher implements TeacherInterface {
String name;
int number;
String subject;
Teacher(this.name,this.number,this.subject);
void sayHi(String toName){
print("Hello ${toName},我是${this.name}老師!編號為${this.number}");
}
void teaching(){
print("${this.name}老師正在進行${this.subject}教學。");
}
}
一個類也可以同時實現多個接口,例如再定義一個人類接口,示例如下:
abstract class TeacherInterface {
void teaching();
}
abstract class PeopleInterface {
void sayHi(String name);
}
class Teacher implements TeacherInterface,PeopleInterface {
String name;
int number;
String subject;
Teacher(this.name,this.number,this.subject);
void sayHi(String toName){
print("Hello ${toName},我是${this.name}老師!編號為${this.number}");
}
void teaching(){
print("${this.name}老師正在進行${this.subject}教學。");
}
}
抽象類不可以被實例化,即不能直接使用抽象類來構造實例對象,只能通過實現這個抽象類接口的類或者繼承它的子類來實例化對象。
遺留問題:類實現了抽象類,則類中必須重寫抽象類中的方法嗎?