Dart语言学习[转]

数据类型

浮点类型 double

String //大写
int a = 10;

// 同c# $符号意思一样 不同的是要放在字符串内部

String s1 = "可以用${a}";

//字符串前加r 代表这是一个原始字符串

String s1 = r"可以用${a}";

dart 中所有类型都是对象 所有对象的默认值都是null

如果变量的类型是动态的变化的,可以用dynamic声明

dynamic d = "temp";
print(d is String); // true
d = 1;
print(d is int); // true

常量可以用final和const的来声明
final和const都可以做类型的推断
与var的声明方式一样
也可以具体的赋予某个类型
但是不能和var混用。

final和const的区别

  1. final的变量只初始化一次,可以是编译时的常量,也可以不是。const必须是编译时的常量。
  2. final只是在第一次运行的时候确定常量值,const必须指定常量值。比如:final可以是一个函数的计算结果,但是const不行。
  3. const可以修饰值,final不可以。const的值必须是const, final可以不是。
    ps: 编译时常量,是指在程序编译时就可以确定的值。比如:一个确定的数值,或者可以获得结果的数值表达式;一个确定的字符串等等。

List
列表
很简单
具体方法可以查阅文档 https://api.dartlang.org/stable/2.0.0/dart-core/List-class.html
Set
集合
不能指定长度 没有index
Map
键值对

运算符

算术运算符
算数运算符有:+(加)、-(减)、*(乘)、/(除)、~/(除,但结果返回整数)、-expr(负号)以及%(取余)。
关系运算符
关系运算符有:==(等于)、!=(不等于)、>(大于)、<(小于)、>=(大于等于)和<=(小于等于)。
类型测试运算符
类型测试运算符有:is(判断是否某一类)、is!(判断是否不是某一类)和as(将对象作为某一类运行)。
位运算符
位运算符有:&(按位与)、|(按位或)、^(按位异或)、~(按位取反)、<<(按位左移)和>>(按位右移)。
逻辑运算符
逻辑运算符有:&&(与)、||(或)、!(非)。
其他的运算符
?? (是否为空)、expr? val1:val2(三元运算符)、…(级联运算符)、.(成员访问)、?.(有条件的成员访问)、()(函数调用)、

var nullVar;
var intVar = 12;

// 如果 nullVar是空,则赋值intVar

var nullVar;
var intVar = 12;

// 如果 nullVar是空,则赋值intVar

nullVar ??= intVar;
print(nullVar); // 12

// 如果intVar大于10则返回a否则返回b

var strVar = intVar > 10 ? "a": "b";
print(strVar); // a

// … 允许你在同一个对象上连续进行操作

var listVar = [1, 2, 3, 4];
listVar.toSet() // 返回一个set对象
..add(5) // 继续操作上一个set对象
..add(1); // 继续操作上一个set对象
// 相当于
var setVar = listVar.toSet();
setVar.add(5);
setVar.add(1);
 
// ?. 允许左侧的对象为空
var mapVar;
print(mapVar?.length); // null
print(mapVar.length);  // 报错,mapVar是空
print(setVar?.length); 

条件判断和循环基本和c#一样 需要注意asset
ssert语句在产品环境下是没有作用的,只有在开发状态下才会有效果。
flutter需要开启Debug模式; dart或者dartjs命令,需要添加参数–enable-asserts

// assert_test.dart文件中的代码
var intVar = 30;
assert(intVar > 10);
print("int ${intVar}");
assert(intVar < 20);
print("int ${intVar}");
 
// dart assert_test.dart的结果
// int 30
// int 30
 
/
// dart --enable-asserts assert_test.dart 的结果
// int 30
// Failed assertion: line 50 pos 10: 'intVar < 20': is not true.

函数

dart 函数也是一个对象
函数都是基于类Function
函数也可以作为一个参数 传入其他函数中
所有函数都有返回值 没有指定返回值 则返回null

