Dart語言之從入門到放棄(萬字長文)

前言

前一段時間系統學習Flutter時總結了一些Dart的基礎知識,東西挺多的,建議收藏慢慢看😂,若是對你有幫助的話別忘了點贊關注啊。先放一個以前一篇文章的連接,看了以後應該可讓你對Dart的異步有更深刻的瞭解:那些你不知道的Dart細節之帶你透徹理解異步。這是我前段時間學習Flutter寫的Flutter版的玩安卓,感興趣的能夠去看看,很適合Flutter初學者:歷時三天,完成了Flutter版本的玩安卓。 好了,廢話很少說,開始進入正題吧,下面我將會從Dart的變量、內置類型、函數(方法)、流程控制語句、異常處理、類、泛型、庫等方面進行描述,準備好了嗎?要開車了,請繫好安全帶🐶,車速較快,請不要中途下車!!!java

變量

Hello World

學習一門語言通常都要從打印「Hello World」開始,那麼就來看一下在Dart中應該怎樣打印:express

void main(){
  print("Hello World");
}
複製代碼

和Java相似,Dart執行也須要一個main方法,打印就要比Java稍微簡單點了,直接進行Print就能夠了。這裏須要注意的是:方法若是沒有返回值的狀況下能夠省略voidjson

變量的聲明

真正要進入主題了。。。在Dart中變量的聲明有三個關鍵字:var、dynamic和Object,三個關鍵字的使用狀況各有千秋。下面來分別說一下:數組

var

首先說一下var關鍵字,瞭解過Kotlin的應該對這個關鍵字很熟悉,在Kotlin中var表示一個可變的變量(val表示不可變的,至關於final,這裏不贅述),在Dart中,var若是沒有初始值,能夠變成任何類型,但若是var有初始值,那麼類型將被鎖定。這是什麼意思呢?看一下下面這段代碼你就明白了:安全

main(){
  //print("Hello World");
  var data;
  data = 'zhujiang';
  data = 123;
  print(data);
}
複製代碼

var在沒有初始值的時候是能夠隨意指定類型的,例如上面的代碼,能夠賦值爲字符串,也能夠賦值爲int。這裏要注意:Dart中字符串可使用單引號也可使用雙引號。bash

上面代碼沒有賦初始值,下面賦初始值再看一下:閉包

main(){
  //print("Hello World");
  var data = 1;
  data = 'zhujiang';
  data = 123;
  print(data);
}
複製代碼

你們能夠看到,若是有初始值,就不能夠改變類型了,正好印證了上面咱們所說的類型被鎖定。異步

Object

這個關鍵字你們應該都很熟悉,Java中的全部對象的父類就是Object。在Dart中dynamic指動態類型,會在編譯階段檢查類型。Object和var不一樣,即便賦了初始值,也一樣能夠指定別的類型:async

Object data2 = 1234;
  data2 = 'dongnao';
  print(data2);
複製代碼

上面所說的會在編譯階段檢查類型指的是若是你調用一個自己沒有的方法,能夠直接報紅,提醒你編寫錯誤(var也是在編譯階段檢查類型)。ide

dynamic

我在學習Dart以前沒有在別的語言見過這個關鍵字,對我而言比較陌生,這個關鍵字和Object很是類似,惟一的區別就是在編譯階段不檢查類型。和Object偏偏相反,若是你調用一個自己沒有的方法,不會報錯,可是當你運行的時候纔會拋異常。

變量的默認值

這個原本不想說的,可是發現和以前所學的不太同樣,仍是說一下吧。

在Dart中,沒有初始化的變量自動獲取一個默認值爲null

一切皆對象,對象的默認值爲null

只要記着上面兩句話就能夠了,不要被以前的Java所禁錮,好比以前的Boolean值默認會是false,可是在Dart中的bool值默認也是null,記着,一切皆對象,對象的默認值爲null

變量 final和const

final你們很瞭解,Java中常用,const相對就比較陌生了,下面說一下他們的共同點:

  • 聲明的類型可省略

    final fVariable1 = 'zhujiang';
    // final String fVariable1 = 'zhujiang';
      const cVariable1 = 'zhujiang';
    // const String cVariable1 = 'zhujiang';
    複製代碼
  • 初始化後不能再賦值

    fVariable1 = 'demo';
    cVariable1 = 'demo';
    複製代碼

    上面初始化以後,這裏就不能夠賦值了,若是賦值即會報錯

  • 不能和var同時使用

    final var fVariable1 = 'zhujiang';
    const var fVariable1 = 'zhujiang';
    複製代碼

    和var同時使用的話也會報錯。

