Dart速通

592 阅读13分钟

Dart语言概念

  • 所有东西都是对象,无论是变量、数字、函数、都是对象。所有对象都是类的实例。所有对象都是继承自内置的Objectd对象。万物皆对象(妈妈再也不担心我没有对象了)。

  • 程序中指定数据类型使得程序更合理地分配内存空间,并帮助编译器进行语法检查。但是指定类型并不是必须的。Dart语言是弱数据类型。

  • Dart代码在运行前解析。指定数据类型和编译时的常量,可提高运行速度。

  • Dart程序有统一的程序入口:main()。这一点和Java很像

  • Dart没有publicprotectedprivate概念。私有特性通过变量或函数加上下划线_来表示。(这一点和Java、Kotlin一点都不像甚至想骂娘)

  • Dart的工具可以检查出警告信息(warning)和错误信息(errors)。警告信息只是表明代码可能不工作,但是不妨碍程序运行。错误信息可以是编译时的错误,也可能是运行时的错误。编译时的错误将阻止程序运行,运行时的错误将会以异常(exception)的方式呈现

  • Dart支持anync/await异步处理

  • Dart有(56个关键字)如:abstractdoimportsuperasin等。(56个呢自己去查吧)

    Dart常用库

    包名 描述
    dart:async 异步编程支持,提供FutureStream
    dart:collction dart:core提供更多的集合支持
    dart:convert 不同类型(JSONUTF-8)间的字符编码、解码支持
    dart:core Dart语言内建的类型、对象以及Ddart语言核心的功能
    dart:html 网页开发用到的库
    dart:io 文件读写I/O相关d操作的库
    dart:math 数字常量及函数。提供随机数算法
    dart:svg 事件和动画的矢量图像支持

    其中如下三个库使用频率最高

    • dart:core:核心库,包括stringsnumberscollectionserrorsdatesURIs

    • dart:html:网页a开发里DOM相关的一些库

    • dart:io:I/O命令行使用的库

      dart:core库是Dart语言初始已经包含的库,其他的任何库在使用前都需要加上import语句。例如使用dart:io

      improt `dart:io`
      

变量和基本数据类型

变量声明使用关键字var,如

var name='张三';

在Dart中一切皆为对象,所以如果变量声明时没有值那么,默认即使null

int name
if(name==null)

常量和固定值

  • 如果定义的变量不会变化,可以使用finalconst来指明。const是一个编译时的常量,final的值只能被设定一次,如下:
    final userName='掘金'//定义一个常量
    userName='掘金呀'//会引发一个错误
    
  • 通过对const类型 做四则运算将自动得到一个const类型的值。下面的代码会得到一个常量,计算圆的面积:
    const pi=3.1415926525;
    const area = pi * 100 * 100;
    
  • 可以通过const来创建常量值,也就是说const[]本身是构造函数,如
    final stars = const [] ;
    const buttons = const [] ;
    

基本数据类型

Dart语言常用的基本数据类型包括:NumberStringBooleanListMap

  • Number类型

    Number类型包括如下两类:

    • int整形。取值范围:-2^53到2^53
    • double浮点型。64位长度的浮点型数据,也就是双精度浮点型。

    intdouble类型都是num类型的子类。int类型不能包含小数点num类型包括的操作有:+-*/以及移位操作符>>num类型包括的常用方法有:absceilfloor

  • String类型

    String类型也就是字符串类型,在开发中大量使用。定义例子如下所示:

    var str = 'hello juejin';//单双引号都可以
    

    String类型可以用+操作

    var str1 = 'hi';
    var str2 = 'fultter'
    var str3 = str1 + str2;
    print(str3)
    

    上面的代码打印输出hi flutter。 flutter可以使用三个单引号和三个双引号来定义多行的String类型,在Flutter中我们用来表示大文本块,如下:

    var s1 = ```使用三个
    单引号```
    var s2 = """"使用三个
    双引号
    """"
    
  • Boolean类型 Dart是强检查类型,只有bool类型的值是true才被认为是true。如下面示例代码编译是不能正常通过的,因为'sex'是一个字符串,不能使用条件判断语句,必需使用bool类型才可以:

    var sex='男';
    if (sex) {
        print('你的性别是!'+sex);
    }
    
  • List类型 在Dart语言中,具有一系列相同类型的数据称为List对象。在Dart里的List对象类似于JavaScript语言的Array对象。定义List例子如下:

    var list - [1,2,3];
    

    List对象的第一个元素的索引是0,最后一个元素的索引是list.lenght-1

    var list = [1,2,3,4,5];
    print('length:'+list.length+'\t last:'+list[list.length-1])
    

    上面的代码会输出length:5 last:5

  • Map类型 Map类型,就是键值对,跟java一样Key是必须的。 Map对象定义如下:

    var week = {
        'Monday':'星期一',
        'Tuesday':'星期二',
        'wednesday':'星期三',
        'Thursday':'星期四'
    }
    

    也可以使用Map对象的构造函数Map()来创建Map对象,如下所示:

    var week = new Map();
    week['Mondy'] = '星期一';
    week['Tuesday'] = '星期二';
    week['wednesday'] = '星期三';
    