// 定义一个不带返回值的函数,可以带void, 也可以不带
void say(var word){
    print("I say '${word}'";
}
 
// 参数可以不用指定类型,如果不指定,则默认是var
bool isInt(numVar){
    return numVar is int;
}
 
// 如果函数只有一个return 语句可以用 => 简写
// 需要注意 => 后面只能是一个表达式或者是单条语句。
bool isInt2(numVar) => numVar is int;
 
main(){
   say('hello world'); // I say 'hello world'
   print(isInt('a')); // false
   print(isInt(1)); // true
}

可选参数
定义可选参数有两种方式一种使用{}, 另一种使用[]。
区别就是, 如果用{}声明,在使用时必须带着参数名,如:a:123;
如果用[]声明,在使用时需要按照顺序使用

void travel({String from, String to}){
    print("From ${from} to ${to}");
}
void travel2(String from, [String to, String city]){
    print("From ${from} to ${to} and ${city}");
}
 
main(){
    travel(); // From null to null
    // 必须传入的是哪个参数的值
    travel(to:"usa"); // From null to usa;
    travel(from:"china", to:"usa") // From china to usa;
    travel2("china"); // From china to null and null;
    // 按照顺序传入参数值
    travel2("china", "usa"); // From china to usa and null;
    travel2("china", "usa", "england"); // From china to usa and england;
}

可选的参数 也可以使用@required 指定可选参数变成必填参数
要使用@required需要引入包 package:meta/meta.dart,如果是用flutter,则可以引入包package:flutter/material.dart。
void travel({@required String from , String to}){}

参数默认值
ps: 老代码中可能用:来赋予参数默认值 不过将在后续版本中去掉 最好韩式用 =

void travel({String from="china", String to="usa"}){
  print("From ${from} to ${to}");
}
 
// list和map的默认值需要用const修饰
void animals([List<String> names= const ["cat", "dog"], Map<String, String> nameMap = const{
  "c": "cat", 
  "d": "dog"
}]){
  print("animals: ${names}");
  print("nameMap: ${nameMap}");
}
 
main(){
    travel(); // From china to usa;
    travel(from:"japan"); // From japan to usa;
    animals(); // animals:['cat', 'dog'] nameMap: {'c', 'cat', 'd': 'dog'}
}

匿名参数

定义:
([[Type] param1[, …]]) {
codeBlock;
};

main(List<String> args) {
  var listVar = ["cat", 'dog'];
  listVar.forEach((animal){
    print("animal is ${animal}");
  });
  // 如果只有一行代码,可以使用=>
  listVar.forEach((animal) => print("animal is ${animal}"));
}

作用域
var level1 = "level1";
main(List<String> args) {
  var level2 = "level2";
  // 定义一个内部函数
  void testScope(){
    var level3 = "level3";
    print(level1); // level1
    print(level2); // level2
    print(level3); // level3
  }
  // 运行内部函数
  testScope(); 
}

闭包
闭包是一个函数对象,可以读取其他作用域内的变量使用。
// 本实例来自于官网

Function makeAdder(num){
  return (addNum){
    return addNum + num;
  };
}
main(){
  var add5 = makeAdder(5);
  var add10 = makeAdder(10);
 
  print(add5(1)); // 6
  print(add10(1)); // 11
}

main 函数
main函数是整个应用的入口 每个应用必须有一个main函数作为入口
main函数没有返回值 并且有一个list类型的变量

捕获异常
使用on可以捕获到某一类的异常,但是获取不到异常对象; catch可以捕获到异常对象。这个两个关键字可以组合使用。

try{
    testException();
  } on FormatException catch(e){ // 如果匹配不到FormatException,则会继续匹配
    print("catch format exception");
    print(e);
    rethrow; // 重新抛出异常
  } on Exception{ // 匹配不到Exception,会继续匹配 
   print("catch exception") ;
  }catch(e, r){ // 匹配所以类型的异常. e是异常对象,r是StackTrace对象,异常的堆栈信息
    print(e);
  }

finally
finally 是内部语句 无论是否有异常 都会执行

testException(){
  throw FormatException("this is exception");
}
 
main(List<String> args) {
  try{
    testException();
  } on FormatException catch(e){
    print("catch format exception");
    print(e);
    rethrow;
  } on Exception{
   print("catch exception") ;
  }catch(e, r){ 
    print(e);
  }finally{
    print("this is finally"); // 在rethrow之前执行
  }
}

每一个对象都有一个runtimeType的属性,通过这个属性可以获取到该对象的类型(Type的对象)。

main(){
    // 返回的是一个Type的对象
    print(Animal().runtimeType); // Animal
}
class Animal(){String name;}

实例变量
如果一个变量类型不是final类型的 那么实例变量都会有隐式的Get Set方法 不然就只有Get方法
如果一个实例变量有初始值 那么他的值在构造方法生效前 就已经被赋予
如果想重写Get和Set方法,可以用关键字get和set实现
以_开头的变量和方法 都是私有的

 // 官网实例代码
class Rectangle {
  num left = 10;
  num _left_width = 12; // 私有变量
  
  // 定义多个变量
  num top, width, height;
 
  Rectangle(this.left, this.top, this.width, this.height);
 
  // 定义两个计算属性: right and bottom.
  // 需要注意的是,get和set需要成对出现
  // 重写get
  num get right => left + width;
  // 重写set
  set right(num value) => left = value - width;
  num get bottom => top + height;
  set bottom(num value) => top = value - height;
}
 
void main() {
  var rect = Rectangle(3, 4, 20, 15);
  assert(rect.left == 3);
  rect.right = 12;
  assert(rect.left == -8);
}

构造函数
造函数的定义,可以通过函数名为ClassName和ClassName.identifier(命名的构造函数 Named Constructors)定义。

通过命名的构造函数可以轻松实现多个不同的构造函数。

如果类没有构造函数,那么默认会有一个没有参数的构造函数。

class Animal{
 
  String name;
 
  String color = "yellow";
  
  Animal(String name){
    this.name = name;
  }
  // 上面的构造函数,可以简写为
  Animal(this.name);
 
  Animal.createInstance(Map values){
    name = values["name"];
  }
 
  run(){
    print("${this.name} is running");
  }
}
main(List<String> args) {
  var animal = Animal.createInstance({"name": "dog"});
  print(animal.name);
}

子类构造函数
父类中的命名构造函数不能被子类继承。如果想要子类也拥有一个父类一样名字的构造函数,必须在子类是实现这个构造函数。默认情况下,子类只能调用父类的无名,无参数的构造函数。父类的无名构造函数会在子类的构造函数前调用。如果initializer list 也同时定义了,则会先执行initializer list 中的内容,然后在执行父类的无名无参数构造函数,最后调用子类自己的无名无参数构造函数。即下面的顺序:

initializer list(初始化列表)
superclass’s no-arg constructor
main class’s no-arg constructor
如果父类不显示提供无名无参数构造函数的构造函数,在子类中必须显式调用父类的一个构造函数。这种情况下,调用父类的构造函数的代码放在子类构造函数名后,子类构造函数体前,中间使用:(colon) 分割。
在开发状态下,初始化列表可以使用assert表达式。

class Animal{
 
  String name;
 
  String color = "yellow";
 
  Animal.createInstance(Map values){
    print("in animal");
    name = values["name"];
  }
 
  run(){
    print("${this.name} is running");
  }
}
 
// extends 继承的关键字
class Bird extends Animal{
  String name = "blue bird";
  String desc;
 
  Bird.createInstance(Map values):
    desc = name, // 初始化列表,注意这里没有this 
    super.createInstance(values){ // 父类方法调用使用super
      print("in bird");
    }
 
}
 
main(List<String> args) {
 var bird = Bird.createInstance({
   "name": "bird"
    });
 print(bird.name); // bird
 print(bird.desc); // blue bird
}

重定向构造函数
所谓重定向构造函数,是指在一个构造函数中指向另一个构造函数,但是重定向构造函数的函数体是空的。以官网代码为例。

class Point {
  num x, y;
 
  // 主构造函数
  Point(this.x, this.y);
 
  // 重定向构造函数
  Point.alongXAxis(num x) : this(x, 0);
}

静态构造函数
如果类的对象不会有任何变化,那么可以让这些对象是编译时常量。通过创建静态构造函数,并且所有成员属性是final的来实现。

静态构造函数,并不是都会返回一个编译时常量。

class Animal{
  final String name;
  final String color;
 
  // 静态构造函数
  const Animal(this.name, this.color);
}
 
main(List<String> args) {
  var cat = const Animal("cat", "yellow");  // 常量
  var cat2 = const Animal("cat", "yellow");
  var cat3 = Animal("cat", "yellow"); // 非常量
    
  // 构造两个相同的编译时常量,实际上是一个常量
  print(identical(cat, cat2)); // true
  print(identical(cat, cat3)); // false
  // 在常量上下文中,可以省略构造函数或文字前面的常量
  const cats = const {
    'bluscat': const [const Animal("cat", "blue")],
    'yellowcat': const [const Aminal("cat", "yellow")],
  };
  // 上面的代码可以写成下面这样,这是Dart2的新特性
  const cats2 = {
    'bluscat': [Animal("cat", "blue")],
    'yellowcat': [Aminal("cat", "yellow")],
  };
}

工厂构造函数
使用关键字final实现工厂构造函数。工厂构造函数可以让你不必要每次都创建一个新的对象,就好像是有一个缓存,缓存了老的对象。

工厂构造函数中不能使用this。

下面是官网的实例:

class Logger {
  final String name;
  bool mute = false;
 
  // 私有变量
  static final Map<String, Logger> _cache =
  <String, Logger>{};
 
  factory Logger(String name) {
    if (_cache.containsKey(name)) {
      return _cache[name];
    } else {
      final logger = new Logger._internal(name);
      _cache[name] = logger;
      return logger;
    }
  }
 
  Logger._internal(this.name);
 
  void log(String msg) {
    if (!mute) {
      print(msg);
    }
  }
}
void main() {
  var p1 = Logger("Part1");
  p1.log("this is part1");
 
  var p2 = Logger("Part2");
  p2.log("this is part2");
}

抽象类和方法
使用关键字abstract声明的类,就是抽象类。抽象方法是没有实现体的方法,抽象方法只能存在于抽象类中。

实例方法、Setter和Getter方法都可以是抽象方法。

抽象类不能被实例化。如果想要抽象类进行实例化,可以用工厂构造函数实现。

抽象类通常用来定义接口。

abstract class Animal{
  String color = "blue";
 
  // 抽象方法
  run();
 
}
 
class Bird extends Animal{
  
  String name;
 
  Bird(this.name);
  // 抽象方法的实现
  run(){
    print("A ${this.color} ${this.name} is flying");
  }
}
 
main(List<String> args) {
  var bird = Bird("tom");
  bird.run();
}

隐式接口
Dart2中的每一个类都有一个隐式的接口,这个隐式的接口包含所有的实例成员和实现的接口的实例成员。

如果你想创建一个类A支持类B的API函数,但是不想继承B的实现,则类A需要继承类B的接口。

继承接口的关键字是implements, 一个类可以继承多个接口。

class Animal{
  String _name; // 在接口中,但是是私有的,所以只能在本类中使用
 
  Animal(this._name); // 构造函数不在接口中
 
  run(){
    print("${this._name} is running");
  }
 
}
 
// 实现Animal的接口,如果还有其他的接口,可以用,分割 implements Animal, ...
class Bird implements Animal{
  String _name;
  Bird(this._name);
  run(){
    print("${this._name} can not run");
  }
}
 
runningAnimal(Animal animal){
  animal.run();
}
 
main(List<String> args) {
  runningAnimal(Animal("dog")); // dog is running
  runningAnimal(Bird("bird"));  // bird can not run
}

类的继承
使用extends来继承父类,super来指定父类。

子类可以重载父类的实例方法、Setter和Getter,使用@override注释重新的方法。

重载noSuchMethod() 方法,可以在对象视图调用不存在的方法或者变量时调用。但是,你不能调用一个不存在的方法,除非是以下情况的一种:

  1. 一个静态类型的dynamic的变量;

  2. 接收方有一个定义未实现的方法的静态类型(抽象的也可以),并且接收方是dynamic并且有一个noSuchMethod()的实现,与类Object中的实现不同。

class Animal{
 
  String name;
 
  String color = "yellow";
 
  Animal.createInstance(Map values){
    print("in animal");
    name = values["name"];
  }
 
  run(){
    print("${this.name} is running");
  }
}
 
// extends 继承的关键字
class Bird extends Animal{
  String name = "blue bird";
  String desc;
 
  Bird.createInstance(Map values):
    desc = name, // 初始化列表,注意这里没有this 
    super.createInstance(values){ // 父类方法调用使用super
      print("in bird");
    }
    
   @override
   run(){
     pirnt("${this.name} can not run");
  }
  @override
  void noSuchMethod(Invocation invocation) {
    print('You tried to use a non-existent member: ' +
        '${invocation.memberName}');
  }
}

枚举
枚举类型是一种特殊的类,通常用来表示相同类型的一组常量。使用关键字enum定义枚举。

枚举的每一个值都有一个index属性,index从0开始计数。

枚举不能被继承,不能创建实例。

enum Animal {
  cat,
  dog,
  bird
}
 
main(List<String> args) {
  print(Animal.dog.index); // 1
  // 获取所有枚举值
  List<Animal> animals = Animal.values;
  Animal dog = Animal.dog;
  switch (dog) {
    case Animal.cat:
      print("animal is cat");
      break;
    case Animal.dog:
      print("animal is dog");
      break;
    default:
      print("which animal?");
  }
  // animal is dog
}

mixins
mixins是给类添加新的特性的方式,也是一种重用类代码的一种方式。

mixins的关键字是with。详细的使用,可以查看文档。

with不能单独使用,必须跟着extends使用。

class Fly{
  fly(){
    print("flying");
  }
}
class Animal{}
 
class Bird extends Animal with Fly{
 
}
 
main(List<String> args) {
  var bird = Bird();
  bird.fly(); // flying
}

类变量和方法
使用关键字static,声明类变量和类方法

class Animal{
 
  static var name; // 类变量
  static const color = "yellow"; // 类常量 
 
  // 类方法
  static run(){
    print("running");
  }
 
}
 
main(List<String> args) {
  Animal.name = "dog";
  print(Animal.name); // dog
  Animal.run(); // running
}

库的引入
使用import关键字引入库。引入库有如下三种方式:

  1. 引入同库文件,直接使用库的路径: import “Path/name.dart”;

  2. 引入Dart语言内部的库,路径前需要添加dart: imprrt “dart:html”;

  3. 引入第三方库,路径前需要添加package: import “package:Path/name.dart”。

引入的库可以用as来重命名,防止不同的库变量名冲突。当使用了别名,那么在调用引入的库中的对象时,需要带着别名,如:asName.ClassName。

使用show和hide关键字,可以单独引入或者剔除库中的某一个API。
import "hello.dart"; // 引入同目录下的hello.dart文件
import "http://xxx/xx.dart"; // 也可是url,但是不推荐
import "dart:html"; // 引入Dart2中的html库
import "package:lib1/lib1.dart"; // 引入lib1包下的lib1.dart
import "package:lib2/lib2.dart" as lib2; // 引入lib2包下的lib2.dart, 并重命名为lib2
import "package:lib3.dart" show Foo; // 只引入lib3.dart中的Foo对象
import "package:lib4.dart" hide Foo; // 除了Foo对象,引入其他所有lib4.dart中的对象
// 使用Element 来自lib1
Element element1 = Element();
 
// 使用Element 来自lib2
lib2.Element element2 = lib2.Element();

库的拆分
我可以将一个比较大的库文件拆成几个比较小的文件,这些文件可以通过part关联。每一个part关联的文件,它们之间共享所有的命名空间,同时共享所有的对象,包括私有的对象。

如果使用了part拆分,库必须使用library声明。

PS: part的引入文件,也可以使用url但是不推荐。如: part “http://xxx/xx.dart”;

// 创建文件test.dart
library testall;
import "dart:html";
 
part("test1.dart"); // 关联test1.dart文件
part("test2.dart");
 
// 创建test1.dart
part of testall; // 声明是testall的一部分,不必再引入dart:html,可以直接使用
 
// 创建test2.dart
part of testall; // 声明是testall的一部分
延迟加载
使用deferred as 关键字,可以实现库的延迟加载。使用loadLibrary()进行加载,可以多次调用,但是只会执行一次,它返回一个Future对象。

为什么使用延迟加载?

  1. 减少APP的启动时间;

  2. 延迟加载很少使用的功能

import 'package:greetings/hello.dart' deferred as hello; // 延迟加载的声明,默认重命名
 
Future greet() async {
  await hello.loadLibrary();
  hello.printGreeting();
}

异步

Dart库中的大多数函数都返回了Future和Stream对象。这些函数都是异步的,它们在设置一个可能耗时的操作(比如I/O)之后返回,而无需等待操作完成。

实现异步的关键字是: async和await。

异步函数
使用async来声明一个异步函数,异步函数都会返回一个Future对象。

如果没有返回任何内容,可以声明为Future。

异步函数在调用时,会立即返回,不会等程序体执行完毕。

Future lookUpVersion() async => ‘1.0.0’;
Futrue
如果你需要一个Future对象,需要满足下面两点:

  1. 使用async和await;

  2. 调用Future的API。

await必须在async声明的函数体中使用,使用方式: await

expression通常是一个Future对象,如果不是,会自动包装成一个Future对象

Future checkVersion() async {
  var version = await lookUpVersion();
  // Do something with version
}
await可以使用try,catch,finally来处理异常和清理代码

try {
  version = await lookUpVersion();
} catch (e) {
  // React to inability to look up the version
}
// await可以使用多次

var entrypoint = await findEntrypoint();
var exitCode = await runExecutable(entrypoint, args);
await flushThenExit(exitCode);

Stream

如果你想从一个Stream中获取值,你需要满足下面两个条件:

  1. 使用async和一个异步循环(await for);

  2. 使用Stream的API。

// expression必须是一个Stream对象
await for (varOrType identifier in expression) {
  // Executes each time the stream emits a value.
}

异步循环执行流程:

等待Stream发出一个值;
执行 for 循环的主体,把变量设置为发出的值;
重复 1 和 2,直到 Stream 关闭。
在异步循环中,break和continue还是有效的。

转自 白菜大叔
原文:https://blog.csdn.net/shanyuu/article/details/83008881

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值