下面再看一下他們的不一樣點:

  • 類級別常量,使用static const

    static const int monday = 1;
    複製代碼

    這裏你們能夠參照一下系統中的類,例如DateTime。

  • const可以使用其餘const 常量的值來初始化其值

    const width = 100;
    const height = 100;
    const square = width * height;
    複製代碼
  • 使用const賦值聲明,const可省略

    const List clist = [1, 2, 3];
    // const List clist = const [1, 2, 3];//dart 2以前,const賦值必須用const聲明
    複製代碼
  • 能夠更改非final、非const變量的值,即便曾經具備const值

    var varList = const [1, 2, 3];
    final finalList = const [1, 2, 3];
    const constList = [1, 2, 3];
    varList = [1];
    // constList = [1];
    // finalList = [1];
    複製代碼
  • const致使的不可變性是可傳遞的

    final List ls = [1, 2, 3];
      ls[1] = 4;
      print(ls);
      const List ls1 = [1, 2, 3];
    // ls1[1] = 4;
    複製代碼
  • 相同的const常量不會在內存中重複建立

    final finalList1 = [1, 2];
      final finalList2 = [1, 2];
      print(identical(finalList1, finalList2)); //identical用於檢查兩個引用是否指向同一個對象
    
      const constList1 = [1, 2];
      const constList2 = [1, 2];
      print(identical(constList1, constList2)); 
    複製代碼

    上面代碼的運行結果是false和ture,也印證了上面所說的。

  • const須要是編譯時常量

    final DateTime finalDateTime = DateTime.now();
      // const DateTime constDateTime = DateTime.now();//DateTime.now() 是運行期計算出來的值
    複製代碼

小總結

本小節簡單說明了在Dart中變量的一些細節問題,若有補充或者錯誤可直接在評論區說出來,不要給我面子😂。

內置類型

概述

在Dart中,內置類型有如下幾種:Numbers 數值、Strings 字符串、Booleans 布爾值、Lists 列表(數組)、Sets 集合、Maps 集合、Runes 符號字符、Symbols 標識符

num, int, double

int和double你們都很熟悉,int仍是整數值,double是64-bit雙精度浮點數。這裏須要着重看一下numint和double是num的子類。剩下的就沒什麼好說的了,其餘的使用方法和Java基本同樣,只是能夠直接定義num類型的數據,剩下的你們直接看下面的代碼應該就明白了:

int i = 1; //整數值
  double d = 1.0; //double 64-bit (雙精度) 浮點數
  int bitLength = i.bitLength;
  print('bitLength: ${bitLength}'); //bitLength判斷int值須要多少bit位
  double maxFinite = double.maxFinite;
  print('maxFinite: ${maxFinite}'); //maxFinitedouble的最大值
  //int和double都是num的子類
  num n1 = 1;
  num n2 = 1.0;
  //支持十進制、十六進制
  int i1 = 0xfff;
  //科學計數法
  double d1 = 1.2e2; //120.0
  //轉換
  //String->int
  int i2 = int.parse('1');
  double d2 = 1; //當double的值爲int值時,int自動轉成double
  print('d2: ${d2}');
// int i2 = int.tryParse('1.0');//返回null
複製代碼

String

  • Dart 字符串是 UTF-16 編碼的字符序列,可使用單引號或者雙引號來建立字符串

    main(){
      String a = 'zhujiang';
      String b = "zhujiang";
    }
    複製代碼
  • 可使用三個單引號或者雙引號建立多行字符串對象

    String c = "dd"
        "sssdffg"
      "vftgt";
      String d = '''ssss fffffffgg grrrr''';
    複製代碼
  • 可使用 r 前綴建立」原始raw」字符串。

    String e = '''ssss fff\nffffgg grrrr''';
      String f = r'''ssss fff\nffffgg grrrr''';
      print(e);
      print(f);
    複製代碼

    這裏須要說明一下,若是使用r前綴,便是原始字符串,\n換行會直接打印出來而不會實現換行,下面是執行結果:

    lib/2-type.dart: Warning: Interpreting this as package URI, 'package:darttest/2-type.dart'.
    ssss
      fff
    ffffgg
      grrrr
    ssss
      fff\nffffgg
      grrrr
    複製代碼
  • 能夠在字符串中使用表達式: ${expression},若是表達式是一個標識符,能夠省略 {},若是表達式的結果爲一個對象,則 Dart 會調用對象的 toString() 函數來獲取一個字符串。

    print("object:$c");
    複製代碼