函数

Dart 是一个面向对象语言,所以函数也是对象,函数属于Function对象。 函数可以像参数一样传递给其他函数,这样便于做回调处理。 如下示例为判断两字符是否相等:

//判断两个字符是否相等
bool equal(String str1,String str2){
    return str1 == str2;
}

可选参数

将参数使用中括号[]括起来,用来表明是可选位置参数。例如,总共传入了三个参数,其中namesex是必需传入的参数,from参数可以不传,代码如下:

//获取用户信息
String getUserInfo(String name,String sex,[String from]){
    var info = '$name的性别是$sex';
    if(from!=null){
        info = '$info来自 $from';
    }
    return info;
}
void test(){
    print(getUserInfo('小王','男'));
}

调用上面的test方法可以输出“小王的性别是男”,但是不会输出来自哪里。

参数默认值

如果参数制定了默认值,当不传入值时,函数里会使用这个默认值。如果传入了值,则用传入的值取代默认值。通常参数默认值为null。改造上面的getUserInfo方法,给from赋上默认值,如下:

//获取用户信息
String getUserInfo(String name,String sex,[String from = '中国']){
    var info = '$name的性别是$sex';
    if(from!=null){
        info = '$info来自 $from';
    }
    return info;
}
void test(){
    print(getUserInfo('小王','男'));
}

调用上面的test方法可以输出“小王的性别是男来自中国”

main函数

Flutter应用程序必须要有一个main函数,和其他语言一样作为程序的入口函数。下面的代码表示应用要启动MyApp类:

void main()=>runApp(MyApp());

函数返回值

在Dart语言中,函数的返回值有如下特点

  • 所有函数都会有返回值。
  • 如果没有指定函数返回值,则默认的返回值是null
  • 没有返回值的函数,系统会在最后添加隐式的return语局。

运算符

Dart支持的运算符基本和Java运算符一致,就不一一举例说明了,下面是完整操作符表

完整操作符表

描述 运算符
一元后缀 expr++expr--().?.
一元前缀 -expr!expr~expr++expr--expr
乘法类型 */%~/
加法类型 +-
位移运算 >><<
并且位运算 &
异或位运算 ^
或位运算 |
关系和类型测试 >=<=><asisis!
等式 ==!=
逻辑并且 &&
逻辑或 ||
条件 expr1 ?expr2 : expr3
赋值 =*=/=~-%=+=-=<<=>>=&=^=/=??=

以前没见过的运算符

??=运算符

仅在变量为null时赋值,使用??=运算符,如下所示:

//赋值给a
num a = value;
//如果b为空,则将值分配给b;否则,b保持不变。
num b=null;
b ?? = a;

流程控制语句

Dart语言的流程控制语句如下:

  • ifelse
  • for(循环)
  • whiledo-while(循环)
  • breakcontinue
  • switchcase
  • assert(断言)
  • try-catchthrow

这些操作符的使用和Java一样

异常处理

抛出异常

抛出异常的例子

void test(){
  throw FormatException('抛出一个 FormatException 异常 ');
}

也可以抛出任意对象

throw `数据非法`;

捕获异常

