JIT,即Just-in-time,動態(即時)編譯,邊執行邊編譯;
https://book.flutterchina.club/chapter1/dart.html flutter中文網
或者讓演員表演兩分鐘,再看兩分鐘指令碼,思考一下,再表演兩分鐘,再看一會指令碼,思考一下。。。恩。。。
這就是 JIT 的原理。批次的把一件事情先整理成爲目標能夠直接接受的方式,然後直接執行。
AOT,Ahead Of Time,指執行前編譯,是兩種程式的編譯方式
假定你是個導演,寫了個指令碼,讓演員表演。
先讓演員把整個指令碼都背下來,吃透,到腦子裏,然後連續的表演一個小時。
Flutter與用於構建移動應用程式的其它大多數框架不同,因爲Flutter既不使用WebView,也不使用操作系統的原生控制元件。 相反,Flutter使用自己的高效能渲染引擎來繪製widget。這樣不僅可以保證在Android和iOS上UI的一致性,而且也可以避免對原生控制元件依賴而帶來的限制及高昂的維護成本。
Flutter使用Skia作爲其2D渲染引擎,Skia是Google的一個2D圖形處理常式庫,包含字型、座標轉換,以及點陣圖都有高效能且簡潔的表現,Skia是跨平臺的,並提供了非常友好的API,目前Google Chrome瀏覽器和Android均採用Skia作爲其繪圖引擎。
目前Flutter預設支援iOS、Android、Fuchsia(Google新的自研操作系統)三個移動平臺。但Flutter亦可支援Web開發(Flutter for web)和PC開發,本書的範例和介紹主要是基於iOS和Android平臺的,其它平臺讀者可以自行瞭解。
Flutter高效能主要靠兩點來保證,首先,Flutter APP採用Dart語言開發。Dart在 JIT(即時編譯)模式下,速度與 JavaScript基本持平。但是 Dart支援 AOT,當以 AOT模式執行時,JavaScript便遠遠追不上了。速度的提升對高影格率下的檢視數據計算很有幫助。其次,Flutter使用自己的渲染引擎來繪製UI,佈局數據等由Dart語言直接控制,所以在佈局過程中不需要像RN那樣要在JavaScript和Native之間通訊,這在一些滑動和拖動的場景下具有明顯優勢,因爲在滑動和拖動過程往往都會引起佈局發生變化,所以JavaScript需要和Native之間不停的同步佈局資訊,這和在瀏覽器中要JavaScript頻繁操作DOM所帶來的問題是相同的,都會帶來比較可觀的效能開銷。
這是一個很有意思,但也很有爭議的問題,在瞭解Flutter爲什麼選擇了 Dart而不是 JavaScript之前我們先來介紹兩個概念:JIT和AOT。
目前,程式主要有兩種執行方式:靜態編譯與動態解釋。靜態編譯的程式在執行前全部被翻譯爲機器碼,通常將這種型別稱爲AOT (Ahead of time)即 「提前編譯」;而解釋執行的則是一句一句邊翻譯邊執行,通常將這種型別稱爲JIT(Just-in-time)即「即時編譯」。AOT程式的典型代表是用C/C++開發的應用,它們必須在執行前編譯成機器碼,而JIT的代表則非常多,如JavaScript、python等,事實上,所有指令碼語言都支援JIT模式。但需要注意的是JIT和AOT指的是程式執行方式,和程式語言並非強關聯的,有些語言既可以以JIT方式執行也可以以AOT方式執行,如Java、Python,它們可以在第一次執行時編譯成中間位元組碼、然後在之後執行時可以直接執行位元組碼,也許有人會說,中間位元組碼並非機器碼,在程式執行時仍然需要動態將位元組碼轉爲機器碼,是的,這沒有錯,不過通常我們區分是否爲AOT的標準就是看程式碼在執行之前是否需要編譯,只要需要編譯,無論其編譯產物是位元組碼還是機器碼,都屬於AOT。在此,讀者不必糾結於概念,概念就是爲了傳達精神而發明的,只要讀者能夠理解其原理即可,得其神忘其形。
現在我們看看Flutter爲什麼選擇Dart語言?筆者根據官方解釋以及自己對Flutter的理解總結了以下幾條(由於其它跨平臺框架都將JavaScript作爲其開發語言,所以主要將Dart和JavaScript做一個對比):
開發效率高
Dart執行時和編譯器支援Flutter的兩個關鍵特性的組合:
基於JIT的快速開發週期:Flutter在開發階段採用,採用JIT模式,這樣就避免了每次改動都要進行編譯,極大的節省了開發時間;
基於AOT的發佈包: Flutter在發佈時可以通過AOT生成高效的ARM程式碼以保證應用效能。而JavaScript則不具有這個能力。
高效能
Flutter旨在提供流暢、高保真的的UI體驗。爲了實現這一點,Flutter中需要能夠在每個動畫幀中執行大量的程式碼。這意味着需要一種既能提供高效能的語言,而不會出現會丟幀的週期性暫停,而Dart支援AOT,在這一點上可以做的比JavaScript更好。
快速記憶體分配
Flutter框架使用函數式流,這使得它在很大程度上依賴於底層的記憶體分配器。因此,擁有一個能夠有效地處理瑣碎任務的記憶體分配器將顯得十分重要,在缺乏此功能的語言中,Flutter將無法有效地工作。當然Chrome V8的JavaScript引擎在記憶體分配上也已經做的很好,事實上Dart開發團隊的很多成員都是來自Chrome團隊的,所以在記憶體分配上Dart並不能作爲超越JavaScript的優勢,而對於Flutter來說,它需要這樣的特性,而Dart也正好滿足而已。
型別安全
由於Dart是型別安全的語言,支援*靜態型別檢測,*所以可以在編譯前發現一些型別的錯誤,並排除潛在問題,這一點對於前端開發者來說可能會更具有吸引力。與之不同的,JavaScript是一個弱型別語言,也因此前端社羣出現了很多給JavaScript程式碼新增靜態型別檢測的擴充套件語言和工具,如:微軟的TypeScript以及Facebook的Flow。相比之下,Dart本身就支援靜態型別,這是它的一個重要優勢。
Dart團隊就在你身邊
看似不起眼,實則舉足輕重。由於有Dart團隊的積極投入,Flutter團隊可以獲得更多、更方便的支援,正如Flutter官網所述「我們正與Dart社羣進行密切合作,以改進Dart在Flutter中的使用。例如,當我們最初採用Dart時,該語言並沒有提供生成原生二進制檔案的工具鏈(這對於實現可預測的高效能具有很大的幫助),但是現在它實現了,因爲Dart團隊專門爲Flutter構建了它。同樣,Dart VM之前已經針對吞吐量進行了優化,但團隊現在正在優化VM的延遲時間,這對於Flutter的工作負載更爲重要。」
本節主要介紹了一下Flutter的特點,如果你感到有些點還不是很好理解,不用着急,隨着日後對Flutter細節的瞭解,再回過頭來看,相信你會有更深的體會。
本節我們先對Flutter的框架做一個整體介紹,旨在讓讀者心中有一個整體的印象,這對初學者來說非常重要。如果一下子便深入到Flutter中,就會像是一個在沙漠中沒有地圖的人,即使可以找到一個綠洲,但是他也不會知道下一個綠洲在哪。因此,無論學什麼技術,都要先有一張清晰的「地圖」,而我們的學習過程就是「按圖索驥」,這樣我們纔不會陷於細節而「目無全牛」。言歸正傳,我們看一下Flutter官方提供的Flutter框架圖,如圖1-1所示:
這是一個純 Dart實現的 SDK,它實現了一套基礎庫,自底向上,我們來簡單介紹一下:
dart:ui
包,它是Flutter引擎暴露的底層UI庫,提供動畫、手勢及繪製能力。這是一個純 C++實現的 SDK,其中包括了 Skia引擎、Dart執行時、文字排版引擎等。在程式碼呼叫 dart:ui
庫時,呼叫最終會走到Engine層,然後實現真正的繪製邏輯。
Flutter框架本身有着良好的分層設計,本節旨在讓讀者對Flutter整體框架有個大概的印象,相信到現在爲止,讀者已經對Flutter有一個初始印象,在我們正式動手之前,我們還需要瞭解一下Flutter的開發語言Dart
1.var
類似javascript裏面的 var 但是 dart裏面的 一旦使用 型別就會固定不能改變 否則會報錯
var t;
t = "hi world";
// 下面 下麪程式碼在dart中會報錯,因爲變數t的型別已經確定爲String,
// 型別一旦確定後則不能再更改其型別。
t = 1000;
2.dynamic和Object
是Dart所有物件的根基礎類別,也就是說所有型別都是Object
的子類(包括Function和Null),所以任何型別的數據都可以賦值給Object
宣告的物件. dynamic
與var
一樣都是關鍵詞,宣告的變數可以賦值任意物件。 而dynamic
與Object
相同之處在於,他們宣告的變數可以在後期改變賦值型別。
dynamic t;
Object x;
t = "hi world";
x = 'Hello Object';
//下面 下麪程式碼沒有問題
t = 1000;
x = 1000;
dynamic
與Object
不同的是,dynamic
宣告的物件編譯器會提供所有可能的組合, 而Object
宣告的物件只能使用Object的屬性與方法, 否則編譯器會報錯。如:
dynamic a;
Object b;
main() {
a = "";
b = "";
printLengths();
}
printLengths() {
// no warning
print(a.length);
// warning:
// The getter 'length' is not defined for the class 'Object'
print(b.length);
}
變數a不會報錯, 變數b編譯器會報錯
dynamic
的這個特性與Objective-C
中的id
作用很像. dynamic
的這個特點使得我們在使用它時需要格外注意,這很容易引入一個執行時錯誤.
3.final和const
如果您從未打算更改一個變數,那麼使用 final
或 const
,不是var
,也不是一個型別。 一個 final
變數只能被設定一次,兩者區別在於:const
變數是一個編譯時常數,final
變數在第一次使用時被初始化。被final
或者const
修飾的變數,變數型別可以省略,如:
常數的值可以賦值給變數 變數的值可以被修改 常數的值不可以被更改
final name = "Bob";
main() {
var t = name;
t = '123';
name = "123" //報錯
print(t);
}
//可以省略String這個型別宣告
final str = "hi world";
//final String str = "hi world";
const str1 = "hi world";
//const String str1 = "hi world";
在學習 Dart 語言時, 應該基於以下事實和概念:
null
都是物件。所有物件繼承自 Object 類。number
被推斷爲 int
型別。 如果要明確說明不需要任何型別, 需要使用特殊型別 dynamic
。List <int>
(整數列表)或 List <dynamic>
(任何型別的物件列表)。main()
), 同樣函數系結在類或物件上(分別是 靜態函數 和 範例函數 )。 以及支援函數內建立函數 ( 巢狀 或 區域性函數 ) 。condition ? expr1 : expr2
的值可能是 expr1
或 expr2
。 將其與 if-else 語句 相比較,if-else 語句沒有值。 一條語句通常包含一個或多個表達式,相反表達式不能直接包含語句。void 函數沒有返回值
void main(){
print('123')
}
var int String 定義變數 會自動推斷型別 推斷型別不能更改 否則報錯;
var str= ‘’;
str = 123 //報錯
String str = '';
str=1234 //報錯 必須爲字串
int str = 123;
str='1234' //報錯 必須爲數位
識別符號不能以數位開頭
變數名必須由數位,字母,下劃線和美元($)組成;
識別符號不能是保留字關鍵字;
變數名的名字是區分大小寫的: age和Age是不同變數名 不建議使用;
識別符號(變數名)一點要見名思意:變數名建議用名詞 ,方法名建議用動詞;
const PI = 3.14159;
PI = 123; // 常數的值不可以修改
final PI = 3.14159;
PI = 123; //常數的值不可以修改
兩者區別在於:const
變數是一個編譯時常數,final
變數在第一次使用時被初始化。被final
或者const
修飾的變數,變數型別可以省略,如:
final a = new DateTime.now();
print(a) // 2020-7-22 16:00
const a = new DateTime.now();
print(a) // 2020-7-22 16:00 //報錯
1.常用數據型別
String 字元型別 字串拼接 可以‘+’拼接 也可以 ${} $a 拼接。
String a = 'a'
String b= ''' asda
asd
asd''';//字串可以換行
String a = 'abc';
String c = 'aaa${abc}' // aaaabc 字串拼接
2**.int 和double數位型別**
double既可以是整形也可以是浮點型
int只能是整形 整形不能賦值爲浮點型 小數
int a = 123;
double b = 1.1;
double c = 2;
3.bool 布爾值 true和false
bool a = false
bool b = true
條件判斷 ‘123’ 不等於 123 不會型別隱式轉換
bool flag = true;
if(flag) {
print('真')
}else {
print('假')
}
4.list 定義方式 (array)
var a = [1,2,3]; //第一種 var a = [];
a.length; //3
a[0]; //1
var l = new List(); //第二種建立list方式 new List();
l.add('張三');
l.add('李四');
l.add('王五');
定義List 指定型別
var a = new List<String>(); //List裏面只能是String型別
var a = new List<int>(); //List裏面只能是int型別
5.Maps 型別 類似 js物件
//第一種定義Maps的方式
var person = {
"name":"張三",
"age":20
}
print(person) //{"name":"張三","age":20}
print(person["name"]) //張三
print(person["age"]) //20
//第二種定義maps的方式
var p = new Map();
p["name"] = "張三";
p["age"] = 20;
print(p) //{"name":"張三","age":20}
print(p["age"]) // 20
6.is 關鍵詞來判斷型別
var str = "123";
if( str == String) {
print (String型別)
} else if ( str == int ){
print(int型別)
}else {
print(’其他‘)
}
int a= 13;
int b = 5;
print(a+b)//18
print(a-b)//8
print(a*b)//65
print(a/b)//2.6
print(a%b)//3 餘
print(a~/b)//2 取整
int a= 13;
int b = 5;
print(a>b)//true
print(a<b)//false
print(a>=b)//true
print(a<=b)// false
print(a==b)// fasle
bool a = !fasle //true
&& 兩個都爲真 才爲真
|| 一個爲真 就爲真
int b = 10或'';
b??=23; //如果 b爲空 b = 23 如果 b 不爲空 b = 10;
print(b) //23
int a = a+
if(a>0) {
print('a大於零')
}else {
print('小於零')
}
String sex = "123";
switch (sex) {
case "男":
print(sex);
break;
case "女":
print(sex);
break;
default:
print('參數錯誤');
}
}
bool a = true;
String c = a?"我是true":"我是false";
??用法
var a;
b = a || 20;//20
var a = 10;
b = a || 20;//10
int 型別 只能轉換 int 型別 不能轉換 double 型別 會報錯
double 可以轉換 int型別 和double型別 但是不能轉換 「」型別 會報錯
字串轉數值型 int.parse 或double.pares String a = '123'; var num = int.parse(a); //true String a = '123.1'; var num = int.parse(a); //會報錯 String a = '123.1'; var num = double.parse(a); //true String a = '123.1'; var num = int.parse(a); //true 假如後臺傳過來一個空 需要做判斷 不會報錯 try 轉換失敗 拋出異常 String price= ''; try{ var num = double.parse(price); print(num) }carch(err) { print("傳過來的爲空"); } 數值轉字串 toString(); var a = 10; var b = a.toString(); //數值轉字串 print(b is String) //true isEmpty 是否爲空; 只能判斷String型別 不能判斷int double 型別 var a = ""; if(a.isEmpty) { print('是爲空') }else { print('不爲空'); } 0除0返回NaN var myNum = 0/0; if(myNum.isNaN) { print('Nan'); }
如果++ --寫在前面 先運算 在賦值 如果++ – 先賦值 後運算
++ 自增寫在前面和後面的區別
var b = 10; // 11
var a = ++b; //11 寫在前面 先 自增加一
print(b) //11
print(a) //11;
var a = b++; //10 寫在後面 先 賦值 給b 所以b =10 再 b+1;
print(10) //11;
for (int i=1;i<=10;i++){
print(i); //1 -10;
}
//100求和
var sum =0;
for(int i=0;i<=100;i++){
sun +=i;
}
print(sum)//5050
//5的階剩 1*2*3*4*5
var sum =1;
for(int i=1;i<=5;i++){
sun *=i;
}
print(sum)//120
1.列印陣列 List
var list = new List();
List list = ['張三','李四','王二'];
for(int i = 0;i<lsit.length-1;i++){
print(list[i]); //張三 李四 王二
}
2.列印陣列 List;
var List = [
{"name":'馮上進'},
{"name":'張三'},
{"name":"李四"}
]
for(int i=0;i<List.length;i++){
print(List[i]["name"]) //張三 李四 王二
}
3.列印陣列List 雙層for回圈 list 必須是 爲陣列 字面量 List
List list = [
{
"title": '標題',
"name": [
{"age": 20},
{"age": 20},
{"age": 20}
]
},
{
"title": '標題',
"name": [
{"age": 20},
{"age": 20},
{"age": 20}
]
},
{
"title": '標題',
"name": [
{"age": 20},
{"age": 20},
{"age": 20}
]
}
];
for (int i = 0; i < list.length; i++) {
print(list[i]["title"]);
for (int j = 0; j < list[i]["name"].length; j++) {
print(list[i]["name"][j]["age"]);
}
}
語法格式
while(表達式/回圈條件) {
裏面的語句會一直回圈
防止死回圈
}
//先判斷語句 再回圈
do{
語句/回圈體
} while('表達式/回圈條件');
//先回圈語句 在判斷條件
//列印1-10
var i=1;
while(i<10){
print(i);
i++;
}
//while 計算 1-100 的和
var i=1;
var sum = 0;
while(i<=100) {
print(sun+=i);
i++;
}
//do...while 計算 1-100 的和
var i = 1;
var sum = 0;
do {
sum += i;
i++;
} while (i <= 100);
print(sum);
while 和 do…while 的 區別 :
如果 條件不成立;
wihile 先判斷條件 在執行 ;
do…while 先執行至少一次 再 判斷條件;
break可以在switch case 中 使用 也可以在for 回圈 while中使用
for(var i=1;i<=10;i++){
if(i==4) {
continue; // 跳出當前回圈體 回圈會繼續執行
}
print(i); //123 56789
}
for(var i=1;i<=10;i++){
if(i==4) {
break; // 跳出當前循結束 只能跳出一層for回圈
}
print(i);//123
}
常用屬性 :
常用放法:
List list = ["蘋果","香蕉","西瓜"];
List list = ['蘋果', '香蕉', '水果'];
print(list.length); //3
print(list.reversed.toList()); // [水果, 香蕉, 蘋果]
print(list.isEmpty); //false
print(list.isNotEmpty); //true
list.add("123");
list.fillRange(1, 2, '111');
list.remove('111');
print(list);//[蘋果, 水果, 123]
var a = list.indexOf("1");
print(a);//-1
list.removeAt(1);
print(list);//[蘋果, 123]
list.insert(1, "馮上進");
print(list);//[蘋果, 馮上進, 123]
list.insertAll(1, ["我", "是", "馮"]);
print(list);//[蘋果, 我, 是, 馮, 馮上進, 123]
var s = list.join();
print(s);//蘋果我是馮馮上進123
var m = list.join(',');
print(m);//蘋果,我,是,馮,馮上進,123
//new Set()陣列去重 返回物件 .toList 轉成List型別
var l = new Set();
l.add("蘋果");
l.add("香蕉");
l.add("蘋果");
print(l);{"蘋果","香蕉"}
print(l.toList());//["蘋果","香蕉"];
常用屬性:
常用放法:
var person = {
"name" :'張三';
"age" :'李四';
}
var person = new Map();
perosn["name"] = "張三"
var person = {"name": "張三", "age": 20};
print(person.keys.toList()); [name, age]
print(person.values.toList());[張三, 20]
print(person.isEmpty);false
print(person.isNotEmpty);true
person.addAll({"work": '敲程式碼', "身高": '160'});
print(person); {name: 張三, age: 20, work: 敲程式碼, 身高: 160}
person.remove("name");
print(person); {age: 20, work: 敲程式碼, 身高: 160}
var s = person.containsValue("張三");檢視value 是否存在
print(s); //false
List list = ["蘋果","香蕉","西瓜"];
list.forEach((value){
print(value); //蘋果香蕉水果
})
List list = [1, 2, 3];
list.forEach((element) {
print(element * 2); //2 4 6
});
List list = [1, 2, 3];
var newList = list.map((value){
return value*2;
})
print(newList); //(2,4,6);
List list = [1, 2, 3,4,5,6,7,8,9];
var newList = list.where((value){
return value>5
})
print(newList); (6,7,8,9);
var s = new Set();
a.addAll([1,222,333,333]);
s.forEach((value){
print(value); //1,222,333 去重後的陣列 List
})
var person = {
"name":'馮上進',
"age":18
}
person.forEach((keys,value){
print("$key---$value")
});
print() 內建方法
自定義方法:
自定義方法的基本格式:
返回型別 方法名稱 (參數1,參數2,...){
方法體;
return 返回值;
}
void 表示函數沒有返回值 函數寫在入口方法外面 是全域性的方法
void main(){
String sum (a,b){
c = a+b;
print(c); //5
};
sum(2,3)
}
函數封裝 1-n的和
int sumNum(int n){
int sum = 0;
for(int i =0;i<=n;i++){
sum+=i;
}
return sum;
}
int s = sumNum(60);
print(s);
1.定義一個方法 然後列印使用者資訊
String printUserInfo(String username,int age){
return "姓名:$username ---年齡:$age";
}
String s = printUserInfo('馮上進',20);
print(s);// 姓名:馮上進---年齡:20
2.//[int age,String sex] 可選參數 可傳 可不傳
String printUserInfo(String username,[int age]){
if(age!==null){
return "姓名:$username ---年齡:$age";
}
return "姓名:$username ---年齡保密";
}
String s = printUserInfo('馮上進');//不傳參數
print(s);// 姓名:馮上進---年齡保密
String s = printUserInfo('馮上進',20);//傳參數
print(s);// 姓名:馮上進---年齡:20
3.預設參數 [int age,String sex="男"]
String printUserInfo(String username,[int age,String sex="男"]){
if(age!==null){
return "姓名:$username ---年齡:$age--性別:$sex";
}
return "姓名:$username ---年齡保密---性別:$sex";
}
String l = printUserInfo('馮上進', 20);
print(l); // 姓名:馮上進 ---年齡:20--性別:男
4.命名參數 {int age,String sex="男"} printUserInfo('馮上進', age:20,sex:'未知')
String printUserInfo(String username,{int age,String sex="男"}){
if(age!==null){
return "姓名:$username ---年齡:$age--性別:$sex";
}
return "姓名:$username ---年齡保密---性別:$sex";
}
String l = printUserInfo('馮上進', age:20,sex:'未知'); //命名參數 傳參
print(l); // 姓名:馮上進 ---年齡:20--性別:未知
5.實現一個把方法當做參數的方法
fn1(){
print('fn1');
}
fn2(fn){
fn();
}
fn2(fn1); //'fn1'
List list =['香蕉','蘋果','橘子'];
list.forEach((value)=>{print(value)});
1.匿名放法 把方法賦值給變數 printNum
var printNum = (){
print(123);
}
2.自執行方法
((n){
print(n);
})(12) //12
3.方法的遞回
int sum = 1;
fn(n){
sum*=n;
if(n== 1){
return;
}
fn(n-1);
}
print(sum);
3.通過方法的遞回 求1-100的和;
int sum = 0;
fn(n) {
sum += n;
if (n == 0) {
return;
}
fn(n - 1);
}
fn(100);
print(sum);
}
1.全域性變數特點:全域性變數常駐記憶體,全域性變數污染全域性。
2.區域性變數的特點:不常駐在記憶體 會被垃圾回收機制 機製回收 ,不會污染全域性。
想實現的功能:
1.常駐記憶體。
2.不污染全域性。
產生閉包,閉包可以解決這個問題…
閉包 :函數巢狀函數,內部函數會呼叫外部函數的變數或參數,變數或參數不會被系統回收。
閉包的寫法:函數巢狀函數,並return裏面的函數,這樣就形成了閉包。
fn() {
var a = 123; //不會全域性污染 常駐記憶體;
return () {
a++;
print(a);
};
}
var b = fn();
b(); //124
b();//125
b();//126
b();//127
b();//128
dart所有的東西都是物件,所有的物件都繼承自Object類。
dart是一門使用類和單繼承的物件導向語言,所有的物件都是類的範例,並且所有的類都是Object的子類
一個類通常是由屬性和放法組成。
系統的類
void main (){
List list = new List();
list.isEmpty;
list.add('香蕉');
list.add('蘋果');
Map m = new Map();
m["userName"] = '馮上進';
m.addAll({"age":20});
}
自定義類 定義裏面的屬性和放法 預設建構函式 範例化的時候呼叫
class Person{
String name = "張三";
int age = 20;
Person(){
print('這是建構函式的內容,這個方法在範例化時候被觸發 預設建構函式');
};
void getInfo(){
print("${this.name} --- ${this.age}");
}
}
Person n = new Person();
print(n.name);//張三
n.getInfo(); // 張三--- 20
class Person{
String name ;
int age;
//預設建構函式
Person(String name,int age){
print('這是建構函式的內容,這個方法在範例化時候被觸發 預設建構函式');
this.name = name;
this.age = age;
}; // 預設建構函式的簡寫Person(this.name,this.age);
void getInfo(){
print("${this.name} --- ${this.age}");
}
}
Person n = new Person('張1',20);
print(n.name);//張1
print(n.age);//20
Person n1 = new Person('李四',25); //類 可以範例化多次
class Person{
String name = "張三";
int age = 20;
Person.now(){
print('命名建構函式');
}
Person(){
print('這是建構函式的內容,這個方法在範例化時候被觸發 預設建構函式');
};
void getInfo(){
print("${this.name} --- ${this.age}");
}
}
Person n = new Person.now(); //命名建構函式 var time = new Datetime.now(); 也屬於命名建構函式
總結:1.預設建構函式 和命名建構函式 都在範例化的時候被觸發。傳參和預設建構函式一樣
2.建構函式封裝 單獨抽離 使用 import 引入
import '檔名/類資料夾';
class Person {
String name = "馮上進";
int age = 20;
//預設建構函式
Person(String name, int age) {
this.name = name;
this.age = age;
}
//命名建構函式
Person.son(String name, int age) {
this.name = name;
this.age = age;
}
}
封裝好的類
import 'person.dart'; //最前面不用加點
void main() {
var n = new Person('李四', 100);
print(n.name);
print(n.age);
var n1 = new Person("馮大大", 1000);
print(n1.name);
print(n1.age);
var n2 = new Person.son("江西", 100);
print(n2.name);
print(n2.age);
}
呼叫上面的類
Dart個其他物件導向不一樣,Dart中沒有public private protected 這些存取修飾符合。但是我們可以使用 " - " (下劃線)把一個屬性或方法定義成私有(必須在一個檔案裏面)。
class Person {
String _name = "馮上進";//是有私屬性
getName(){//公有方法
return this._name; //私有屬性 通過公有方法可存取
}
_run (){
print('123');
}//是私有方法
getrun(){//公有方法
this._run();
}
int age = 20;
//預設建構函式
Person(String name, int age) {
this.name = name;
this.age = age;
}
//命名建構函式
Person.son(String name, int age) {
this.name = name;
this.age = age;
}
}
封裝好的類
import 'person.dart'; //最前面不用加點
void main() {
var n = new Person();
n._name; //存取不到 _name 因爲 _name 爲上面 抽離類的是有方法。
n.getName(); // 通過公有的方法可以存取上面類的是有方法,獲取私有屬性。
n._run(); //存取不到上面類的私有方法。
n.getrun();//通過公有方法 可以存取到 上面的私有方法。
}
總結:通過公有方法間接呼叫私有屬性和私有方法、
class Per {
int width;
int height;
sum(int width, int height) {
return width * height;
}
}
void main() {
var n = new Per();
var s = n.sum(100, 200);
print(s); //40000
}// 第一種方法 非get存取類裏面的方法
class Per {
int width;
int height;
Per(this.width, this.height);
get sum { //通過花括號計算屬性得到的 sum的值
return width * height;
}
set areaHeight(value){
this.height = value;
}
}
void main() {
var n = new Per(20,20);
n.sum // 400 使用get的時候 存取類裏面的方法是用帶你的形式存取。 注意呼叫直接通過存取屬性的方式存取 sum 類似計算屬性;
n.areaHeight = 10; //set的計算屬性 呼叫修改高度後 再計算面積
print(n.sum); //呼叫修改高度後 再計算面積
}
class Rect(){
int height;
int width;
Rect():height=2,width=10{
print("${this.height}---${this.width}"); //2---10初始化範例變數
} //dart中我們也可以在建構函式體執行之前初始化範例變數
getArea(){
return width*height;
}
}
void main(){
Reat r = new Reat();
print(r.getArea());
}
class Rect(){
static int height = 10;
static int width = 20;
int age = 20; //靜態方法
static getArea(){
return width*height;
}
static prints(){
return age; //報錯 靜態方法不可訪非靜態屬性。
printWdith(); //報錯 靜態方法不可訪非靜態方法。
}
//非靜態方法
void printWidth(){
print(width); //非靜態方法 可以存取靜態成員和非靜態成員。
print(this.age); //存取非靜態屬性
prints() //呼叫靜態方法
}
}
void main(){
print(Rect.height); //10
print(Rect.getArea());//200 //通過類存取靜態屬性和靜態方法
Reat r= new Reat();
r.printWidth();//20 20
}
? 條件運算子(瞭解)
as 型別轉換
is 型別判斷
… 級聯操作(連綴)(記住)
class Person {
String name = "張三";
int age = 20;
printName() {
print(this.name);
}
}
void main() {
Person p = new Person();
if (p is Person) { //is的用法
p.name = "李四";
}
p.printName(); //李四
var p1;
p1 ='';
p1 = new Person('張三',20);
p1.printInfo(); //老版本報錯 新版本不報錯
(p1 as Person).printInfo(); //型別轉換 as 字串轉換成Person 類
}
class Person { //級聯..的用法
String name;
int age;
Person(this.name, this.age);
printName() {
print("${this.name}---${this.age}");
}
}
void main() {
Person p = new Person('張三', 20);
p.printName();
p.name = '李四'; //20
p.age = 200;
p.printName(); //200
//上面的簡寫下面 下麪的樣子
//用級聯 ..
p
..name = "李四222"
..age = 100
..printName(); //李四222---100
}
class Father {
String name = '爸爸';
int age = 20;
void printName() {
print(this.name);
}
}
class Son extends Father {}
void main() {
Son n = new Son();
print(n.name); //爸爸
}
//類的繼承
//建構函式繼承
class Father {
String name = '爸爸';
int age = 20;
Father(this.name, this.age);
void printName(String name, int age) {
print("${this.name}---${this.age}");
}
}
class Son extends Father {
Son(String name, int age) : super(name, age) {} //關鍵 處 繼承預設建構函式
}
void main() {
Son n = new Son('張三', 20);
n.printName('馮上進', 20);
}
//建構函式繼承 子類可以傳自己的參數 定義自己的方法
class Father {
String name = '爸爸';
int age = 20;
Father(this.name, this.age);
Father.xxx(this.name, this.age);
void printName(String name, int age) {
print("${this.name}---${this.age}");
}
void age(){
print(this.age);
}
}
class Son extends Father {
String sex;
//繼承父類別的預設建構函式 和命名建構函式 和傳參
Son(String name, int age, String sex) : super.xxx(name, age) { //命名建構函式傳參
this.sex = sex;
}
run() { //自己的方法
print("${this.name}---${this.age}---${this.sex}");
super.age(); // 在run方法裏面呼叫父類別的方法 age
}
@override //複寫父類別的方法 @override可寫 可不寫
void printName(String name, int age) {
print("${this.name}-----${this.age}");
}
}
void main() {
Son n = new Son('張三', 20, '男');
n.printName('馮上進', 20); //'馮上進'-----20
n.run(); // 張三 --- 20 --- 男 20
}
總結: 1.類的繼承 extends 繼承父類別的屬性方法 繼承不了 建構函式 2, 3爲繼承建構函式的方法
2.預設建構函式的繼承 Son(String name, int age) : super(name, age) {}
3.命名建構函式的繼承 Son(String name, int age) : super.xxx(name, age){}
4.子類可以傳自己的參數到建構函式中 Son(String name, int age, String sex) : super.xxx(name, age)
5.複寫父類別的 方法 @override(可以省略) 父類別的方法名
6.子類方法中可以呼叫父類別的方法 super.age(); // 在run方法裏面呼叫父類別的方法 age
Dart中抽象類:Dart抽象類主要用於定義標準,子類可以繼承抽象類,也可以實現抽象類介面。
extends抽象類 和 implements的區別:
案例:定義一個Animal 類 要求它的子類必須包含eat方法。
//定義一個 Animal的抽象類
abstract class Animal {
eat(); // 抽象方法
run(); // 抽象方法
printInfo() {
//抽象類中的普通方法 子類的都可以繼承
print("抽象類中的普通方法");
}
}
class Dog extends Animal {
@override
eat() {
print('小狗吃骨頭');
}
@override
run() {
print('小狗在跑');
}
}
class Cat extends Animal {
@override
eat() {
print('小貓在吃老鼠');
}
@override
run() {
print('貓在跑');
}
}
void main() {
Dog l = new Dog(); //抽象類的 子類可以範例化
l.eat();
l.run();
l.printInfo();
Cat c = new Cat();//抽象類的 子類可以範例化
c.eat();
c.run();
c.printInfo();
// Animal a = new Animal(); //抽象類不能被範例化 只有繼承它的子類可以範例化
}
子類的範例賦值給父類別的參照
//定義一個 Animal的抽象類
abstract class Animal {
eat(); // 抽象方法
printInfo() {
//抽象類中的普通方法
print("抽象類中的普通方法");
}
}
class Dog extends Animal {
@override
eat() {
print('小狗吃骨頭');
}
run() {
print('小狗在跑');
}
}
class Cat extends Animal {
@override
eat() {
print('小貓在吃老鼠');
}
run() {
print('貓在跑');
}
}
void main() {
Animal d = new Dog(); //子類的範例賦值給父類別的參照 多型
d.eat(); //小狗在跑
// d.run(); //會報錯
Animal c = new Cat();
c.eat(); //貓在跑
// c.run(); //會報錯
}
和Java 一樣,dart 也有介面 ,但是和java還是有區別的。
首先,dart的介面沒有interface關鍵字第一介面,而是普通類或抽象類都可以作爲介面被實現。
同樣使用implement 關鍵字進行實現。
但是dart的介面有點奇怪,如果實現的類是普通類,會將普通類和抽象類中的屬性的方法全部需要複寫一遍。
而因爲抽象類可以定義抽象方法,普通類不可以,所以一般如果要實現像java介面那樣的方式,一般會使用抽象類。
建議使用抽象類(定義標準)定義介面。
//定義一個DB庫 支援mysql mssql mongodb mysql mssql mongodb三個類裏面有同樣的方法
abstract class Db { //抽象類當做介面 定義標準
//介面
String uri; //數據庫的鏈接地址
add(String data);
save();
delete();
}
//實現Mysql介面 類似繼承標準
class Mysql implements Db {
@override
String uri;
Mysql(this.uri);
@override
add(data) {
print('這是mysql的add方法' + data);
}
@override
delete() {
// TODO: implement delete
throw UnimplementedError();
}
@override
save() {
// TODO: implement save
throw UnimplementedError();
}
}
//實現ms介面
class ms implements Db {
@override
String uri;
@override
add(data) {
// TODO: implement add
throw UnimplementedError();
}
@override
delete() {
// TODO: implement delete
throw UnimplementedError();
}
@override
save() {
// TODO: implement save
throw UnimplementedError();
}
}
void main() {
Mysql n = new Mysql('xxxxx');
print(n.uri);
n.add('馮上進');
}
abstract class A {
String name;
printA();
}
abstract class B {
printB();
}
class C implements A, B { //一個類繼承多個介面
@override
String name = "張三";
@override
printA() {
print(A);
}
@override
printB() {
print(B);
}
}
void main() {
C c = new C();
print(c.name);//張三
c.printA(); //A
c.printB();//B
}
類是不可以繼承的,但是在Dart中可以使用mixins實現類似多繼承的功能。
因爲mixins使用的條件,隨着Dart版本一直在變,這裏講的是Dart2.x中使用mixins的條件
class A {
String info = 'this is info';
A();//不能有建構函式
printA() {
print(A);
}
}
class B {
printB() {
print(B);
}
}
class Person {
String name;
int age;
Person(this.name, this.age);
printP() {
print('${this.name}---${this.age}');
}
}
//A和B兩個類 有相同的方法 那個類 ↓ 誰在後面 列印誰
class C extends Person with A, B { //繼承了 Person的 建構函式 又繼承了 Persond的屬性和方法
C(String name, int age) : super(name, age) {}
}
void main(){
C c = new C('張三',20);
c.printP(); //張三 ---20
}
class C with A, B {}//可以使用mixins實現類似多繼承的功能 通過 with繼承
void main() {
C c = new C();
print(c.info);//this is info
c.printA(); //A
c.printB();//B
print(c is C); true
print(c is A);true
print(c is B);true //c爲A和B的超類
A a = new A();
print(a is Object);true //所有的類都繼承自Object
}
通俗理解:泛型就是解決 類 介面 方法的複用性,以及對不特定數據型別的支援(型別校驗)。
不指定型別放棄了型別檢查。我們在想實現的是傳入什麼 返回什麼。比如:傳入number型別必須返回 num
T getData<T>(T value){ //對傳入和返回的參數做校驗 去除第一個 T 就是 不對返回參數做校驗
return value
}
void main(){
print(getData int(123))//123
print(getData String('xxx'))//xxx
}
系統內建泛型類 List
void main() {
List myList = new List<int>(); //<int>尖括號 裏面爲int型別新增的只能是數位 不能是別的型別
myList.add(123);
myList.add('123');//錯誤寫法
print(myList);
}
//泛型類
class Sum<T> {
List list = new List<T>();
void printS(T value) {
this.list.add(value);
}
}
void main() {
Sum s1 = new Sum<int>(); //範例化子類的型別爲int 所以list不能add字串。
s1.list.add(123);
s1.list.add("你好"); //報錯 因爲s1範例傳的;型別爲int型別。
print(s1.list);
}
Dart中的泛型介面
實現數據快取的功能:有檔案快取,和記憶體快取。記憶體快取和檔案快取按照介面實現。
// 泛型介面
abstract class Cache<T>{
getByKey( String key);
void setByKey(String key,T value);
}
abstract class Person<T> {
//泛型介面
getByKey(String key);
void setByKey(String key, T value);
}
class Son<T> implements Person<T> {
@override
getByKey(String key) {
print('123');
}
@override
void setByKey(String key, T value) {
print("${key} ----${value}");
}
}
void main() {
Son s = new Son<int>(); //要求setByKey 中的value的型別 與子型別範例化參數一致
s.setByKey('馮上進', 123);
}
在Dart中,庫的使用是通過import 關鍵字引入。
library指令可以建立一個庫,每個dart檔案都是一個庫,即使沒有使用library指令來指定。
Dart中的庫有三種:
我們自定義的庫 import ’lib/xxx.dart‘;
系統內建庫
Pub包管理系統中的庫
name:xxx
dessciption:A new flutter module project.
dependencies:
http:^0.12.0+2
date_format:^1.0.6
void main() async { //使用非同步方法 必須加 async await 纔可以使用非同步方法;
var result = await testAsync();
print(result);
}
//非同步方法
testAsync() async {
return '張三';
}
as 庫的重新命名
import 'lib/Person1.dart'
import 'lib/Person2.dart as lib'
main(List<String>args) {
Perosn p1 = new Person('張三',20);
p1.PrintInfo();
}
lib.Person p2 = new lib.Person('李四',20);
p2.PrintInfo();
部分匯入
import 'lib/myMath.dart' show getName; //只能用庫裏面的getName();
import 'lib/myMath.dart' hide getName; //隱藏庫裏面的getName();