Dart 入门教程

[TOC]git

1、开篇

dart 语言具备以下特性github

  • 一切变量皆是对象,每一个对象都是类的实例。int、double、函数、 null 等都是对象,全部对象都继承自 Object 类
  • dart 是强类型语言,但因为具有类型推导功能因此类型声明是可选的
  • dart 支持顶级函数、静态函数、实例函数,也容许在函数中嵌套函数,即局部函数。相似的,dart 也支持顶级变量、静态变量和实例变量
  • dart 没有关于 public、protected、private 的关键字。经过为变量标识符添加下划线前缀,代表该标识符对其库是私有的
  • ....

先来看个小例子编程

/** * 多行注释 */
void printString(String msg) {
  print("msg value: $msg");
}

void main() {
  var msg = "Hello, World!";
  printString(msg); //msg value: Hello, World!
  printString(null); //msg value: null
}
复制代码

如上代码包含了 dart 语言(也是基本全部编程语言)的基本元素markdown

  • 多行注释和单行注释
  • 以分号结尾且必需有
  • 容许定义顶层函数
  • 最基础的数据类型之一:String,其它的内置数据类型还有 int 、double、list、map 等
  • 类型推导。经过关键字 var 来声明变量而无需指明变量类型
  • 一种方便的插入变量值的方式,字符串字面值:$msg
  • 应用程序的入口:main 函数

2、变量

2.一、变量声明

与 Java 语言相比,dart 语言包含的相似的基本数据类型只有 intdouble 两种,且这两种类型的变量均是对象,其默认值均为 null编程语言

本教程遵循官方风格指南建议,大部分例子都是使用 var 来声明变量ide

void main() {
  int value;
  print(value); //null

  value = 10;
  print(value); //10

  var varValue = 20;
  print(varValue); //20
}
复制代码

dart 语言是强类型语言,没法将一个已声明具体变量类型的变量赋值为另外一个无继承关系的变量函数

例如,第二行代码是会致使报错的,没法将一个 double 值赋值到一个 int 类型变量上学习

int intValue = 20;
  intValue = 20.0; //error
复制代码

但因为 intdouble 类都是 num 类的子类,因此如下操做是合法的ui

num numValue = 10;
  print(numValue.runtimeType); //int
  numValue = 10.22;
  print(numValue.runtimeType); //double
复制代码

2.二、dynamic

dynamic 相似于 Java 中的 Objectdynamic 对象能够用来指向任意类型变量,非 null 的 dynamic 变量会有具体的运行时类型this

dynamic value = "leavesC";
  print(value.runtimeType); //String
  value = 12121;
  print(value.runtimeType); //int
复制代码

2.三、final 和 const

若是你但愿一个变量在赋值后其引用不能再改变,能够经过 final 或 const 这两个关键字来实现。const 变量表明的是编译时常量,在编译期,程序运行前就有肯定值了,所以实例变量不能使用 const 修饰。而 final 修饰的变量是运行时常量,能够在运行时再赋予变量值,所以实例变量能使用 final 修饰

void main() {
  const URL = "https://github.com/leavesC/JavaKotlinAndroidGuide";
  var booleValue = true;
  final name = getName(booleValue);
  print(name);
}

String getName(boolValue) {
  if (boolValue) {
    return "leavesC";
  } else {
    return "leavesC =_=";
  }
}
复制代码

3、内建类型

3.一、num

dart 的数字类型有 int 和 double 两种,这两种都是 num 类的子类

int 类型根据平台的不一样,整数值不大于64位。在 Dart VM 上,值能够从 -263 到 263-1,编译成 JavaScript 的 Dart 使用JavaScript 代码,容许值从 -253 到 253 - 1

double 类型即64位双精度浮点数,由 IEEE 754标准 指定

void main() {
  var intValue = 100;
  print(intValue.runtimeType); //int

  var doubleValue = 100.0;
  print(doubleValue.runtimeType); //double

  num numValue = 100;
  print(numValue.runtimeType); //int
  numValue = 100.0;
  print(numValue.runtimeType); //double
}
复制代码

一些常见的数字类型转换方法

print(num.parse("2000"));
  print(int.parse("200"));
  print(double.parse("121"));
复制代码

3.二、string