這裏既然已經說到String了,那就再說一下StringBuffer吧,使用方法和Java同樣,可是能夠省略new關鍵字,還能夠進行鏈式調用:

StringBuffer stringBuffer = StringBuffer();
  stringBuffer..write("sss")..write("ssss");
複製代碼

bool

這裏的bool至關於Java中boolean,這裏只須要記着bool對象未初始化的默認值是null(上一篇文章中提到過)

List

  • Dart中能夠直接打印list包括list的元素,List也是對象(java中直接打印list結果是地址值)。

    print(List)
    複製代碼

    這裏會直接輸出List。

  • Dart中List的下標索引和java同樣從0開始

  • 和java同樣支持泛型。

  • 有增刪改查,支持倒序,自帶排序、洗牌,可以使用+將兩個List合併

    //經常使用方法 增刪改查,排序,洗牌,複製子列表
      var list4 = [];
      //增
      list4.add(1);
      print('add 1 :${list4}');
      //刪
      list4.remove(5);
      print('remove 5 :${list4}');
      list4.removeAt(2);
      print('remove at 0 :${list4}');
      //改
      list4[4] = 5;
      print('update list4[4] to 5 :$list4}');
      //range
      list4.fillRange(0, 3, 9);
      print('fillRange update list4[0]-list4[2] to 9 :$list4}');
      Iterable getRange = list4.getRange(0, 3);
      print('getRange list4[0]-list4[2] :$getRange}');
      //查
      var contains = list4.contains(5);
      print('list4 contains 5 :${contains}');
      var indexOf = list4.indexOf(1);
      print('list4 indexOf 1 :${indexOf}');
      int indexWhere = list4.indexWhere((test) => test == 5);
      print('list4 indexWhere 5 :${indexWhere}');
      //排序
      list4.sort();
      print('list4 sort :${list4}');
      //洗牌
      list4.shuffle();
      print('list4 shuffle :${list4}');
      //複製子列表
      var list5 = list4.sublist(1);
      print('sublist(1) list5 :${list5}');
      //操做符
      var list6 = [8, 9];
      print('list6 :${list6}');
      var list7 = list5 + list6;
      print('list5 + list6 :${list7}');
    複製代碼

    這裏的代碼比較長,可是很好理解,就是一個集合的增刪改查和一些比較經常使用的方法,你們能夠本身手寫一下加深理解。

Map

map這裏就很少說了,和Java相似

Set

Set這裏其實也和Java差很少,可是有幾個地方須要說明一下:

  • set1.difference(set2):返回set1集合裏有但set2裏沒有的元素集合
  • set1.intersection(set2):返回set1和set2的交集
  • set1.union(set2):返回set1和set2的並集
  • set1.retainAll():set1只保留某些元素(要保留的元素要在原set中存在)

這幾個方法很是好用,簡單寫一下僞代碼你們看一下吧:

var difference12 = set1.difference(set2);
  var difference21 = set2.difference(set1);
  print('set1 difference set2 :${difference12}'); //返回set1集合裏有但set2裏沒有的元素集合
  print('set2 difference set1 :${difference21}'); //返回set2集合裏有但set1裏沒有的元素集合
  var intersection = set1.intersection(set2);
  print('set1 set2交集 :${intersection}'); //返回set1和set2的交集
  var union = set1.union(set2);
  print('set1 set2並集 :${union}'); //返回set1和set2的並集
  set2.retainAll(['aa', 'flutter']); //只保留(要保留的元素要在原set中存在)
  print('set2只保留aa flutter :${set2}');
複製代碼

Runes

表示符文的意思,用於在字符串中表示Unicode字符。使用String.fromCharCodes顯示字符圖形。若是非4個數值,須要把編碼值放到大括號中。

