Dart 入門教程

[TOC]git

1、開篇

dart 語言具備以下特性github

  • 一切變量皆是對象,每一個對象都是類的實例。int、double、函數、 null 等都是對象,全部對象都繼承自 Object 類
  • dart 是強類型語言,但因爲具有類型推導功能因此類型聲明是可選的
  • dart 支持頂級函數、靜態函數、實例函數,也容許在函數中嵌套函數,即局部函數。相似的,dart 也支持頂級變量、靜態變量和實例變量
  • dart 沒有關於 public、protected、private 的關鍵字。經過爲變量標識符添加下劃線前綴,代表該標識符對其庫是私有的
  • ....

先來看個小例子編程

/** * 多行註釋 */
void printString(String msg) {
  print("msg value: $msg");
}

void main() {
  var msg = "Hello, World!";
  printString(msg); //msg value: Hello, World!
  printString(null); //msg value: null
}
複製代碼

如上代碼包含了 dart 語言(也是基本全部編程語言)的基本元素markdown

  • 多行註釋和單行註釋
  • 以分號結尾且必需有
  • 容許定義頂層函數
  • 最基礎的數據類型之一:String,其它的內置數據類型還有 int 、double、list、map 等
  • 類型推導。經過關鍵字 var 來聲明變量而無需指明變量類型
  • 一種方便的插入變量值的方式,字符串字面值:$msg
  • 應用程序的入口:main 函數

2、變量

2.一、變量聲明

與 Java 語言相比,dart 語言包含的相似的基本數據類型只有 intdouble 兩種,且這兩種類型的變量均是對象,其默認值均爲 null編程語言

本教程遵循官方風格指南建議,大部分例子都是使用 var 來聲明變量ide

void main() {
  int value;
  print(value); //null

  value = 10;
  print(value); //10

  var varValue = 20;
  print(varValue); //20
}
複製代碼

dart 語言是強類型語言,沒法將一個已聲明具體變量類型的變量賦值爲另外一個無繼承關係的變量函數

例如,第二行代碼是會致使報錯的,沒法將一個 double 值賦值到一個 int 類型變量上學習

int intValue = 20;
  intValue = 20.0; //error
複製代碼

但因爲 intdouble 類都是 num 類的子類,因此如下操做是合法的ui

num numValue = 10;
  print(numValue.runtimeType); //int
  numValue = 10.22;
  print(numValue.runtimeType); //double
複製代碼

2.二、dynamic

dynamic 相似於 Java 中的 Objectdynamic 對象能夠用來指向任意類型變量,非 null 的 dynamic 變量會有具體的運行時類型this

dynamic value = "leavesC";
  print(value.runtimeType); //String
  value = 12121;
  print(value.runtimeType); //int
複製代碼

2.三、final 和 const

若是你但願一個變量在賦值後其引用不能再改變,能夠經過 final 或 const 這兩個關鍵字來實現。const 變量表明的是編譯時常量,在編譯期,程序運行前就有肯定值了,所以實例變量不能使用 const 修飾。而 final 修飾的變量是運行時常量,能夠在運行時再賦予變量值,所以實例變量能使用 final 修飾

void main() {
  const URL = "https://github.com/leavesC/JavaKotlinAndroidGuide";
  var booleValue = true;
  final name = getName(booleValue);
  print(name);
}

String getName(boolValue) {
  if (boolValue) {
    return "leavesC";
  } else {
    return "leavesC =_=";
  }
}
複製代碼

3、內建類型

3.一、num

dart 的數字類型有 int 和 double 兩種,這兩種都是 num 類的子類

int 類型根據平臺的不一樣,整數值不大於64位。在 Dart VM 上,值能夠從 -263 到 263-1,編譯成 JavaScript 的 Dart 使用JavaScript 代碼,容許值從 -253 到 253 - 1

double 類型即64位雙精度浮點數,由 IEEE 754標準 指定

void main() {
  var intValue = 100;
  print(intValue.runtimeType); //int

  var doubleValue = 100.0;
  print(doubleValue.runtimeType); //double

  num numValue = 100;
  print(numValue.runtimeType); //int
  numValue = 100.0;
  print(numValue.runtimeType); //double
}
複製代碼

一些常見的數字類型轉換方法

print(num.parse("2000"));
  print(int.parse("200"));
  print(double.parse("121"));
複製代碼

3.二、string

除了能夠經過單引號或者雙引號來聲明一個字符串外,也能夠經過相鄰的字符串字面量來聲明一個組合字符串(至關於使用 + 把字符串相加爲一個總體)

