Flutter之Dart语言基础

Dart语言的诞生

2011年10月,Google 发布了一种新的编程语言 Dart,谷歌希望利用这款语言,帮助程序开发者克服JavaScript语言的缺点,并使Dart支持所有项目,从小型松散的项目到Gmail和谷歌文档这种大型复杂的项目。Dart 语言可以说是集百家之长,拥有其他优秀编程语言的诸多特性和影子,另外,最重要的是,Dart成为了Flutter的官方开发语言~

Dart的特性

编译模式:JIT和AOT

我们都知道,程序在运行之前通常都需要编译,JIT和AOT是最常用的两种编译方式

  • JIT:即时编译,在开发时使用,可以动态下发和执行代码,开发测试效率高,但运行速度和性能会收到影响;
  • AOT:预先编译(静态编译),可以生成被直接执行的二进制代码,运行速度快、执行性能表现好,但每次执行前都需要提前编译,开发测试效率低;
    总结:JIT适合在开发测试时使用,AOT适合在发布时使用,而Dart是少数同时支持JIT和AOT的语言,Flutter非常受欢迎的功能热重载正是基于JIT,因此Dart 具有运行速度快、执行性能好的特点。
单线程模式

我们都知道,支持并发执行线程的高级语言(C++,Java等),都是以抢占式的方式切换线程,每个线程都会被分配一个固定的时间片来执行,这样当多线程操作更新共享资源时,就可能导致数据不同步的问题,虽然可以加锁来保证同步,但是锁本身会带来性能消耗,并且还可能出现死锁等比较严重的问题;这时候Dart单线程模式的优势就体现出来了,他不会存在资源竞争和数据同步的问题,一旦某个函数开始执行,直到这个函数执行完成,都不会被其他Dart代码打断;

基础语法和类型变量

变量和类型

在Dart中用var或具体类型来声明一个变量,用var声明表示类型是由编译器推断决定,或者你可以用具体类型来声明如:int x=0; 显式的告诉编译器你定义的变量类型;
默认情况下未初始化的变量值都是null,Dart是类型安全语言,所有类型都是对象类型(Object),一切变量的值都是类的实例;
Dart 内置了一些基本类型,如 num、bool、String、List 和 Map;
num:有两个子类int(整形)和double(浮点型)

int x = 10;
double y = 1.5;

bool类型,只有两个对象具有bool类型:true和false,他们都是编译时常量;
String由UTF-16的字符串组成,字符串变量既可以用单引号也可以用双引号表示,还可以在字符串中嵌入变量或表达式;可以用${express}的方式,将表达式放入字符串中,如果是一个标识符,可以省略{}

  var text = 'monkey';
  var text1 = 'this is :$text';
  var text2 = 'this is :${text.toUpperCase()}';
List和Map

就是我们常见的集合或数组和字典类型,在Dart中统称为集合类型,我们用个例子来展示一下List和Map的使用

  var arr1 = ["one", "two", "three"];
  var arr2 = List.of([1, 2, 3]);

  var map1 = {"name": "jiao", "sex": "male"};
  var map2 = new Map();
  map2['name'] = 'jiao';
  map2['sex'] = 'male';

容器里的元素有类型,比如arr2的类型是List,map2则为Map,Dart会自动根据上下文来推断;
和Java类似,在初始化集合对象时,我们可以为集合添加类型约束(泛型)如:

  var arr1 = ["one", "two", "three"];
  var arr2 = List.of([1, 2, 3]);

  var map1 = {"name": "jiao", "sex": "male"};
  var map2 = new Map();

这样是不是更清晰~

常量

Dart中常量的定义可以用const和final关键字来声明
const:表示在编译期就能确定的值
final:表示在运行时可以确定的值
两者区别
const i = 1 (正确) const i =x+y(错误)
const必须直接赋一个字面量,而不能是一个变量或者表达式;
final i =1;(正确) final i =x+y(正确)
final则无以上限制,但是final的值一旦确定,就不能更改了;

函数

函数定义

Dart中所有的类型都是对象类型,函数也是对象,类型是function,这说明他可以定义为变量,甚至可以定义为参数传递给另一个函数;

  bool isZero(int num) {
    return num == 0;
  }

  void printInfo(int num, Function check) {
    print('$num is Zero:${check(num)}');
  }

  Function f = isZero;
  int x = 10;
  int y = 0;
  printInfo(x, f); // 输出 10 is Zero: false
  printInfo(y, f); // 输出 0 is Zero: true

首先定义了一个判断是否为0的函数isZero,然后定义了一个打印判断结果的函数printInfo,可以看到第二个函数的入参为第一个函数;
如果函数体只有一行表达式,可以像JavaScript那样用箭头函数来简化这个函数

  bool isZero(int num) => num == 0;

  printInfo(int num, Function check) => print('$num is Zero:${check(num)}');
函数可变参数

我们知道在C++或者Java中,函数支持重载,即提供同名但参数不同的函数,但在Dart中并不支持重载,但Dart提供了可选命名参数和可选参数;

  • {} 可选命名参数,以name:value 的方式指定参数;
  • [] 可选参数 ,意味着这些参数可以忽略;
  //{}设置可选命名参数
  addValue({int x, int y}) {
    print("x=$x,y=$y");
  }

  addValue(x: 10, y: 20);
  addValue(x: 10); //y可选 默认null
  addValue(y: 20); //x可选 默认null

  //{}可选命名参数设置默认值
  addValue1({int x, int y = 1}) {
    print("x=$x,y=$y");
  }

  addValue1(x: 10); //y可选 已设默认值1

  //[]可选参数
  printValue(bool x, [bool y]) {
    print("x=$x,y=$y");
  }

  printValue(true); //y可忽略 默认null
  printValue(true, false);

  //[]可选参数 默认值
  printValue1(bool x, [bool y = true]) {
    print("x=$x,y=$y");
  }

  printValue1(true); //y可忽略 已设默认值 true