除了能够经过单引号或者双引号来声明一个字符串外,也能够经过相邻的字符串字面量来声明一个组合字符串(至关于使用 + 把字符串相加为一个总体)

var stringValue = "leavesC";

  var stringValue2 = 'leavesC =_=';

  var stringValue3 = "分段 "
      "换行了也能够"
      '又换了一行';

  print(stringValue3); //分段 换行了也能够又换了一行
复制代码

此外,也可使用带有单引号或双引号的三重引号,包含的转义字符将会生效

var stringValue4=''' \n 换行符 \t 制表符 ''';
复制代码

也能够用 r前缀 建立一个原始字符串,包含的转义字符将会失效

var stringValue4=r''' \n 换行符 \t 制表符 ''';
复制代码

3.三、bool

dart 语言也用 bool 关键字来表示事物的真假,只有两个对象具备 bool 类型:true 和 false,它们都是编译时常量。且 dart 是强 bool 类型检查,只有 bool 类型的值是 true 才被认为是 true

3.四、list

list 也是最多见的数据类型之一,dart 经过方括号来声明 list 变量

因为 dart 具备类型推导功能,所以 listValue 自动被赋予为 List 类型,所以在声明 listValue 后就没法为其添加其余类型变量的值了

var listValue = [1, 2, 3];
  // listValue.add("4"); error
  print(listValue.runtimeType); //List<int>
复制代码

若是想要为 List 添加不一样数据类型的变量,则须要直接指明数据类型为 Object

var listValue = <Object>[1, 2, 3];
  listValue.add("4");
  print(listValue.runtimeType); //List<Object>
复制代码

大多数时候为了限制 List 的可存储数据类型,在使用时就直接指明数据类型

var intList = <int>[1, 2, 3, 4];
复制代码

若是在声明 List 时调用了其指定集合大小的构造函数,则集合大小就是固定的了,列表的长度不能在运行时更改

var list = List<int>(2);
  list.add(2); //error,会致使抛出 Unsupported operation: Cannot add to a fixed-length list
复制代码

要建立一个编译时常量列表,则在列表字面量以前添加 const 关键字

var constantList = const [1, 2, 3];
  //error,可正常编译,但会致使运行时抛出异常
  //constantList[0] = 2;
  //constantList.add(2);
复制代码

3.五、set

Set 是一种不包含重复数据的数据集合,使用方式上和 List 基本相似

void main() {
  var list = [1, 2, 2, 3, 4, 5, 5];
  var set = Set.from(list);
  print(set); //{1, 2, 3, 4, 5}
}
复制代码

3.六、map

map 是一个关联键和值的数据类型,键和值能够是任何类型的对象

void main() {
  var mapValue = {"name": "leavesC", "age": 24};
  mapValue["url"] = "https://github.com/leavesC";
  print(mapValue); //{name: leavesC, age: 24, url: https://github.com/leavesC}
  print(mapValue.length); //3
  print(mapValue.runtimeType); //_InternalLinkedHashMap<String, Object>
}
复制代码

也能够限定 map 能够存储的数据类型

var mapValue = <String, String>{"name": "leavesC"};
复制代码

与 list 相似,要建立一个编译时常量的 map 须要在 map 的字面量前加上 const 关键字

var mapValue = const {"name": "leavesC", "age": 24};

  //error,可正常编译,但会致使运行时抛出异常
  mapValue["name"] = "hi";
复制代码

4、函数

dart 是一种真正的面向对象语言,因此即便函数也是对象,即变量能够指向函数,也能够将函数做为参数传递给其余函数

4.一、通常概念

通常,为了方便调用者理解,函数须要指明其接受的参数类型,但也容许不指明参数类型,此时函数依然能够正常调用

void main() {
  printMsg("leavesC");

  printMsg2(100);
  printMsg2("leavesC");
}

void printMsg(String msg) {
  print(msg);
}

void printMsg2(msg) {
  print(msg);
}
复制代码

若是函数只包含一个表达式,则可使用简写语法

void printMsg3(msg) => print(msg);
复制代码

全部函数均有返回值,若是没有指明函数的返回值类型,则函数默认返回 null

void main() {
  print(printValue(121) == null); //true
}

printValue(value) {
  print("value is: $value");
}
复制代码

4.二、函数也是对象

在 dart 中,能够用变量来引用函数对象、向函数传递函数参数、建立函数对象