var stringValue = "leavesC";

  var stringValue2 = 'leavesC =_=';

  var stringValue3 = "分段 "
      "換行了也能夠"
      '又換了一行';

  print(stringValue3); //分段 換行了也能夠又換了一行
複製代碼

此外,也可使用帶有單引號或雙引號的三重引號,包含的轉義字符將會生效

var stringValue4=''' \n 換行符 \t 製表符 ''';
複製代碼

也能夠用 r前綴 建立一個原始字符串,包含的轉義字符將會失效

var stringValue4=r''' \n 換行符 \t 製表符 ''';
複製代碼

3.三、bool

dart 語言也用 bool 關鍵字來表示事物的真假,只有兩個對象具備 bool 類型:true 和 false,它們都是編譯時常量。且 dart 是強 bool 類型檢查,只有 bool 類型的值是 true 才被認爲是 true

3.四、list

list 也是最多見的數據類型之一,dart 經過方括號來聲明 list 變量

因爲 dart 具備類型推導功能,所以 listValue 自動被賦予爲 List 類型,所以在聲明 listValue 後就沒法爲其添加其餘類型變量的值了

var listValue = [1, 2, 3];
  // listValue.add("4"); error
  print(listValue.runtimeType); //List<int>
複製代碼

若是想要爲 List 添加不一樣數據類型的變量,則須要直接指明數據類型爲 Object

var listValue = <Object>[1, 2, 3];
  listValue.add("4");
  print(listValue.runtimeType); //List<Object>
複製代碼

大多數時候爲了限制 List 的可存儲數據類型,在使用時就直接指明數據類型

var intList = <int>[1, 2, 3, 4];
複製代碼

若是在聲明 List 時調用了其指定集合大小的構造函數,則集合大小就是固定的了,列表的長度不能在運行時更改

var list = List<int>(2);
  list.add(2); //error,會致使拋出 Unsupported operation: Cannot add to a fixed-length list
複製代碼

要建立一個編譯時常量列表,則在列表字面量以前添加 const 關鍵字

var constantList = const [1, 2, 3];
  //error,可正常編譯,但會致使運行時拋出異常
  //constantList[0] = 2;
  //constantList.add(2);
複製代碼

3.五、set

Set 是一種不包含重複數據的數據集合,使用方式上和 List 基本相似

void main() {
  var list = [1, 2, 2, 3, 4, 5, 5];
  var set = Set.from(list);
  print(set); //{1, 2, 3, 4, 5}
}
複製代碼

3.六、map

map 是一個關聯鍵和值的數據類型,鍵和值能夠是任何類型的對象

void main() {
  var mapValue = {"name": "leavesC", "age": 24};
  mapValue["url"] = "https://github.com/leavesC";
  print(mapValue); //{name: leavesC, age: 24, url: https://github.com/leavesC}
  print(mapValue.length); //3
  print(mapValue.runtimeType); //_InternalLinkedHashMap<String, Object>
}
複製代碼

也能夠限定 map 能夠存儲的數據類型

var mapValue = <String, String>{"name": "leavesC"};
複製代碼

與 list 相似,要建立一個編譯時常量的 map 須要在 map 的字面量前加上 const 關鍵字

var mapValue = const {"name": "leavesC", "age": 24};

  //error,可正常編譯,但會致使運行時拋出異常
  mapValue["name"] = "hi";
複製代碼

4、函數

dart 是一種真正的面嚮對象語言,因此即便函數也是對象,即變量能夠指向函數,也能夠將函數做爲參數傳遞給其餘函數

4.一、通常概念

通常,爲了方便調用者理解,函數須要指明其接受的參數類型,但也容許不指明參數類型,此時函數依然能夠正常調用

void main() {
  printMsg("leavesC");

  printMsg2(100);
  printMsg2("leavesC");
}

void printMsg(String msg) {
  print(msg);
}

void printMsg2(msg) {
  print(msg);
}
複製代碼

若是函數只包含一個表達式,則可使用簡寫語法

void printMsg3(msg) => print(msg);
複製代碼

全部函數均有返回值,若是沒有指明函數的返回值類型,則函數默認返回 null

void main() {
  print(printValue(121) == null); //true
}

printValue(value) {
  print("value is: $value");
}
複製代碼

4.二、函數也是對象

在 dart 中,能夠用變量來引用函數對象、向函數傳遞函數參數、建立函數對象

