Flutter基礎篇(2)-- 老司機用一篇博客帶你快速熟悉Dart語法

版權聲明:本文為博主原創文章,未經博主允許不得轉載。http://www.lxweimin.com/p/3d927a7bf020

轉載請標明出處:
http://www.lxweimin.com/p/3d927a7bf020
本文出自 AWeiLoveAndroid的博客


Flutter系列博文鏈接 ↓:

工具安裝:

Flutter基礎篇:

Flutter進階篇:

Dart語法系列博文鏈接 ↓:

Dart語法基礎篇:

Dart語法進階篇:


【前言】Dart語言是使用flutter框架開發時候必備的語言,flutter是一個跨平臺的框架,一套代碼就可以完美實現安卓和ios兩個平臺,適配也很不錯,Dart語言很友好,和java很類似,學習成本也是很低的。所以這也是我推薦學習Dart語言的一個原因。

從本篇文章開始講解Dart語言的基本使用,我將會連續推出好幾篇文章詳解,希望幫助大家快速掌握Dart語言。


本文目錄:

一、注釋
二、關鍵字(56個)
三、變量和常量
四、特殊數據類型
五、運算符
六、控制流程語句
七、異常


本文代碼同步發布在Github:
https://github.com/AweiLoveAndroid/Flutter-learning/tree/master/projects/dart_demo

怎么運行代碼?

如果你使用IDEA或者Android Studio:

打開IDEA或者Android Studio,新建一個Flutter項目,然后在test目錄運行我的代碼;或者里面去寫你自己的dart文件,然后右鍵run就可以運行了。(注意:需要連接手機或者模擬器)。

如果你使用vscode:

打開vscode,執行菜單欄運行,就可以了(確保只有一個dart文件,免得運行的文件不是你想要的,就很尷尬了,vscode也可以設置默認運行的文件是哪個,但是新手不建議去設置,很麻煩。因為你們想最快的運行效果,所有建議只有一個dart文件是最好的)。


一、注釋

Dart的注釋分為3種:單行注釋、多行注釋、文檔注釋。

1、單行注釋以//開頭。Dart編譯器會忽略//和行尾之間的所有內容。

例如:// todo:待完成

2、多行注釋以/*開頭,以*/結尾。介于/**/兩者之間的內容會被編譯器忽略(除非該注釋是一個文檔注釋)。多行注釋可以嵌套。

例如:/* todo:待完成 */

3、文檔注釋以///或者/**開頭。可以通過dartdoc命令導出文檔。

文檔注釋的使用,例如:/// todo:待完成

文檔的導出如圖所示:


文檔的導出

導出的結果在我的工程根路徑的/doc/api/文件夾里面,如圖所示:

導出的結果

然后瀏覽器打開index.html就可以看到文檔了。如圖所示:

本地的文檔

二、關鍵字(60個)

5個上下文關鍵字(僅在特定位置具有含義。它們在任何地方都是有效的標識符)

關鍵字 - - -
async hide on show
sync - - -

其中內置標志符有:(20個)

關鍵字 - - -
abstract as covariant defered
dynamic export external factory
Function get implements import
interface library mixin operator
part set static typedef

Dart新增的,有限的保留字,支持異步功能的關鍵字有:(2個)

關鍵字 - - -
await yield

33個保留字(不能使用保留字作為標識符)

關鍵字 - - -
assert break case catch
class const continue default
do else enum extends
false final finally for
if in is new
null rethrow return super
switch this throw true
try var void while
with - - -

跟java相比,Dart特有的關鍵字有:(27個)

關鍵字 - - -
as async await covariant
deferred dynamic export external
factory Function get hide
in is library mixin
on operator part rethrow
set show sync typedef
var with yield

三、變量和常量

(一)變量的聲明,可以使用 var、Object 或 dynamic 關鍵字。

創建變量并初始化變量實例:

var name = '張三' ;

變量存儲引用。

    1. 使用Object或dynamic關鍵字
dynamic name = '張三';

調用的變量name包含對String值為“張三” 的對象的引用。
name推斷變量的類型是String,但可以通過指定它來更改該類型。
如果對象不限于單一類型(沒有明確的類型),請使用Object或dynamic關鍵字

Object name = '張三';
dynamic name = '李四';
    1. 顯式聲明將被推斷的類型

比如String,int等。

//可以使用String顯示聲明字符串類型
String name = '張三' ; //代替var name = '張三';

這個類型有很多,具體在下文有介紹。

(二)默認值

未初始化的變量的初始值為null(包括數字),因此數字、字符串都可以調用各種方法。

//測試 數字類型的初始值是什么?
int intDefaultValue;
// assert 是語言內置的斷言函數,僅在檢查模式下有效
// 在開發過程中, 除非條件為真,否則會引發異常。(斷言失敗則程序立刻終止)
assert(intDefaultValue == null);
print(intDefaultValue);//打印結果為null,證明數字類型初始化值是null