Runes runes = new Runes('\u{1f605} \u6211‘);
  var str1 = String.fromCharCodes(runes);  
  print(str1);
複製代碼

直接這樣寫就能夠了,下面看一下打印效果:

😅 我
複製代碼

直接能夠打印出表情,有表情需求的能夠直接使用Runes。

Symbol

Symbol標識符,之前主要是反射使用,可是如今mirrors模塊已經被移除,因此沒啥大用了,你們只要知道有這麼一個內置類型就能夠了。

方法

方法這一塊很重要,寫代碼離不開方法啊,繼續開車,抓穩了!

定義

  • 可在函數內定義

    void main(){
      void test(){
      }
    }
    複製代碼

    和Java不一樣,Dart中的函數能夠定義在函數內部(和Java的匿名內部類別搞混了)

  • 定義函數時可省略類型(不建議)

    main(){
      test(){
        // return null;
      }
    }
    複製代碼

    上面的函數能夠寫成下面這樣,Dart中函數是Function類型的對象。全部的函數都返回一個值。若是沒有指定返回值,則 默認把語句 return null; 做爲函數的最後一個語句執行。

  • 支持縮寫語法 => (Kotlin是用=來實現)

    main(){
      print(test1());
    }
    test()=> "zhujiang";
    String test1(){
      return "zhujiang";
    }
    複製代碼

    上面代碼中的test()和test1()效果是同樣的,「=>」至關於大括號和return

可選參數

既然是函數,那麼確定要傳參,我在通過Java構造方法的摧殘以後,看這個功能的時候淚流滿面😭。爲何會這樣呢?下面先來看一個我們的Java類的構造函數:

public class Test {

    private String name;
    private int age;
    private String address;

    public Test(String name, int age, String address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }
    
    public Test(String name, String address) {
        this.name = name;
        this.address = address;
    }

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

    public Test(String name) {
        this.name = name;
    }
}
複製代碼

一個很是簡單的構造方法,只是想要傳參數形式多一點,居然須要寫這麼多重載方法。。。那麼下面我們來看一下拿Dart寫的一樣功能的代碼:

class Test{
  String name;
  int age;
  String address;
  Test(this.name, {this.age, this.address});
}
複製代碼

What?居然能夠這樣?是的,能夠這樣。下面來詳細說一下函數的可選參數。

可選命名參數

其實上面構造方法我使用的就是可選命名參數,寫的時候其實很簡單,只須要加上大括號就行。下面是使用方法:

main() {
  print(add(a: 1, b: 3));
}

int add({int a = 1, int b}) {
  return a + b;
}
複製代碼

上面的add方法調用的時候能夠不填參數,也能夠填任何一個參數,也能夠將參數都填上。這裏要注意:

  • 若是a不填的話,默認值就是1,若是b不填,默認值就是null。
  • 傳參數的時候必定要記得寫a:,否則會報錯。

可選位置參數

和上面的可選命名參數不一樣,上面的函數若是想調用,必須命名再加上冒號才能使用,可選位置參數就不須要了。這裏和上面同樣,默認值可加可不加:

int add2(int a,[int b = 2,int c = 3]) {
  return a + b;
}
複製代碼

下面是上面add2()方法的幾種調用方式:

print(add2(1));
  print(add2(1,2));
  print(add2(1,1,3));
複製代碼

注意

上面的可選命名參數和可選位置參數,可以使用list或map做爲默認值,但必須是const。

int add3({List a = const [1,2,3], int b = 2}) {
  return b;
}
複製代碼

匿名函數

可賦值給變量,經過變量調用。可在其餘函數中直接調用或傳遞給其餘函數。匿名函數分爲無參匿名函數和有參匿名函數

  • 無參匿名函數

    var printFun = () => print("**無參匿名函數**");
    複製代碼
  • 有參匿名函數

    var printFun2 = (name) => print("**有參匿名函數 $name**");
    printFun2("sss");
    複製代碼

這裏還有一個小的知識點能夠經過()調用,不推薦。

(() =>print("***能夠經過()調用,不推"))();
複製代碼

閉包

這裏感受沒什麼好說的,你們直接看代碼應該就能夠理解:

Function makeAddFunc(int x) {
  x++;
  return (int y) => x + y;
}
var makeAddFun = makeAddFunc(11);
print(makeAddFun(10));
複製代碼

就是建立一個方法,返回的也是一個方法,能夠再繼續調用。

函數別名

typedef給函數起一個別名,使用比較方便。例如定義一個方法的回調,直接使用別名定義。沒返回值,則只要參數匹配就好了,若是定義了返回值,則返回值不同會報錯。

typedef Fun1(int a, int b);
typedef Fun2<T, K>(T a, K b);
int add(int a, int b) {
  print('a + b');
  return a + b;
}
class Demo1 {
  Demo1(int f(int a, int b), int x, int y) {
    var sum = f(x, y);
    print("sum1 = $sum");
  }
}
class Demo2 {
  Demo2(Fun1 f, int x, int y) {
    var sum = f(x, y);
    print("sum2 = $sum");
  }
}
class Demo3 {
  Demo3(Fun2<int, int> f, int x, int y) {
    var sum = f(x, y);
    print("sum3 = $sum");
  }
}
複製代碼

上面代碼就是定義函數別名的方法,下面是調用方法:

Fun1 fun1 = add(11, 12);
複製代碼

操做符、流程控制語句、異常

這一小節看着東西應該挺多,其實很簡單,好多都和Java基本一致。

操做符

上面這幅圖中就是Dart中的操做符,標爲黑色的操做符和Java中使用基本同樣,這裏也就不過多贅述,我們來仔細看看標紅Java中沒有的操做符:

  • 後綴操做: ?.

    若是寫過Kotlin的話,能夠跳過這段了,這裏的問號點和Kotlin中基本一致,都是爲了判空而出現的,那麼下面來看一下使用方式吧:

    main() {
      String a;
      print(a.length != null ? a.length : '');
    }
    複製代碼

    上面就是我們平時寫的代碼,一個三目表達式來進行判空。可是在Dart中能夠不這樣寫,能夠直接使用問號點。

    main() {
      String a;
      //print(a.length != null ? a.length : &#39;&#39;);
      print(a?.length);
    }
    複製代碼

    是否是很方便?之後判空能夠直接使用**"?."**了

  • 除(取整):~/

    這個操做符其實很簡單,只是對數字進行取整,下面是實例代碼:

    print(1/2);
    print(1~/2);
    複製代碼

    上面代碼的輸出值是0.5和0

  • 類型操做:as

    這個和Kotlin中的as也基本同樣,是對數據進行類型轉換,很簡單,直接看代碼:

    num n = 1;
    int n2 = n as int;
    複製代碼
  • 類型操做: is

    num n1 = 1.0;
    if(n1 is int){
      print("int");
    }else{
      print("double");
    }
    複製代碼
  • 類型操做:is!

    從名稱上就能夠知道和上面的is正好相反,就不寫代碼驗證了。

  • 判空:??

    這個上面也寫出來了,意思就是判空,下面直接看一下使用方法吧:

    bool aaa;
    aaa = aaa ?? false;
    複製代碼
  • 級聯:..

    這個其實就是鏈式調用,以前的文章中寫過,當時的例子是StringBuffer:

    StringBuffer stringBuffer = StringBuffer();
     stringBuffer..write("sss")..write("ssss");
    複製代碼

操做符就說到這裏吧,掌握好操做符在寫代碼的時候會事半功倍的,但願你們都能掌握好。

流程控制語句

這個,怎麼說,稍微提一下吧,這個是基礎的基礎,Dart中的流程控制語句和Java基本同樣,直接進行使用就行。

不是不想寫,if語句、for循環、while循環、brake、continue、switch、case,沒了,這就是Dart的流程控制語句,一模一樣。跳過。

異常

Dart 提供了 Exception 和 Error 類型, 以及一些子類型。還能夠定義本身的異常類型。可是,Dart 代碼能夠拋出任何非 null 對象爲異常,不只僅是實現了 Exception 或者 Error 的對象。

下面這幅圖是Dart中的Exception

下面這幅圖是Dart中的Error

拋出異常

  • 這個和Java中寫法同樣,全部的 Dart 異常是非檢查異常。 方法不必定聲明瞭他們所拋出的異常, 而且你不要求捕獲任何異常。

    // 拋出Exception 對象
    // throw new FormatException(‘格式異常');
    
    // 拋出Error 對象
    // throw new OutOfMemoryError();
    複製代碼
  • Dart 代碼能夠拋出任何非 null 對象爲異常,不只僅是實現了 Exception 或者 Error 的對象。

    // 拋出任意非null對象
    // throw '這是一個異常';
    複製代碼

捕獲異常

  • 全部的 Dart 異常是非檢查異常。 方法不必定聲明瞭他們所拋出的異常, 而且你不要求捕獲任何異常。可使用on 或者 catch 來聲明捕獲語句,也能夠 同時使用。使用 on 來指定異常類型,使用 catch 來 捕獲異常對象。

    try {
        throw new OutOfMemoryError();
      } on OutOfMemoryError {
        print('沒有內存了');
      }
    複製代碼
  • catch() 能夠帶有一個或者兩個參數, 第一個參數爲拋出的異常對象, 第二個爲堆棧信息 (一個 StackTrace 對象)。

    try {
        throw new OutOfMemoryError();
      } catch (e, s) {
        print('Exception details: $e');
        print('Stack Trace: $s');
      } finally {
        print('end');
      }
    複製代碼
  • 可使用rethrow把捕獲的異常從新拋出。

    try {
        throw new OutOfMemoryError();
      } catch (e, s) {
        print('Exception details: $e');
        print('Stack Trace: $s');
        rethrow;
      }
    複製代碼

類的點點滴滴

前幾小節分別講解了Dart中的變量、內置類型、函數(方法)、操做符、流程控制語句和異常,對Dart的基本語法已經有了不少的瞭解,那麼接來講一下Dart中的類。

構造函數

前幾篇文章中在講函數(方法)的一篇中提到過,這裏再說一下吧,首先來看一下Java中構造函數的寫法:

class Point {
  double x;
  double y;

  Point(int x, int y) {
    this.x = x;
    this.y = y;
  }
}
複製代碼

下面是dart中的建議寫法:

class Point {
  num x;
  num y;
  Point(this.x, this.y);
}
複製代碼

命名構造函數

使用命名構造函數能夠爲一個類實現多個構造函數, 或者使用命名構造函數來更清晰的代表你的意圖。

class Point { 
  num x; 
  num y; 

  Point(this.x, this.y); 

  //命名構造函數
  Point.fromJson(Map json) { 
    x = json['x']; 
    y = json['y']; 
  } 
}
複製代碼

重定向構造函數

一個重定向構造函數是沒有代碼的,在構造函數聲明後,使用 冒號調用其餘構造函數。

class Point { 
  num x; 
  num y; 

  Point(this.x, this.y); 

  //重定向構造函數,使用冒號調用其餘構造函數
  Point.alongXAxis(num x) : this(x, 0);
}
複製代碼

初始化列表

在構造函數體執行以前能夠初始化實例參數。 使用逗號分隔初始化表達式。初始化列表很是適合用來設置 final 變量的值。

import 'dart:math';

class Point {
  //final變量不能被修改,必須被構造函數初始化
  final num x;
  final num y;
  final num distanceFromOrigin;

  //初始化列表
  Point(x, y)
      : x = x,
        y = y,
        distanceFromOrigin = sqrt(x * x + y * y);
}
複製代碼

調用超類構造函數

首先來創建一個超類(父類),下面的代碼都繼承自此類:

class Parent {
  int x;
  int y;

  //父類命名構造函數不會傳遞 
  Parent.fromJson(x, y)
      : x = x,
        y = y {
    print('父類命名構造函數');
  }
}
複製代碼
  • 超類命名構造函數不會傳遞,若是但願使用超類中定義的命名構造函數建立子類,則必須在子類中實現該構造函數。

    class Child extends Parent {
      int x;
      int y;
    
      Child.fromJson(x, y) 
    	 : x = x,
    	   y = y,
        print('子類命名構造函數');
      }
    }
    複製代碼
  • 若是超類沒有默認構造函數, 則你須要手動的調用超類的其餘構造函數,調用超類構造函數的參數沒法訪問 this。

    class Child extends Parent {
      int x;
      int y;
      //若超類沒有默認構造函數, 須要手動調用超類其餘構造函數
      Child(x, y) : super.fromJson(x, y) {
        //調用父類構造函數的參數沒法訪問 this
        print('子類構造函數');
      }
    }
    
    複製代碼
  • 在構造函數的初始化列表中使用 super(),須要把它放到最後。

    class Child extends Parent {
      int x;
      int y;
     
      Child.fromJson(x, y) 
    	 : x = x,
    	   y = y,
    	   super.fromJson(x, y) {
        print('子類命名構造函數');
      }
    }
    
    複製代碼