void main() {
  var printUserFun = printName;
  printUserFun("leavesC"); //name: leavesC

  var list = ["leavesC", "葉"]; 
  list.forEach(printName); //name: leavesC name: 葉

  var sayHelloFun = (String name) => print("$name , hello");
  sayHelloFun("leavesC"); //leavesC , hello
}

void printName(String name) {
  print("name: $name");
}
複製代碼

4.三、位置參數

位置參數即該參數可傳也可不傳,當不傳時該參數值默認爲 null,位置參數用中括號包裹起來

void main() {
  printUser("leavesC"); //name: leavesC, age: null
  printUser("leavesC", 25); //name: leavesC, age: 25
}

void printUser(String name, [int age]) {
  print("name: $name, age: $age");
}
複製代碼

4.四、命名參數

命名參數,即在調用該函數時需同時標明該參數的參數名,命名參數用花括號包裹起來,以 {type paramName} 或者 {paramName: type} 兩種方式聲明參數,調用命名參數時只能以 funcName(paramName: paramValue) 的形式來調用。且命名參數可傳也可不傳值,當不傳指時該參數值爲 null

void main() {
  printUser("leavesC"); //name: leavesC, age: null
  printUser("leavesC", age: 25); //name: leavesC, age: 25
  printUser2("leavesC", age: 25); //name: leavesC, age: 25
}

void printUser(String name, {int age}) {
  print("name: $name, age: $age");
}

void printUser2(String name, {age: int}) {
  print("name: $name, age: $age");
}
複製代碼

4.五、默認參數值

和 kotlin 相似,dart 語言也支持爲位置函數和命名參數設置默認值,默認值必須是編譯時常量,若是沒有提供默認值,則默認值爲 null

void main() {
  printUser("leavesC"); //name: leavesC, age: 30
  printUser("leavesC", 25); //name: leavesC, age: 25

  printUser2("leavesC"); //name: leavesC, age: 30
  printUser2("leavesC", age: 25); //name: leavesC, age: 25
}

void printUser(String name, [int age = 30]) {
  print("name: $name, age: $age");
}

void printUser2(String name, {int age = 30}) {
  print("name: $name, age: $age");
}
複製代碼

4.六、函數變量

前面說了,dart 是一種真正的面嚮對象語言,即便函數也是對象,即變量能夠賦予函數類型

void main() {
  var printFunction = printUser;
  printFunction("leavesC");
  print(printFunction); //Closure: (String, [int]) => void from Function 'printUser': static.
}

void printUser(String name, [int age = 30]) {
  print("name: $name, age: $age");
}
複製代碼

也能夠將函數做爲參數傳遞給另一個函數

void main() {
  var list = {1, 2, 3};
// value is: 1
// value is: 2
// value is: 3
  list.forEach(printValue);
}

void printValue(value) {
  print("value is: $value");
}
複製代碼

4.七、匿名函數

匿名函數即不具有函數名稱的函數,在函數只使用一次會就再也不調用時使用匿名函數會比較方便

void main() {
  var list = {1, 2, 3};
// value is: 1
// value is: 2
// value is: 3
  list.forEach((element) {
    print("value is: $element");
  });
  list.forEach((element) => print("value is: $element"));
}
複製代碼

4.八、局部函數

局部函數即嵌套於其餘函數內部的函數

void main() {
  var list = {1, 2, 3};
// value is: 2
// value is: 3
// value is: 4
  list.forEach((element) {
    int add(int value1, int value2) {
      return value1 + value2;
    }
    print("value is: ${add(element, 1)}");
  });
}
複製代碼

5、運算符

dart 提供了一些比較簡便的運算符來簡化操做,大部分和 Java 相同,如下介紹下幾個不一樣的運算符