你可以指定一个或多个参数捕获异常(catch),第一个是抛出的异常,第二个是堆栈跟踪(StackTrace对象)。如下所示:

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

上面代码第一个catch用来捕获异常详细信息,第二个catch是堆栈跟踪信息。

Finally

Finally和其他的语言的finally效果一样的。代码示例如下

void test(){
    try{
        //....
    } on Exception catch(e){
        print('Exception details:\n $e');
    }catch(e,s){
        print('Exception details:\n $e');
        print('Stack trace:\n $s ');
    } finally {
        print('Do some thing:\n');
    }
}

无论如何都会走到finally块中。

面向对象

Dart作为高级语言支持面向对象的很多特性,并且支持基于mixin继承方式。基于mixin的继承方式是指:一个类可以继承多个父类,相当于其他语言的多继承。所有的类都有同一个基类Object,这个特性类似于Java语言,Java所有的类也都是继承自Object,也就是说一切皆对象。 使用new语句实例化一个类,如下所示:

//实例化一个User类的对象user
var user = new User('jack','20');

实例化成员变量

定义一个User类,在里面添加成员变量nameage,如下:

class User{
    String name;
    int age;
}

类定义中所有的变量都会隐式的定义setter方法,针对非空的变量会额外增加getter方法。实例化User如下:

main() {
    var user = User();
    user.name='jack';//相当于使用了name的setter方法
    user.age=20;
}

构造函数

常规构造函数

代码如下:

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

也可以简写

class User{
    String name;
    int age;
    User(this.name,this.age);
}

命名构造函数

使用命名构造函数从另一类或现有的数据中快速实现构造函数,如下:

class User{
    String name;
    int age;
    User(this.name,this.age);
    //命名构造函数
    User.fromJson(Map map){
        name = map['name'];
        age = map['age'];
    }
}

构造函数初始化列表

除了调用父类的构造函数,也可以通过初始化列表在子类的构造函数运行前来初始化实例成员变量值,代码如下:

class User{
    final String name;
    final int age;
    User(name,age)
        :name=name,
         age=age;
}
main(){
    var u = User('cxl',21);
}

get()set()

get()set()是专门用于读取和写入对象的属性方法,每一类的实例,系统都会隐式地包含有get()set()方法。 例如,定义一个矩形地类,有上、下、左、右四个成员变量:topbottomleftright,使用getset关键字分别对rightbottom进行获取和设置值。如下

class Rectangle{
  num left;
  num top;
  num width;
  num height;
  Rectangle(this.left,this.top,this.width,this.height);
  //获取right值
  num get right   => left + width;
  //设置right值地时候 同时left也要发生变化
  set right(num value) => left = value-width;
  //获取bottom值
  num get bottom => top + height;
  //设置bottom值 同时top也要变化
  set bottom(num value)=>top = value - height;
}

main(){
  var rect = Rectangle(3,4,20,15);
  print('left:${rect.left}');
  print('right:${rect.right}');
  rect.right=30;
  print('修改 right 值为 30');
  print('left:${rect.left}');
  print('right:${rect.right}');
  print('-------------------------------');
  print('top:${rect.top}');
  print('bottom:${rect.bottom}');
  rect.bottom=50;
  print('修改 bottom 值为 50');
  print('top:${rect.top}');
  print('bottom:${rect.bottom}');
}

上面例子对应输出为:

left:3
right:23
修改 right 值为 30
left:10
right:30
-------------------------------
top:4
bottom:19
修改 bottom 值为 50
top:35
bottom:50

重载操作

编写一个例子,定义一个Vector向量类,编写两个方法分别用于重载加号及减号,那么当两个向量相见,就表示他们的x值及y值相加,当两个向量相减,就表示他们地x值及y值相减,如下:

//定义一个向量类
class Vector {
  final int x;
  final int y;
  const Vector(this.x,this.y);
  //重载加号 + (a+b)
  Vector operator +(Vector v){
    return new Vector(x+v.x, y+v.y);
  }
  //重载减号- (a -b)
  Vector operator - (Vector v){
    return new Vector(x-v.x, y-v.y);
  }
}