常量構造函數

定義const構造函數要確保全部實例變量都是final。 const關鍵字放在構造函數名稱以前。

class Point2 {
  //定義const構造函數要確保全部實例變量都是final
  final num x;
  final num y;
  static final Point2 origin = const Point2(0, 0);

  //const關鍵字放在構造函數名稱以前,且不能有函數體
  const Point2(this.x, this.y);
}
複製代碼

工廠構造函數(Dart中的單例)

工廠構造函數是一種構造函數,與普通構造函數不一樣,工廠函數不會自動生成實例,而是經過代碼來決定返回的實例對象。若是一個構造函數並不老是返回一個新的對象(單例),則使用 factory 來定義這個構造函數。工廠構造函數沒法訪問this。

class Singleton {
  String name;
  //工廠構造函數沒法訪問this,因此這裏要用static
  static Singleton _cache; 

  //工廠方法構造函數,關鍵字factory
  factory Singleton([String name = 'singleton']) =>
      Singleton._cache ??= Singleton._newObject(name);

  //定義一個命名構造函數用來生產實例
  Singleton._newObject(this.name);
}
複製代碼

Setter和Getter

在Java中get和set方法能夠直接生成,在Dart中無需本身定義。每一個實例變量都隱含的具備一個 getter, 若是變量不是 final 的則還有一個 setter。能夠經過實行 getter 和 setter 來建立新的屬性, 使用 get 和 set 關鍵字定義 getter 和 setter。能夠開始使用實例變量,後來能夠把實例變量用函數包裹起來,而調用你代碼的地方不須要修改。下面是代碼實例:

class Rectangle {
  num left;
  num top;
  num width;
  num height;

  Rectangle(this.left, this.top, this.width, this.height);

  num get right => left + width;
  set right(num value) => left = value - width;
  num get bottom => top + height;
  set bottom(num value) => top = value - height;
}
複製代碼

抽象類(接口)

  • 不能被實例化,除非定義一個工廠構造函數。

    這個很好理解,在Java中的抽象類也一樣不可實例化。

  • 抽象類一般用來定義接口, 以及部分實現。

    在Dart中沒有interface這個關鍵字,只有abstract,因此可使用abstract來生成接口:

    abstract class Demo{
      void zhujiang();
    }
    
    class Zhu implements Demo{
      @override
      void zhujiang() {}
    }
    複製代碼
  • 抽象類一般具備抽象方法,抽象方法不須要關鍵字,以分號結束便可。

    其實上面代碼也能夠用做這個實例,只須要把implements變成extends便可:

    class Zhu extends Demo{
      @override
      void zhujiang() {}
    }
    複製代碼
  • 接口方式使用時,須要重寫抽象類的成員變量和方法,包括私有的。

  • 一個類能夠implement一個普通類。Dart任何一個類都是接口。

  • 一個類能夠implement多個接口。

最後幾個說的其實有點繞,說白了就是能夠實現多個,這裏須要注意的是:implement只是實現接口,你須要重寫接口中的方法,否則是不會執行的。還有一點是若是想extents多個類的話須要使用with關鍵字。說了這麼多不如直接看代碼,看代碼應該好理解一些:

abstract class Demo{
  void zhujiang();
}

abstract class Demo2{
  void zhujiang();
}

abstract class Demo3{
  void zhujiang();
}

class Zhu extends Demo with Demo2,Demo3 implements Demo3,Demo2{
  @override
  void zhujiang() {}
}
複製代碼

可調用類

實現call()方法可讓類像函數同樣可以被調用。這個很簡單,直接上代碼:

class ClassFunction {
  call(String a, String b, String c) => '$a $b $c!';
}

main() {
  var cf = new ClassFunction();
  var out = cf("aaa","flutter","damon");
  print('$out');
  print(cf.runtimeType);
  print(out.runtimeType);
  print(cf is Function);
}
複製代碼

下面是打印結果:

lib/5.dart: Warning: Interpreting this as package URI, 'package:darttest/5.dart'.
aaa flutter damon!
ClassFunction
String
false
複製代碼

Mixin

  • 子類沒有重寫超類A方法的前提下,若是2個或多個超類擁有相同簽名的A方法,那麼子類會以繼承的最後一個超類中的A方法爲準。
  • 若是子類本身重寫了A方法則以自己的A方法爲準。

在這裏先不過多解釋Minxin,個人理解就相似於策略模式,抽出不變的爲接口,而後多實現。這裏先不寫了,若是有可能的話等專門寫一篇介紹Dart中的Minxin的文章吧。