void main() {
  //is 用於判斷變量是不是指定的數據類型
  //is! 含義是 is 取反
  var strValue = "leavesC";
  print(strValue is String); //true
  print(strValue is int); //false
  print(strValue is! String); //false

  // ~/ 用於除法運算時取整,/ 則不取整
  print(10 / 3); //3.3333333333333335
  print(10 ~/ 3); //3

  //as 用於強制類型轉換
  num numValue = 10;
  int intValue = numValue as int;

  //若是 ??= 左邊的變量值爲 null ,則將其賦值爲右邊的值
  var name = null;
  var age = 25;
  name ??= "leavesC";
  age ??= 30;
  print("name:$name"); //name:leavesC
  print("age: $age"); //age: 25

  //若是 ?. 左邊的變量值不爲 null,則右邊的操做生效
  //用於避免發生空指針異常
  var area = null;
  print(area?.runtimeType); //null

  //級聯運算符 .. 用於連續操做某個對象,而無需每次操做時都調用該對象
  var list = [1, 2, 3, 4, 5];
  list
    ..insert(0, 6)
    ..removeAt(4)
    ..add(7);
  print(list); //[6, 1, 2, 3, 5, 7]

  //擴展運算符 ... 和 空值感知擴展運算符 ...?
  //提供了一種將多個元素插入集合的簡潔方法
  var list1 = [1, 2, 3];
  var list2 = [0, ...list1];
  print(list2); //[0, 1, 2, 3]
  //若是 list3 可能爲 null,此時則須要使用空值感知擴展運算符,不然會拋出異常
  //空值感知擴展運算符只有當 list3 不爲 null 時纔會執行插值操做
  var list3 = null;
  var list4 = [0, ...?list3];
  print(list4); //[0]
}
複製代碼

6、流程控制

dart 的流程控制的語義和邏輯大多和 Java 相同

void main() {
  //if
  int value = 20;
  if (value < 10) {
    print("value < 10");
  } else if (value < 30) {
    print("value < 30");
  } else {
    print("value unknown");
  }

  //while
  int score = 10;
  while (score < 100) {
    score++;
  }

  //switch
  String strValue = "leavesC";
  switch (strValue) {
    case "ye":
      break;
    case "leavesC":
      print(strValue);
      break;
    default:
      print("default");
      break;
  }

  //for
  var list = [];
  for (int index = 1; index < 10; index++) {
    list.add(index.toString());
  }
  for (var item in list) {
    print("循環遍歷:$item");
  }
}
複製代碼

此外也有一些比較奇特的操做,能夠經過條件 if循環 for 來構建集合

var hasName = true;
  var info = {if (hasName) "name": "leavesC", "age": 24};
  print(info); //{name: leavesC, age: 24}
複製代碼
var list = {1, 2, 3};
  var info = {for (var item in list) "$item", 4};
  print(info); //{1, 2, 3, 4}
複製代碼

7、枚舉

枚舉用於定義命名常量值,使用enum關鍵字來聲明枚舉類型

enum State { RESUME, STOP, PAUSE }

void main() {
  var state = State.PAUSE;
  print(state);
  State.values.forEach((state) {
    print(state);
  });
}
複製代碼

8、異常處理

dart 語言能夠拋出和捕獲異,捕獲異常能夠避免程序運行崩潰,與 Java 不一樣,dart 的全部異常均是未檢查異常,方法沒必要聲明自己可能拋出的異常,也不要求調用方捕獲任何異常。dart 提供了 Exception 和 Error 類型,以及許多預約義的子類型,開發者也能夠本身定義異常。並且,dart 能夠拋出任何對象,不只僅是 Exception 和 Error 兩類

例如,以下代碼就表示 throwException 方法內部捕獲了 RangeError ,但對其餘異常不進行處理

void throwException() {
  try {
    List<String> stringList = new List();
    stringList.add("value");
    print('${stringList[1]}');
  } on RangeError {
    print("拋出了異常...");
  }
}

void main() {
  throwException();
}
複製代碼

若是在拋出異常時須要異常對象,則須要用到 catch

void throwException() {
  try {
    List<String> stringList = new List();
    stringList.add("value");
    print('${stringList[1]}');
  } on RangeError catch (e) {
    print("${e.message}"); //Invalid value
    print("${e.runtimeType}"); //RangeError
  } catch (e) {
    print("若是異常沒有被上方捕獲,則會統一被此處捕獲");
  }
}
複製代碼

也可使用兩個參數的寫法,第二個參數表明堆棧跟蹤(StackTrace對象)

void throwException() {
  try {
    List<String> stringList = new List();
    stringList.add("value");
    print('${stringList[1]}');
  } on RangeError catch (e, s) {
    print("${s.toString()}");
  }
}
複製代碼

也能夠在捕獲異常後將異常再次拋出

void throwException() {
  try {
    List<String> stringList = new List();
    stringList.add("value");
    print('${stringList[1]}');
  } on RangeError catch (e, s) {
    print("${s.toString()}");
    rethrow;
  }
}
複製代碼

相似 Java,finally用於確保在拋出異常時某些代碼也能夠被執行