void main() {
  var printUserFun = printName;
  printUserFun("leavesC"); //name: leavesC

  var list = ["leavesC", "叶"]; 
  list.forEach(printName); //name: leavesC name: 叶

  var sayHelloFun = (String name) => print("$name , hello");
  sayHelloFun("leavesC"); //leavesC , hello
}

void printName(String name) {
  print("name: $name");
}
复制代码

4.三、位置参数

位置参数即该参数可传也可不传,当不传时该参数值默认为 null,位置参数用中括号包裹起来

void main() {
  printUser("leavesC"); //name: leavesC, age: null
  printUser("leavesC", 25); //name: leavesC, age: 25
}

void printUser(String name, [int age]) {
  print("name: $name, age: $age");
}
复制代码

4.四、命名参数

命名参数,即在调用该函数时需同时标明该参数的参数名,命名参数用花括号包裹起来,以 {type paramName} 或者 {paramName: type} 两种方式声明参数,调用命名参数时只能以 funcName(paramName: paramValue) 的形式来调用。且命名参数可传也可不传值,当不传指时该参数值为 null

void main() {
  printUser("leavesC"); //name: leavesC, age: null
  printUser("leavesC", age: 25); //name: leavesC, age: 25
  printUser2("leavesC", age: 25); //name: leavesC, age: 25
}

void printUser(String name, {int age}) {
  print("name: $name, age: $age");
}

void printUser2(String name, {age: int}) {
  print("name: $name, age: $age");
}
复制代码

4.五、默认参数值

和 kotlin 相似,dart 语言也支持为位置函数和命名参数设置默认值,默认值必须是编译时常量,若是没有提供默认值,则默认值为 null

void main() {
  printUser("leavesC"); //name: leavesC, age: 30
  printUser("leavesC", 25); //name: leavesC, age: 25

  printUser2("leavesC"); //name: leavesC, age: 30
  printUser2("leavesC", age: 25); //name: leavesC, age: 25
}

void printUser(String name, [int age = 30]) {
  print("name: $name, age: $age");
}

void printUser2(String name, {int age = 30}) {
  print("name: $name, age: $age");
}
复制代码

4.六、函数变量

前面说了,dart 是一种真正的面向对象语言,即便函数也是对象,即变量能够赋予函数类型

void main() {
  var printFunction = printUser;
  printFunction("leavesC");
  print(printFunction); //Closure: (String, [int]) => void from Function 'printUser': static.
}

void printUser(String name, [int age = 30]) {
  print("name: $name, age: $age");
}
复制代码

也能够将函数做为参数传递给另一个函数

void main() {
  var list = {1, 2, 3};
// value is: 1
// value is: 2
// value is: 3
  list.forEach(printValue);
}

void printValue(value) {
  print("value is: $value");
}
复制代码

4.七、匿名函数

匿名函数即不具有函数名称的函数,在函数只使用一次会就再也不调用时使用匿名函数会比较方便

void main() {
  var list = {1, 2, 3};
// value is: 1
// value is: 2
// value is: 3
  list.forEach((element) {
    print("value is: $element");
  });
  list.forEach((element) => print("value is: $element"));
}
复制代码

4.八、局部函数

局部函数即嵌套于其余函数内部的函数

void main() {
  var list = {1, 2, 3};
// value is: 2
// value is: 3
// value is: 4
  list.forEach((element) {
    int add(int value1, int value2) {
      return value1 + value2;
    }
    print("value is: ${add(element, 1)}");
  });
}
复制代码

5、运算符

dart 提供了一些比较简便的运算符来简化操做,大部分和 Java 相同,如下介绍下几个不一样的运算符