(三)Final 和 Const的用法

如果您從未打算更改一個變量,那么使用 final 或 const,不是var,也不是一個類型。
一個 final 變量只能被設置一次;const 變量是一個編譯時常量。(Const變量是隱式的final。)
final的頂級或類變量在第一次使用時被初始化。

  • 1、被final或者const修飾的變量,變量類型可以省略。
//可以省略String這個類型聲明
final name1 = "張三";
//final String name1  = "張三";
    
const name2 = "李四";
//const String name2 = "李四";
  • 2、被 final 或 const 修飾的變量無法再去修改其值。
final name1 = "張三";
// 這樣寫,編譯器提示:a final variable, can only be set once
// 一個final變量,只能被設置一次。
//name1 = "zhangsan";
    
const name2 = "李四";

// 這樣寫,編譯器提示:Constant variables can't be assigned a value
// const常量不能賦值
// name2 = "lisi";
  • 3、注意:flnal 或者 const 不能和 var 同時使用
//這樣寫都會報錯
//final var name1 = "張三";
//const var name2 = "李四";
  • 4、常量如果是類級別的,請使用 static const
static const speed = 100;
  • 5、常量的運算
const speed = 100; //速度(km/h)
const double distance = 2.5 * speed; // 距離 = 速度 * 時間

final speed2 = 100; //速度(km/h)
final double distance2 = 2.5 * speed2; // 距離 = 速度 * 時間
  • 6、const關鍵字不只是聲明常數變量。您也可以使用它來創建常量值,以及聲明創建常量值的構造函數。 任何變量都可以有一個常量值。
// 注意: [] 創建的是一個空的list集合
// const []創建一個空的、不可變的列表(EIL)。
var varList = const []; // varList 當前是一個EIL
final finalList = const []; // finalList一直是EIL
const constList = const []; // constList 是一個編譯時常量的EIL

// 可以更改非final,非const變量的值
// 即使它曾經具有const值
varList = ["haha"];

// 不能更改final變量或const變量的值
// 這樣寫,編譯器提示:a final variable, can only be set once
// finalList = ["haha"];
// 這樣寫,編譯器提示:Constant variables can't be assigned a value  
// constList = ["haha"];
  • 7、只要任何插值表達式是一個計算結果為null或數字,字符串或布爾值的編譯時常量,那么文字字符串就是編譯時常量。(關于$表達式和不同的數據類型后面會講解。)
// 這些是常量字符串
const aConstNum = 0;
const aConstBool = true;
const aConstString = 'a constant string';

// 這些不是常量字符串

var aNum = 0;
var aBool = true;
var aString = 'a string';
const aConstList = const [1, 2, 3];

const validConstString = '$aConstNum $aConstBool $aConstString';

//這樣用就會報錯:Const variables must be initialized with a constant value
// const常量必須用conat類型的值初始化。
// const invalidConstString = '$aNum $aBool $aString $aConstList';

四、特殊數據類型

Dart支持以下特殊類型:

numbers 數字
strings 字符串
booleans 布爾
lists list集合(也稱為數組)
maps map集合
runes 字符(用于在字符串中表示Unicode字符)

(一)num數字類型

num是數字類型的父類,有兩個子類 intdouble
num類型包括基本的運算符,如+,-,/和*,位運算符,如>>,在int類中定義。如果num和它的子類沒有你要找的東西,math庫可能會找到。比如你會發現abs(),ceil()和floor()等方法。

(1)int類型

int表示整數,int默認是64位二進制補碼整數,int的取值不大于64位,具體取決于平臺。編譯為JavaScript時,整數僅限于valus,可以用雙精度浮點值精確表示。可用的整數值包括-253和253之間的所有整數,以及一些幅度較大的整數。這包括一些大于2^63的整數。 因此,在編譯為JavaScript的Dart VM和Dart代碼之間,int類中的運算符和方法的行為有時會有所不同。例如,當編譯為JavaScript時,按位運算符將其操作數截斷為32位整數。
示例如下:

int intNum1 = 10 ;
print(intNum1);//結果是10
int intNum2 = 0xDEADBEEF ;
print(intNum2);//結果是3735928559

判斷一個int值需要多少bit(位),可以使用bitLength,例如:

// bitLength 返回存儲此int整數所需的最小位數
int a1 = 1; // 占了1個bit     相當于二進制數字 00000000 00000001
int a2 = 12; // 占了4個bit    相當于二進制數字 00000000 00001100
int a3 = 123; // 占了7個bit   相當于二進制數字 00000000 01111011
int a4 = 1234; // 占了11個bit 相當于二進制數字 00000100 11010010
print('${a1.bitLength}'); //  1
print('${a2.bitLength}');  // 4
print('${a3.bitLength}'); // 7
print('${a4.bitLength}'); // 11
(2)double類型