void throwException() {
  try {
    List<String> stringList = new List();
    stringList.add("value");
    print('${stringList[1]}');
  } on RangeError catch (e, s) {
    print("throwException ${e.message}");
    rethrow;
  } finally {
    print("finally");
  }
}

void main() {
  try {
    throwException();
  } catch (e) {
    print("main ${e.message}");
  }
// throwException Invalid value
// finally
// main Invalid value
}
複製代碼

此外,dart 也容許 throw 任何對象,包括 null

void throwException() {
  try {
    List<String> stringList = new List();
    stringList.add("value");
    print('${stringList[1]}');
  } on RangeError catch (e, s) {
    throw null;
    //or throw "發生了異常";
  }
}

void main() {
  try {
    throwException();
  } catch (e) {
    print("main ${e}"); //main Throw of null.
    print("${e.runtimeType}"); //NullThrownError
  }
}
複製代碼

此外,因爲拋出異常是一個表達式,因此如下寫法是合乎語法的

int throwException() => throw "";
複製代碼

9、類

9.一、類聲明

dart 是一門徹底面向對象的語言,其關於類的內容和 Java 較爲相似

class Person {
  
  String name;

  int age;

  Person(String name, int age) {
    this.name = name;
    this.age = age;
  }

}
複製代碼

dart 會自動爲 nameage 提供隱式的 gettersetter 方法,且未經初始化的實例變量均爲 null

在 dart 2 中 new 關鍵字成爲了可選關鍵字,所以能夠選擇省略 new 聲明,這一點和 kotlin 相同

void main() {
  var person = Person("leavesC", 25);
  print('${person.name} ${person.age}'); //leavesC 25
  print('${person.runtimeType}'); //Person
}
複製代碼

9.二、構造函數

dart 爲用於賦予初始值的構造函數提供了簡便寫法,如下兩種寫法的語義是一致的

Person(String name, int age) {
    this.name = name;
    this.age = age;
  }

  Person(this.name, this.age);
複製代碼

此外,dart 也提供了命名構造函數,用於方便調用者生成不一樣用途或含義的變量

Person.getDefault() {
    this.name = "leavesC";
    this.age = 25;
  }
複製代碼

也能夠在構造函數主體運行以前初始化實例變量

Person.getDefault()
      : name = "leavesC",
        age = 25 {}
複製代碼

所以當想要獲取一個默認的 Person 實例時就如同在調用 Person 的一個靜態方法

void main() {
  var defaultPerson = Person.getDefault();
  print('${defaultPerson.name} ${defaultPerson.age}'); //leavesC 25
  print('${defaultPerson.runtimeType}'); //Person
}
複製代碼

9.三、繼承

默認狀況下,子類中的構造函數會隱式調用父類的未命名的無參數構造函數,父類的構造函數在子類構造函數體的開始處被調用。若是父類沒有未命名的無參數構造函數,則必須手動調用父類中的構造函數

此外,構造函數不能被子類繼承,父類中的命名構造函數不會被子類繼承,因此若是子類也想要擁有一個和父類同樣名字的構造函數,則必須子類本身實現這個構造函數

class Man extends Person {
  
  Man(String name, int age) : super(name, age);

  Man.getDefault() : super.getDefault();
  
}
複製代碼

9.四、抽象類

dart 語言的抽象類和 Java 基本一致

abstract class Printer {
  void print(String msg);
}

class HpPrinter extends Printer {
  @override
  void print(String msg) {
    // TODO: implement print
  }
}
複製代碼

9.五、接口

dart 沒有用來專門聲明接口的語法,類聲明自己就是 dart 中的接口,實現類使用implements關鍵字來實現接口,實現類必須提供目標接口的全部功能的具體實現,即類必須從新定義它但願實現的接口中的每個函數

void main() {
  var human = Human();
  human.eat();
  human.speak();
}

class Human implements Eat, Speak {
  void funA() {}

  @override
  void eat() {
    // TODO: implement funB
  }

  @override
  void speak() {
    // TODO: implement funC
  }
}

class Eat {
  void eat() {}
}

class Speak {
  void speak() {}
}
複製代碼

9.六、mixins

mixins 是一個重複使用類中代碼的方式

void main() {
  var c = C();
  c.funA();
  c.funB();
}

class A {
  void funA() {}
}

class B {
  void funB() {}
}

//使用 with 關鍵字表示類C是由類A和類B混合構成的
class C = A with B; 複製代碼

10、未完待續

本文已加入個人學習筆記大全:JavaKotlinAndroidGuide

相關文章
相關標籤/搜索