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();
    }
    复制代码

总结

这一篇文章以前写了很久,原本想每一个小节分红一篇文章发布的,这样就会有好几篇文章了😂,但又想了想那样不太厚道,直接全写完再发吧。看掘金中都是大佬,各类一线大厂技术、各类阿里腾讯名义的文章。。。比不了比不了,只是一个小破公司的一个一直在努力前进的小码农,写的文章也许不太好,但也都算用心了,但愿你们可以喜欢,最后说一句,别忘了点赞关注评论啊😂😂😂,感激涕零。

相关文章
相关标签/搜索