Dart的double是IEEE 754標準中規定的64位浮點數。double的最大值是:1.7976931348623157e+308,double類里面有一個常量maxFinite,我們通過語句print(double. maxFinite)可以得到double的最大值
如果一個數字包含一個小數,那么它就是一個double類型。示例如下:

double doubleNum1 = 1.1;
print(doubleNum1); //結果是1.1
double doubleNum2 = 1.42e5;
print(doubleNum2); //結果是142000.0
(3)Dart2.1里面新增特性,當double的值為int值時,int自動轉成double。

例如:double test = 12;//打印結果是12.0

(4)Dart2.1,int也有api轉成double。

例如:

int test = 10;
print(test.toDouble()); // 結果是: 10.0
(5)Dart2.1,double也有api轉成int,會把小數點后面的全部去掉。

例如:

double test2 = 15.1;
double test3 = 15.1234;
print(test2.toInt());// 結果是15
print(test3.toInt());// 結果是15

(二)String字符串類型

Dart里面的String是一系列 UTF-16代碼單元。

(1)您可以使用單引號或雙引號來創建一個字符串。
String str1 = '單引號基本使用demo.';
String str2 = "雙引號基本使用demo.";
(2)單引號或者雙引號里面嵌套使用引號。

單引號里面嵌套單引號,或者//雙引號里面嵌套雙引號,必須在前面加反斜杠。

// 單引號里面有單引號,必須在前面加反斜杠
String str3 = '單引號里面有單引號it\'s,必須在前面加反斜杠.';
//雙引號里面嵌套單引號(正常使用)
String str4 = "雙引號里面有單引號it's.";
//單引號里面嵌套雙引號(正常使用)
String str5 = '單引號里面有雙引號,"hello world"';
//雙引號里面嵌套雙引號,必須在前面加反斜杠
String str6 = "雙引號里面有雙引號,\"hello world\"";

print(str3);// 雙引號里面有單引號it's,必須在前面加反斜杠
print(str4);// 雙引號里面有單引號it's.
print(str5);// 單引號里面有雙引號,hello world"
print(str6);//雙引號里面有雙引號,"hello world"
(3)多個字符串相鄰中間的空格問題:

除了單引號嵌套單引號或者雙引號嵌套雙引號不允許出現空串之外,其余的幾種情況都是可以運行的。
示例如下:

這個會報錯
//String blankStr1 = 'hello''''world';
//這兩個運行正常
String blankStr2 = 'hello'' ''world'; //結果: hello world
String blankStr3 = 'hello''_''world'; //結果: hello_world


// 這個會報錯
//String blankStr4 = "hello""""world";
這兩個運行正常
String blankStr5 = "hello"" ""world"; //結果: hello world
String blankStr6 = "hello""_""world"; //結果: hello_world

單引號里面有雙引號,混合使用運行正常

String blankStr7 = 'hello""""world'; //結果: hello""""world
String blankStr8 = 'hello"" ""world'; //結果: hello"" ""world
String blankStr9 = 'hello""_""world'; //結果: hello""_""world

雙引號里面有單引號,混合使用運行正常

String blankStr10 = "hello''''world"; //結果: hello''''world
String blankStr11 = "hello'' ''world"; //結果: hello'' ''world
String blankStr12 = "hello''_''world"; //結果: hello''_''world
(4)您可以使用相鄰字符串文字或+ 運算符連接字符串:
  1. 直接把相鄰字符串寫在一起,就可以連接字符串了。
  String connectionStr1 =  '字符串連接''甚至可以在''換行的時候進行。';
  print(connectionStr1);// 字符串連接甚至可以在換行的時候進行。
  1. 用+把相鄰字符串連接起來。
  String connectionStr2 =  '字符串連接'+ '甚至可以在'+ '換行的時候進行。';
  print(connectionStr2);// 字符串連接甚至可以在換行的時候進行。
  1. 使用單引號或雙引號的三引號:
String connectionStr3 = ''' 
  這是用單引號創建的
  多行字符串。
  ''' ;
print(connectionStr3);
String connectionStr4 = """這是用雙引號創建的
  多行字符串。""";
print(connectionStr4);

print(connectionStr3)輸出結果如下:

  這是用單引號創建的
  多行字符串。

print(connectionStr4)的輸出結果如下:

這是用雙引號創建的
  多行字符串。
(5)關于轉義符號的使用

聲明raw字符串(前綴為r),在字符串前加字符r,或者在\前面再加一個\
可以避免“\”的轉義作用,在正則表達式里特別有用。

舉例如下:

print(r"換行符:\n"); //這個結果是 換行符:\n
print("換行符:\\n"); //這個結果是 換行符:\n
print("換行符:\n");  //這個結果是 換行符:
(6)使用$可以獲得字符串中的內容,使用${表達式}也可以將表達式的值放入字符串中。使用${表達式}時可以使用字符串拼接,也可以使用String類或者Object里面的某些方法獲得相關字符串屬性。

1、使用$+字符串

var height = 48.0;
print('當前標題的高度是$height'); //當前標題的高度是48.0

2、使用$+字符串,以及字符串拼接

String name = "張三";
print("$name"+"是我們的部門經理"); // 張三是我們的部門經理

3、這里使用了字符串的拼接,以及使用了String類里面的toUpperCase()函數,把字母全部變成大寫。

String replaceStr = 'Android Studio';
assert('你知道' +
'${replaceStr.toUpperCase()}'
+ '最新版本是多少嗎?' ==
'你知道ANDROID STUDIO最新版本是多少嗎?'); 

注:==操作符測試兩個對象是否相等。assert是斷言,如果條件為true,繼續進行,否則拋出異常,中端操作。

(三)bool布爾類型

Dart表示布爾值的類型叫做bool,它有兩個值,分別是:truefalse,它們都是編譯時常量。
Dart使用的是顯式的檢查值,檢查值的類型,如下所示:

  // 檢查是否為空字符串
  var emptyStr = '';
  assert(emptyStr.isEmpty);

  // 檢查是否小于等于0
  var numberStr = 0;
  assert(numberStr <= 0);  

  // 檢查是否為null
  var nullStr;
  assert(nullStr == null);

  // 檢查是否為NaN
  var value = 0 / 0;
  assert(value.isNaN);

assert 是Dart語言里的的斷言函數,僅在Debug模式下有效
在開發過程中, 除非條件為真,否則會引發異常。(斷言失敗則程序立刻終止)。

(四)list集合,也成為數組

在Dart中,數組是List對象,因此大多數人只是將它們稱為List。
以下是一個簡單的Dart的List:

創建一個int類型的list

List list = [10, 7, 23];
print(list);// 輸出結果  [10, 7, 23]

要創建一個編譯時常量const的list,示例如下:

List constantList = const[10,3,15];
print(constantList);// 輸出結果  [10, 3, 15]

注意事項:

1.可以直接打印list包括list的元素,list也是一個對象。但是java必須遍歷才能打印list,java若直接打印list,結果是地址值。
2.和java一樣list里面的元素必須保持類型一致,不一致就會報錯。
3.和java一樣list的角標從0開始。

Dart的list集合給我們提供了很多api,示例如下,api太多就不逐個展示了:

操作 代碼 含義 輸出結果
新增 list.add(1);print(list); 把數字1添加到list中,默認是添加到末尾 [10, 7, 23, 1]
移除 list.remove(1);print(list); 移除數字1 [10, 7, 23]
插入 list.insert(0, 5);print(list); 在索引為0的地方插入數字5 [5, 10, 7, 23]
查找某個索引的值 int value = list.indexOf(10);print(value); 查找10在list中的索引 1
判斷元素是否包含 bool result = list.contains(5);print(result); 查找list中是否包含數字5 true

(五)map集合

Dart中的map是將鍵和值相關聯的對象。鍵和值都可以是任何類型的對象。每個鍵只出現一次,但您可以多次使用相同的值。

(1)創建方式:
    1. 直接聲明,用{}表示,里面寫key和value,每組鍵值對中間用逗號隔開。
Map companys = {'first': '阿里巴巴', 'second': '騰訊', 'fifth': '百度'};
print(companys);//打印結果 {first: 阿里巴巴, second: 騰訊, fifth: 百度}
    1. 先聲明,再去賦值。
  Map companys1 = new Map();
  companys1['first'] = '阿里巴巴';
  companys1['second'] = '騰訊';
  companys1['fifth'] = '百度';
  print(companys1);
  //打印結果 {first: 阿里巴巴, second: 騰訊, fifth: 百度}
    1. 要創建一個編譯時常量const的map,請在map文字之前添加const:
final fruitConstantMap = const {2: 'apple',10: 'orange',18: 'banana'};
// 打印結果{second: 騰訊, fifth: 百度, 5: 華為}
(2)添加元素。格式: 變量名[key] = value,其中key可以是不同類型。
//添加一個新的元素,key為“5”,value為“華為”
  companys[5] = '華為';
  print(companys);//打印結果 {first: 阿里巴巴, second: 騰訊, fifth: 百度, 5: 華為}
(3)修改元素。格式:變量名[key] = value

例如:把key為first的元素對應的value改成 alibaba

  companys['first'] = 'alibaba';
  print(companys);//打印結果 {first: alibaba, second: 騰訊, fifth: 百度, 5: 華為}