void main() {
  //is 用于判断变量是不是指定的数据类型
  //is! 含义是 is 取反
  var strValue = "leavesC";
  print(strValue is String); //true
  print(strValue is int); //false
  print(strValue is! String); //false

  // ~/ 用于除法运算时取整,/ 则不取整
  print(10 / 3); //3.3333333333333335
  print(10 ~/ 3); //3

  //as 用于强制类型转换
  num numValue = 10;
  int intValue = numValue as int;

  //若是 ??= 左边的变量值为 null ,则将其赋值为右边的值
  var name = null;
  var age = 25;
  name ??= "leavesC";
  age ??= 30;
  print("name:$name"); //name:leavesC
  print("age: $age"); //age: 25

  //若是 ?. 左边的变量值不为 null,则右边的操做生效
  //用于避免发生空指针异常
  var area = null;
  print(area?.runtimeType); //null

  //级联运算符 .. 用于连续操做某个对象,而无需每次操做时都调用该对象
  var list = [1, 2, 3, 4, 5];
  list
    ..insert(0, 6)
    ..removeAt(4)
    ..add(7);
  print(list); //[6, 1, 2, 3, 5, 7]

  //扩展运算符 ... 和 空值感知扩展运算符 ...?
  //提供了一种将多个元素插入集合的简洁方法
  var list1 = [1, 2, 3];
  var list2 = [0, ...list1];
  print(list2); //[0, 1, 2, 3]
  //若是 list3 可能为 null,此时则须要使用空值感知扩展运算符,不然会抛出异常
  //空值感知扩展运算符只有当 list3 不为 null 时才会执行插值操做
  var list3 = null;
  var list4 = [0, ...?list3];
  print(list4); //[0]
}
复制代码

6、流程控制

dart 的流程控制的语义和逻辑大多和 Java 相同

void main() {
  //if
  int value = 20;
  if (value < 10) {
    print("value < 10");
  } else if (value < 30) {
    print("value < 30");
  } else {
    print("value unknown");
  }

  //while
  int score = 10;
  while (score < 100) {
    score++;
  }

  //switch
  String strValue = "leavesC";
  switch (strValue) {
    case "ye":
      break;
    case "leavesC":
      print(strValue);
      break;
    default:
      print("default");
      break;
  }

  //for
  var list = [];
  for (int index = 1; index < 10; index++) {
    list.add(index.toString());
  }
  for (var item in list) {
    print("循环遍历:$item");
  }
}
复制代码

此外也有一些比较奇特的操做,能够经过条件 if循环 for 来构建集合

var hasName = true;
  var info = {if (hasName) "name": "leavesC", "age": 24};
  print(info); //{name: leavesC, age: 24}
复制代码
var list = {1, 2, 3};
  var info = {for (var item in list) "$item", 4};
  print(info); //{1, 2, 3, 4}
复制代码

7、枚举

枚举用于定义命名常量值,使用enum关键字来声明枚举类型

enum State { RESUME, STOP, PAUSE }

void main() {
  var state = State.PAUSE;
  print(state);
  State.values.forEach((state) {
    print(state);
  });
}
复制代码

8、异常处理

dart 语言能够抛出和捕获异,捕获异常能够避免程序运行崩溃,与 Java 不一样,dart 的全部异常均是未检查异常,方法没必要声明自己可能抛出的异常,也不要求调用方捕获任何异常。dart 提供了 Exception 和 Error 类型,以及许多预约义的子类型,开发者也能够本身定义异常。并且,dart 能够抛出任何对象,不只仅是 Exception 和 Error 两类

例如,以下代码就表示 throwException 方法内部捕获了 RangeError ,但对其余异常不进行处理

void throwException() {
  try {
    List<String> stringList = new List();
    stringList.add("value");
    print('${stringList[1]}');
  } on RangeError {
    print("抛出了异常...");
  }
}

void main() {
  throwException();
}
复制代码

若是在抛出异常时须要异常对象,则须要用到 catch

void throwException() {
  try {
    List<String> stringList = new List();
    stringList.add("value");
    print('${stringList[1]}');
  } on RangeError catch (e) {
    print("${e.message}"); //Invalid value
    print("${e.runtimeType}"); //RangeError
  } catch (e) {
    print("若是异常没有被上方捕获,则会统一被此处捕获");
  }
}
复制代码

也可使用两个参数的写法,第二个参数表明堆栈跟踪(StackTrace对象)

void throwException() {
  try {
    List<String> stringList = new List();
    stringList.add("value");
    print('${stringList[1]}');
  } on RangeError catch (e, s) {
    print("${s.toString()}");
  }
}
复制代码

也能够在捕获异常后将异常再次抛出

void throwException() {
  try {
    List<String> stringList = new List();
    stringList.add("value");
    print('${stringList[1]}');
  } on RangeError catch (e, s) {
    print("${s.toString()}");
    rethrow;
  }
}
复制代码

相似 Java,finally用于确保在抛出异常时某些代码也能够被执行