main(){
  //实例化两个向量
  final v = Vector(2,3);
  final w = Vector(2,2);
  final r1=v+w;
  print('r1.x=${r1.x}\tr1.y=${r1.y}');
  final r2 = v - w ;
  print('r2.x=${r2.x}\tr2.y=${r2.y}');
}

上面例子输出为:

r1.x=4	r1.y=5
r2.x=0	r2.y=1

继承、抽象、枚举

Dart中的继承、抽象、枚举基本和Java一致吗,没啥好说的。

Mixins

Mixins(混入功能)相当于多继承,也就是说可以继承多个类。 具体可以参考这篇文章:HelloDart-MixIn,土话记录多继承机制

下面是个Mixins的小例子:

class S{
  a() => print('S.a');
}
class A{
  a()=>print('A.a');
  b()=>print('A.b');
}
class T = A with S;

main(){
  var t = T();
  t.a();
  t.b();
}

以上代码输出如下:

S.a
A.b

泛型

泛型通常是为了类型安全而设计的,设当地指定泛型类型会生成更好的地代码,也可以使用泛型来减少代码重复。Dart中使用地方式来定义泛型。例如,想要List包含字符串,可以将其声明为list<String>。如下:

void test(){
    var names = List<String>();
    names.addAll(['cxl','zs','ls']);
}

用于集合泛型

泛型用于ListMap类型参数化:

List:<type>
Map:<keyType,valueType>

示例:

var names = <String>['cxl','zs','ls'];
var weeks = <String,String>{
    'Monday':'星期一''Tuesday':'星期二',
    ...
    'Sunday':'星期日'
}

在构造函数中参数化

Map类型如:var user = new Map<String,String>(); List类型如:var user = new List<User>();

库地使用

引用库

通过import语句在一个库中引用另一个库地文件。需要注意以下事项:

  • import语句后面需要接上库文件地路径。
  • Dart语言提供的库文件使用dart:xx格式。
  • 第三方的库文件使用package:xx格式。 import的例子如下:
import 'dart:io';
import 'package:mylib/mylib.dart';
import 'package:utils/utils.dart';

指定一个库的前缀

当引用的库拥有互相冲突的名字,可以为其中或几个指定不一样的前缀。这与命名空间的概念比较接近,示例代码如下:

import 'package:lib1/lib1.dart';
import 'package:lib1/lib1.dart' as lib2;

void test(){
    Element element1 = new Element();//使用lib1中的Element
    lib2.Element element2=new lib2.Element();//使用lib2中的Element
}

lib1/lib1.dartlib2/lib2.dart里面都有Element类,如果直接引用就不知道具体引用那个Element类,所以代码中把lib2/lib2.dart指定成lib2这样使用lib2.Element就不会发生冲突。

引用库的一部分

如果只需要使用库的一部分内容,可以有选择地使用,有如下关键字:

  • show关键字:只引用一点。
  • hide关键字:除此之外都引用。

示例代码如下:

//导入foo
import 'package:lib1/lib1.dart' show foo;
//除了foo导入其他所有内容
import 'package:lib2/lib2.dart' hide foo;

异步支持

Dart语言是目前少数支持异步操作的语言。一般使用async函数和await表达式实现异步操作。 Dart库提供asynchronous功能,该功能提供接口来进行耗时操作,比如文件读写、网络请求。该功能返回FutureStream对象。 可以通过如下方式来获取asynchronous功能返回地Future对象值:

  • 使用async函数和await表达式。
  • 使用Future功能提供地API。 可以通过如下地方式获取asynchronous功能返回地Stream对象值:
  • 使用async和一个异步的循环(await for)。
  • 使用Stream的相关API。 下面示例代码使用asyncawait异步处理,虽然代码看起像同步处理:
await readFile()

必须在一个使用async关键字标记后的函数中使用await表达式:

void fileOperate() async{
    //读取文件
    readFile()
    //其他操作
    //...
}

元数据(@

使用袁术给代码添加更多的信息,元数据是以@开始的修饰符,在@后面接着编译时的常量或调用一个常量构造函数。目前Dart语言提供三个@修饰符:

  • @deprecated被弃用。
  • @override重写。
  • @proxy代理。

注释

Dart支持三种注释类型

  • 单行注释
  • 多行注释
  • 文档注释

跟JAVA一样