(4)查詢元素
  bool mapKey = companys.containsKey('second');
  bool mapValue = companys.containsValue('百度');
  print(mapKey); //結果為:true
  print(mapValue); //結果為:true
(5)刪除元素.可以使用map的remove或者clear方法。
  companys.remove('first');// 移除key為“first”的元素。
  print(companys);// 打印結果{second: 騰訊, fifth: 百度, 5: 華為}

  companys.clear();// 清空map集合的數據。
  print(companys);// 打印結果{}
(6)關于map集合的小結:
1.創建map有兩種方式。
2.map的key類型不一致也不會報錯。
3.添加元素的時候,會按照你添加元素的順序逐個加入到map里面,哪怕你的key不連續。
比如key分別是 1,2,4,看起來有間隔,事實上添加到map的時候{1:value,2:value,4:value} 這種形式。
4.添加的元素的key如果是map里面某個key的英文,照樣可以添加到map里面,
比如可以為3和key為three可以同時存在。
5.map里面的key不能相同,但是value可以相同,value可以為空字符串或者為null。

(六)runes 字符(用于在字符串中表示Unicode字符)

Unicode為世界上所有的書寫系統中使用的每個字母,數字和符號定義了唯一的數值。
Dart字符串是UTF-16代碼單元的序列,所以在字符串中表達32位Unicode值需要特殊的語法。
Unicode代碼點的常用方法是\uXXXX,其中XXXX是一個4位十六進制值。

例如,心形字符()是\u2665。要指定多于或少于4個十六進制數字,請將該值放在大括號中。 例如,笑的表情符號是\u{1f600}

String類有幾個屬性可以用來提取符文信息。 codeUnitAt和codeUnit屬性返回16位代碼單元。
以下示例說明了符文,16位代碼單元和32位代碼點之間的關系。

var clapping = '\u{1f44f}';
print(clapping);
print(clapping.codeUnits);
print(clapping.runes.toList());

//使用String. fromCharCodes顯示字符圖形 
Runes input = new Runes(
        '\u2665  \u{1f605}  \u{1f60e}  \u{1f47b}  \u{1f596}  \u{1f44d}');
print(new String.fromCharCodes(input));

五、運算符

運算符在每一種語言中都很常見,Dart的運算符如下表所示:

我這里不詳細去講解每個運算符的用法,我這里主要講一下Dart里面比較有代表性的以及有特點的一些運算符相關用法。

(一)?..一樣,但最左邊的操作數可以為空。

比如:Test?.funs從表達式Test中選擇屬性funs,除非Test為空(當Test為空時,Test?.funs的值為空)。

class Test {
  static int funs = 5;

  Test() {
    print('構造函數 Test');
  }
  static fun() {
    print('Test fun函數');
  }
}
void main(){
  print(Test?.funs); // 打印5
}

(二)..級聯符號..

級聯符號..允許您在同一個對象上進行一系列操作。 除了函數調用之外,還可以訪問同一對象上的字段。其實相當于java的鏈式調用。
例如:

String s = (new StringBuffer()
        ..write('test1 ')
        ..write('test2 ')
        ..write('test3 ')
        ..write('test4 ')
        ..write('test5'))
      .toString();
print(s); // test1 test2 test3 test4 test5

(三)?? 三目運算符的一種形式。

expr1 ?? expr2 表示如果expr1非空,則返回其值; 否則返回expr2的值。

//普通三元運算符
int a = 10;
var values = a > 5 ? a : 0;
//??操作符
print('a ??=3 : ${a ??= 3}');  // 3

(四)~/ 除法,返回一個整數結果,其實就是取商。

小學都學過:被除數 ÷ 除數 = 商 ... 余數,在Dart里面A ~/ B = C,這個C就是商,這個語句相當于Java里面的A / B = C。Dart與java不同的是,Dart里面如果使用A / B = D語句,這個結果計算出來的是真實的結果。示例如下:

  var result1 = 15/7;
  print(result1); // 結果是:2.142857...
  var result2 = 15~/7;
  print(result2); // 結果是:2

順便提一下取模操作,在Dart里面A % B = E,這個E就是余數,%符號表示取模,例如:

 var result3 = 15%7;
  print(result3); // 結果是:1

(五)as、is與is!

as 判斷屬于某種類型
is 如果對象具有指定的類型,則為true
is! 如果對象具有指定的類型,則為false

例如:

class Test {
  static int funs = 5;

  Test() {
    print('構造函數 Test');
  }
  static fun() {
    print('Test fun函數');
  }
}

class Test2 extends Test {
  Test2() {
    print('構造函數 Test2');
  }
  void fun() {
    print('Test2 fun函數');
  }
}