通过上面的示例代码可以看出,既然都是可选参数,两者有什么区别呢?主要有两点区别:
1.{}访问是以 name:value的形式 []则不是
2.{}无需按参数顺序写参数 []则严格按照参数顺序写入参数

Dart是面向对象的语言,每个对象都是类的一个实例,都继承自顶层的Object,Dart中并没有public,protected,privite这些关键字,在声明变量和方法是加上""表示private,如果不加"",则默认是public的,这里要注意"_"的限制级别是库级别的,而不是类级别;

class Apple {
  num x, y;
  static num z = 0;

  //语法糖,等同于在函数体内:this.x = x;this.y = y;
  Apple(this.x, this.y);

  void printXY() => print("x=$x,y=$y");

  static void printZ() => print("z=$z");
}

在Apple类中,定义了两个成员变量x和y,通过构造函数语法糖进行初始化,而类变量z在声明时就已经赋好了默认值。
类的构造方法也可以使用可选命名参数和可选参数,另外Dart还支持命名构造函数 ,使类的实例化过程语义更清晰。

class Apple {
  num x, y, z;

  Apple(this.x, this.y) : z = 0;//初始化变量z

  Apple.start(num x) : this(x, 0);//重定向构造函数 调用上面的构造方法

  void printXYZ() => print("x=$x,y=$y,z=$z");
}

Apple类中有两个构造函数Apple和Apple.start,Apple.start将其成员变量初始化重定向到了Apple中,而Apple在初始化时为z赋上了默认值0

复用

在其他面向对象的语言中,我们要想复用其他类的变量和方法通常有两种方式继承和接口实现,在Dart中同样也可以:
继承: 继承自父类,子类由父类派生,会自动获得父类的方法和变量,子类也可以根据需要复写父类的构造函数或方法;
接口: 接口实现的话,子类主要获取到的是父类的方法或变量符号,需要自己实现父类的方法和变量,更多的是父类对子类进行约束;
下面,我们看一下在Dart中继承和接口的区别

class Apple {
  num x, y;
  void printValue() => print("x=$x,y=$y");
}

class Apple1 extends Apple {
  num z;
  @override//复写 printValue方法
  void printValue() => print("x=$x,y=#y,z=$z");
}

class Apple2 implements Apple{
  //成员变量需要重新声明
  @override
  num x;

  @override
  num y;

  @override
  void printValue() {
    //成员函数需要重新实现
    // TODO: implement printValue
  }

}

可以看到,继承的话和其他语言都差不多,Apple2是可以采用接口的方式(implements)实现一个类的,然而得到的仅仅是父类的一个空壳子,从语义层面可以当作Apple来用,父类中所有的成员变量和方法都需要重新实现;那有没有什么机制,我们是以非继承的形式实现一个类,并且能复用父类的方法和变量而无需重新声明?答案是肯定的,在Dart中有一个概念Mixin,即混入关键字with修饰,可以实现上述功能,可以被看成是具有实现方法的接口,说白了就是和继承一样的功能,但又不是继承;可以理解为为了解决多继承而出现的一种新的类之间关系。

class Apple1 extends Apple {
  num z;

  @override //复写 printValue方法
  void printValue() => print("x=$x,y=#y,z=$z");
}

class Apple2 with Apple {
  num z;

  @override //复写 printValue方法
  void printValue() => print("x=$x,y=#y,z=$z");
}

看,是不是和继承一模一样的作用

运算符

Dart和其他大多数语言一样,运算符基本作用都是一致的,我们这里只特殊说明几个比较常用但又并是其他语言都有的运算符,类似kotlin中的非空判断

  • ?. 如:apple?.printValue(); 这个比较好理解,当apple不为空时,执行printValue方法;
  • ??= 如:a??=value,如果a为null 赋给a的值为value,否则跳过;
  • ?? 如:我们在java中经常使用的 a!=null?a:b;如果a不等于null返回a的值,否则返回b,在Dart中我们可以直接使用:a??b;
    在Dart中一切皆对象,包括运算符也是对象成员函数的一部分
    Dart提供了类似C++的运算符复写机制,我们可以复写或自定义运算符;
class Apple {
  num x;

  Apple(this.x);

  num operator +(Apple a) => this.x + a.x * 5;
}

  Apple a1 = new Apple(10);
  Apple a2 = new Apple(20);

  print("a1+a2=${a1 + a2}");

输出结果:a1+a2=110
我们来分析一下:Apple类重写了运算符+,当Apple类进行+号运算时,如上例:a1+a2,首先取a1的x值10+a2的x值20乘以5 返回,所以最后得出的结果是110

总结

最后我们来总结一下以上内容

  • Dart语言的诞生,并了解了Dart语言的一些特性,编译模式,单线程模式等;
  • Dart语言的基础语法以及变量类型,List和Map的使用等;
  • 函数的定义,函数的可变参数,可选命名参数和可选参数以及两者的区别;
  • Dart中的类,类的复用的方式,及“混入”概念的理解;
  • Dart中的运算符,主要是非空判断的一些运算符使用,以及如何复写运算符;

你可能感兴趣的:(Flutter之Dart语言基础)