Dart泛型

  • Java中的泛型信息是編譯時的,泛型信息在運行時是不存在的
  • Dart的泛型類型是固化的,在運行時也有能夠判斷的具體類型
var names = List<String>();
print(names is List<String>);//true
print(names.runtimeType); // List<String> 
複製代碼

在Java中,能夠測試對象是否爲List,但沒法測試它是不是List。可是在Dart中是預編譯的。

其餘的泛型使用方法和Java中的相似,這裏就不贅述了。

Dart中的庫

這個平時用的很是多,並且很是實用,Dart有你不知道的但又很是好用的語法,繼續出發!

庫-使用核心庫

使用import關鍵字來載入庫:

import "dart:math";

void main() {
  print(sqrt(4));//開平方 2.0
}
複製代碼

對於內置的庫,URI 使用特殊的 dart: scheme。對於其餘的庫,你可使用文件系統路徑或者 package: scheme。

庫-載入第三方庫

在Flutter中想使用第三方庫的話須要在pubspec.yaml中的dependencies中添加依賴:

dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^0.1.0
  dio: ^2.1.0
複製代碼

添加完成以後點擊右上角的packages get進行導入依賴。下面是三方哭調用方法:

import "package:dio/dio.dart";

void main() {
  getHttp();
}

void getHttp() async {
  try {
    Response response = await Dio().get("https://www.baidu.com");
    print(response);
  } catch (e) {
    print(e);
  }
}
複製代碼

這裏給你們一個網站,須要尋找第三方庫的時候直接在裏面搜索便可:pub.dev/

庫-指定庫前綴

在Java編碼過程常常會出現一個類中調用兩個類中相同名字的方法,這時就須要全限定名稱來調用了,可是在Dart中是不須要的:

import 'MyLib1.dart' as lib1; 
import 'MyLib2.dart' as lib2; 

void main() {
  var myLib = lib1.MyLib();
  var myLib2 = lib2.MyLib();
}
複製代碼

上面代碼中MyLib1.dart 和 MyLib2.dart 都有一個名字爲 MyLib 的類。

庫-選擇性載入

  • show-只載入庫的某些部分
  • hide-篩選掉庫的某些部分
import 'Mylib1.dart' as lib1 show Test;
import 'Mylib2.dart' as lib2 hide Test;

var test = lib1.Test();
var lib = lib2.MyLib();
複製代碼

若是隻使用庫的一部分功能,則能夠選擇須要導入的 內容。

庫-延遲載入

  1. 使用deferred as導入
  2. 使用標識符調用loadLibrary()加載庫
import 'MyLib1.dart' deferred as lazyLib;

void main() {
  lazyLoad();
}
lazyLoad() async {
  await lazyLib.loadLibrary();
  var t = lazyLib.Test();
  t.test();
}
複製代碼

使用 await 關鍵字暫停代碼執行一直到庫加載完成。可提升程序啓動速度。用在不常使用的功能和載入時間過長的包。

庫-自定義庫

  1. 首先將一個文件做爲library:

    library mylib;
    
    part 'util.dart';
    part 'tool.dart';
    
    void printMyLib() => print('mylib');
    複製代碼
  2. 而後使用part 把一個庫分開到多個 Dart 文件中。

    part of mylib;
    void printTool() => print('tool');
    複製代碼
    part of mylib;
    void printUtil() => print('util');
    複製代碼
  3. import不會徹底共享做用域,而part之間是徹底共享的。若是說在A庫中import了B庫,B庫import了C庫,A庫是沒有辦法直接使用C庫的對象的。而B,C如果A的part,那麼三者共享全部對象。而且包含全部導入。

    import 'mylib/mylib.dart';
    
    void main() {
      printMyLib();
      printUtil();
      printTool();
    }
    複製代碼

總結

這一篇文章以前寫了很久,原本想每一個小節分紅一篇文章發佈的,這樣就會有好幾篇文章了😂,但又想了想那樣不太厚道,直接全寫完再發吧。看掘金中都是大佬,各類一線大廠技術、各類阿里騰訊名義的文章。。。比不了比不了,只是一個小破公司的一個一直在努力前進的小碼農,寫的文章也許不太好,但也都算用心了,但願你們可以喜歡,最後說一句,別忘了點贊關注評論啊😂😂😂,感激涕零。

相關文章
相關標籤/搜索