void main(){
  print(test2 is Test);  // true
  print(test is! Test2);  // true

  (test2 as Test2).fun();  // Test2 fun函數
  // 相當于
  // if (test2 is Test) {
  //   test2.fun();
  // }

六、控制流程語句

控制流程語句和Java語言差不多,有這些語句:

(一)if else

if(條件語句){
    內容體
}else{
內容體
}

(二)for循環

1.簡單for循環

for(初始值;判斷條件;循環后的語句){
    內容體
}

例如:

for(int i=0;i<10;i++){
    print(i);
}

也可以通過for循環內部的閉包獲取索引的值。

var array = [];
for(var i=0;i<10;i++){
    array.add(()=> print(i));
}

2.使用foreach循環,一般List和Set都可以使用foreach遍歷元素。

如果要迭代的對象是Iterable,或者你不想知道當前的迭代次數,可以使用foreach()方法。

var numbers = [1,2,3,4,5,6,7,8,9];
numbers.foreach((number)=> print(number));

3.使用for in循環,一般List和Set使用for-in遍歷元素。

var list = [1,2,3];
for(var data in list){
    print(data);
}

4.Dart的for循環里面可以使用標記:(比較有特色的地方)

Dart的標記:標記是后面跟著冒號的標識符。帶標記的陳述是以標記 L為前綴的陳述。帶標簽的case子句是標簽L前綴的switch語句中的case子句。標簽的唯一作用是為“break”和“continue”聲明提供對象。
大多數此功能與其他語言類似,因此以下大部分內容可能對讀者來說都很熟悉。Dart的switch聲明中處理continue是比較獨特的,所以這一部分需要一點時間去閱讀和熟悉。

  • 1.循環(Loops)

標簽最常用作breakcontinue內部循環。假設你有嵌套的循環,并要跳轉到breakcontinue到外部循環。如果沒有標記,這不可能(輕松)實現。

以下示例使用continue 標記名稱從內部循環直接跳轉到外部循環的下一輪循環:

// 返回具有最小總和的內部列表(正整數)。
/// Returns the inner list (of positive integers) with the smallest sum.
List<int> smallestSumList(List<List<int>> lists) {
  var smallestSum = 0xFFFFFFFF; //已知list的總和較小。
  var smallestList = null;
  outer: // 這就是標記
  for (var innerList in lists) {
    var sum = 0;
    for (var element in innerList) {
      assert(element >= 0);
      sum += element;
      // 無需繼續迭代內部列表。它的總和已經太高了。
      if (sum > smallestSum) continue outer; // continue 跳出到標記處(outer)
    }
    smallestSum = sum;
    smallestList = innerList;
  }
  return smallestList;
}

此函數在所有list中運行,但只要總和過高,就會停止累加變量。

同理,可以使用break跳出到外部循環:

// 計算第一個非空list
List<int> firstListWithNullValueList(List<List<int>> lists) {
  var firstListWithNullValues = null;
  outer:
  for (var innerList in lists) {
    for (var element in innerList) {
      if (element == null) {
        firstListWithNullValues = innerList;
        break outer;  // break 返回到標記處
      }
    }
  }
  // 現在繼續正常的工作流程
  if (firstListWithNullValues != null) {
    // ...
  }
  return firstListWithNullValues;
}
  • 2.跳出代碼塊

標記也可以用于跳出代碼塊。假設我們想要統一處理錯誤條件,但有多個條件(可能是深度嵌套)來揭示(reveal)錯誤。標簽可以幫助構建此代碼。

void doSomethingWithA(A a) {
  errorChecks: {
    if (a.hasEntries) {
      for (var entry in a.entries) {
        if (entry is Bad) break errorChecks;   // 跳出代碼塊
      }
    }
    if (a.hasB) {
      var b = new A.b();
      if (b.inSomeBadState()) break errorChecks;  // 跳出代碼塊
    }
    // 一些看起來都正常
    use(a);
    return;
  }
  // 錯誤的情況,執行這里的代碼:
  print("something bad happened");
}

class A{
  bool hasEntries = false;
  bool hasB = true;
  List<Bad> entries = [new Bad2(),new Bad2()];
  A.b(){

  }

  bool inSomeBadState(){
    return false;
  }
  
}

void use(A a){}

abstract class Bad{}
class Bad1 extends Bad{}
class Bad2 extends Bad{}

對代碼塊的使用break指令,使得Dart繼續執行塊之后的語句。從某個角度來看,它是一個結構化的goto,它只允許跳轉到當前指令之后的嵌套較少的位置。

雖然聲明標簽在代碼塊中最有用,但它們可以用在在每個語句中。
例如,foo: break foo;是一個有效的聲明。

請注意:continue上面的循環可以通過將循環體包裝到帶標記的代碼塊中并使用break來實現。
也就是說,以下兩個循環是等效的:

//以下兩種描述是等價的:

// 使用 continue
for (int i = 0; i < 10; i++) {
  if (i.isEven) continue;
  print(i);
}

// 使用 break.
for (int i = 0; i < 10; i++) {
  labels: {
    // isEven 當且僅當該整數為偶數時才返回true
    if (i.isEven) break labels;
    print(i);
  }
}
  • 3.Switch中的標記(label)

標記也可以用于switch內部。
Switch中的標記允許continue 使用其它的case 子句。在最簡單的形式中,這可以作為一種方式來實現下一個子句:

void switchExample(int foo) {
  switch (foo) {
    case 0:
      print("foo is 0");
      break;
    case 1:
      print("foo is 1");
      continue shared; // Continue使用在被標記為shared的子句中
    shared:
    case 2:
      print("foo is either 1 or 2");
      break;
  }
}

有趣的是, Dart沒有要求continue的目標子句是當前case子句之后的子句。
帶標記的任何case子句都是有效的目標。這意味著,Dart的switch語句實際上是狀態機(state machines)。

以下示例演示了這種濫用,其中整個switch實際上只是用作狀態機(state machines)。

void main() {
  runDog();
}

void runDog() {
  int age = 0;
  int hungry = 0;
  int tired = 0;

  bool seesSquirrel() => new Random().nextDouble() < 0.1;
  bool seesMailman() => new Random().nextDouble() < 0.1;

  switch (1) {
    start:
    case 0:
      print("dog 方法已經開始");
      print('case 0 ==> age: $age');
      print('case 0 ==> hungry: $hungry');
      print('case 0 ==> tired: $tired');
      continue doDogThings;

    sleep:
    case 1:
      print("sleeping");
      tired = 0;
      age++;
      if (age > 20) break;
      print('case 1 ==> age: $age');
      print('case 1 ==> hungry: $hungry');
      print('case 1 ==> tired: $tired');
      continue doDogThings;

    doDogThings:
    case 2:  
      if (hungry > 2) continue eat;
      if (tired > 3) continue sleep;
      if (seesSquirrel()) continue chase;
      if (seesMailman()) continue bark;
      print('case 2 ==> age: $age');
      print('case 2 ==> hungry: $hungry');
      print('case 2 ==> tired: $tired');
      continue play;

    chase:
    case 3:  
      print("chasing");
      hungry++;
      tired++;
      print('case 3 ==> age: $age');
      print('case 3 ==> hungry: $hungry');
      print('case 3 ==> tired: $tired');
      continue doDogThings;

    eat:
    case 4:  
      print("eating");
      hungry = 0;
      print('case 4 ==> age: $age');
      print('case 4 ==> hungry: $hungry');
      print('case 4 ==> tired: $tired');
      continue doDogThings;

    bark:
    case 5: 
      print("barking");
      tired++;
      print('case 5 ==> age: $age');
      print('case 5 ==> hungry: $hungry');
      print('case 5 ==> tired: $tired');
      continue doDogThings;

    play:
    case 6: 
      print("playing");
      tired++;
      hungry++;
      print('case 6 ==> age: $age');
      print('case 6 ==> hungry: $hungry');
      print('case 6 ==> tired: $tired');
      continue doDogThings;
  }
}

這個函數從一個switch子句跳到另一個子句,模擬了狗的生命。
在Dart中,標簽只允許在case子句中使用,因此我必須添加一些case永遠不會到達的行。

這個功能很酷,但很少使用。由于我們的編譯器增加了復雜性,我們經常討論它的刪除。到目前為止,它已經在我們的審查中幸存下來,但我們最終可能會簡化我們的規范并讓用戶自己添加一個while(true)循環(帶有標記)。這個dog的示例可以重寫如下:

var state = 0;
loop:
while (true)
  switch (state) {
    case 0:
      print("dog has started");
      state = 2; continue;

    case 1:  // sleep.
      print("sleeping");
      tired = 0;
      age++;
      // The inevitable... :(
      if (age > 20) break loop;  // 跳出循環
      // Wake up and do dog things.
      state = 2; continue;
    
    case 2:  // doDogThings.
      if (hungry > 2) { state = 4; continue; }
      if (tired > 3) { state = 1; continue; }
      if (seesSquirrel()) { state = 3; continue; }
      ...

如果狀態值被命名為常量,那么它將與原始版本一樣具有可讀性,但不需要switch語句來支持狀態機。

(三)while 和do while

while(判斷條件){
    內容體
}
do{
內容體
} while(判斷條件);
while(a>5){
    print(a);
}
do{
print(a);
} while(a>5);

(四)break continue

break 停止循環

while(a>5){
  if(b>5){
  print(a);
    break;
  }
}

continue 跳到下一個循環

while(a>5){
  if(b<10){
  print(b);
    continue;
  }
}

如果使用Iterable(list或者set),則可以使用下面這種方式:

// 第一個是滿足條件就進入  第二個是foreach遍歷
arrays
  .when((c)=>c.counts >=5)
  .foreach((c)=>c.next());

(五)switch case

比較integer, string,編譯時常量 使用==。比較對象必須是同一個類的實例(不是其子類的實例),并且該類不能重寫==。枚舉類型在switch也可以運行。
每一條非空case字子句以break結束,也可以使用其他的方式結束:continue,throw或者return

var command = 'OPEN';
switch (command) {
  case 'CLOSED':
    executeClosed();
    break;
  case 'PENDING':
    executePending();
    break;
  case 'APPROVED':
    executeApproved();
    break;
  case 'DENIED':
    executeDenied();
    break;
  case 'OPEN':
    executeOpen();
    break;
  default:
    executeUnknown();
}

(六)assert

如果布爾條件為false,則使用assert語句來中斷正常執行。例如:

// 確保變量具有非空值 
assert(text != null);
// 確保值小于100
assert(number < 100);
// 確保這是一個 https 網址
assert(urlString.startsWith('https'));

要將消息附加到斷言,請添加一個字符串作為第二個參數。

assert(urlString.startsWith('https'),'URL ($urlString) should start with "https".');

上例中assert的第一個參數可以是任何解析為布爾值的表達式。如果表達式的值為true,則斷言成功并繼續執行。如果為false,則斷言失敗并拋出異常


七、異常

Dart代碼可以拋出并捕獲異常。Exception是指示發生意外事件的錯誤。如果未捕獲異常,則會暫停引發異常的isolate ,并且通常會終止isolate及其程序。

與Java相比,Dart的所有異常都是未經檢查的異常。方法不會聲明它們可能引發的異常,并且您不需要捕獲任何異常。

Dart提供了ExceptionError 類型,以及許多預定義的子類型。當然,您可以定義自己的Exception。但是,Dart程序可以拋出任何非null對象,作為Exception(不僅僅是Exception和Error對象)。

(一)throw

以下是拋出或引發異常的示例:

throw FormatException('Expected at least 1 section');

你也可以拋出任意對象,例如:throw '格式不正確!';
通常在開發中會拋出Error或者Exception類型。

因為拋出異常是一個表達式,所以可以在=>語句中以及允許表達式的任何其他地方拋出異常:

void distanceTo(Point other) => throw UnimplementedError();   

(二)try catch

捕獲或捕獲異常會阻止異常傳遞(除非您重新拋出異常)。捕獲異常使您有機會處理它:

try {
    breedMoreLlamas();
} on OutOfLlamasException {
    buyMoreLlamas();
}

要處理可能拋出多種類型異常的代碼,可以指定多個catch子句。與拋出對象的類型匹配的第一個catch子句處理異常。如果catch子句未指定類型,則該子句可以處理任何類型的拋出對象。
您可以使用on或catch兩者兼而有之。使用on時需要指定異常類型。使用catch時,你的異常處理程序需要異常對象。
示例:

try {
  breedMoreLlamas();
} on OutOfLlamasException {
  // A specific exception
  buyMoreLlamas();
} on Exception catch (e) {
  // Anything else that is an exception
  print('Unknown exception: $e');
} catch (e) {
  // No specified type, handles all
  print('Something really unknown: $e');
}

您可以指定一個或兩個參數catch()。第一個是拋出的異常,第二個是堆棧跟蹤(StackTrace對象)。
示例:

try {
  // ···
} on Exception catch (e) {
  print('Exception details:\n $e');
} catch (e, s) {
  print('Exception details:\n $e');
  print('Stack trace:\n $s');
}

要部分處理異常,同時允許它傳遞,請使用rethrow關鍵字。
示例:

void misbehave() {
  try {
    dynamic foo = true;
    print(foo++); // 運行時異常
  } catch (e) {
    print('misbehave() partially handled ${e.runtimeType}.');
    rethrow; // 允許調用者查看exception.
  }
}

void main() {
  try {
    misbehave();
  } catch (e) {
    print('main() finished handling ${e.runtimeType}.');
  }
}

(三)finally

無論是否拋出異常,要確保某些代碼運行,請使用finally子句。如果沒有catch子句匹配該異常,則在finally子句運行后傳遞異常。
示例:

try {
  breedMoreLlamas();
} finally {
  // 即使拋出異常  也會執行這句代碼.
  cleanLlamaStalls();
}
該finally子句在任何匹配的catch子句之后運行:
try {
  breedMoreLlamas();
} catch (e) {
    // 首先會處理異常
  print('Error: $e'); 
} finally {
  // 然后執行這句語句
  cleanLlamaStalls(); 
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,739評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,702評論 2 370

推薦閱讀更多精彩內容