void throwException() {
  try {
    List<String> stringList = new List();
    stringList.add("value");
    print('${stringList[1]}');
  } on RangeError catch (e, s) {
    print("throwException ${e.message}");
    rethrow;
  } finally {
    print("finally");
  }
}

void main() {
  try {
    throwException();
  } catch (e) {
    print("main ${e.message}");
  }
// throwException Invalid value
// finally
// main Invalid value
}
复制代码

此外,dart 也容许 throw 任何对象,包括 null

void throwException() {
  try {
    List<String> stringList = new List();
    stringList.add("value");
    print('${stringList[1]}');
  } on RangeError catch (e, s) {
    throw null;
    //or throw "发生了异常";
  }
}

void main() {
  try {
    throwException();
  } catch (e) {
    print("main ${e}"); //main Throw of null.
    print("${e.runtimeType}"); //NullThrownError
  }
}
复制代码

此外,因为抛出异常是一个表达式,因此如下写法是合乎语法的

int throwException() => throw "";
复制代码

9、类

9.一、类声明

dart 是一门彻底面向对象的语言,其关于类的内容和 Java 较为相似

class Person {
  
  String name;

  int age;

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

}
复制代码

dart 会自动为 nameage 提供隐式的 gettersetter 方法,且未经初始化的实例变量均为 null

在 dart 2 中 new 关键字成为了可选关键字,所以能够选择省略 new 声明,这一点和 kotlin 相同

void main() {
  var person = Person("leavesC", 25);
  print('${person.name} ${person.age}'); //leavesC 25
  print('${person.runtimeType}'); //Person
}
复制代码

9.二、构造函数

dart 为用于赋予初始值的构造函数提供了简便写法,如下两种写法的语义是一致的

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

  Person(this.name, this.age);
复制代码

此外,dart 也提供了命名构造函数,用于方便调用者生成不一样用途或含义的变量

Person.getDefault() {
    this.name = "leavesC";
    this.age = 25;
  }
复制代码

也能够在构造函数主体运行以前初始化实例变量

Person.getDefault()
      : name = "leavesC",
        age = 25 {}
复制代码

所以当想要获取一个默认的 Person 实例时就如同在调用 Person 的一个静态方法

void main() {
  var defaultPerson = Person.getDefault();
  print('${defaultPerson.name} ${defaultPerson.age}'); //leavesC 25
  print('${defaultPerson.runtimeType}'); //Person
}
复制代码

9.三、继承

默认状况下,子类中的构造函数会隐式调用父类的未命名的无参数构造函数,父类的构造函数在子类构造函数体的开始处被调用。若是父类没有未命名的无参数构造函数,则必须手动调用父类中的构造函数

此外,构造函数不能被子类继承,父类中的命名构造函数不会被子类继承,因此若是子类也想要拥有一个和父类同样名字的构造函数,则必须子类本身实现这个构造函数

class Man extends Person {
  
  Man(String name, int age) : super(name, age);

  Man.getDefault() : super.getDefault();
  
}
复制代码

9.四、抽象类

dart 语言的抽象类和 Java 基本一致

abstract class Printer {
  void print(String msg);
}

class HpPrinter extends Printer {
  @override
  void print(String msg) {
    // TODO: implement print
  }
}
复制代码

9.五、接口

dart 没有用来专门声明接口的语法,类声明自己就是 dart 中的接口,实现类使用implements关键字来实现接口,实现类必须提供目标接口的全部功能的具体实现,即类必须从新定义它但愿实现的接口中的每个函数

void main() {
  var human = Human();
  human.eat();
  human.speak();
}

class Human implements Eat, Speak {
  void funA() {}

  @override
  void eat() {
    // TODO: implement funB
  }

  @override
  void speak() {
    // TODO: implement funC
  }
}

class Eat {
  void eat() {}
}

class Speak {
  void speak() {}
}
复制代码

9.六、mixins

mixins 是一个重复使用类中代码的方式

void main() {
  var c = C();
  c.funA();
  c.funB();
}

class A {
  void funA() {}
}

class B {
  void funB() {}
}

//使用 with 关键字表示类C是由类A和类B混合构成的
class C = A with B; 复制代码

10、未完待续

本文已加入个人学习笔记大全:JavaKotlinAndroidGuide

相关文章
相关标签/搜索