@artman328
2022-05-19T14:46:45.000000Z
字数 14273
阅读 613
dart
Dart 是静态类型语言。
Dart 程序的入口是一个叫做 main 的函数。它有如下结构:
void main(){
...
}
Dart 程序的源代码文件以 .dart 为扩展名,它可以编译成操作系统本地机器码运行,也可编译成 JavaScript 语言代码有 JavaScript 解释器解释执行。
编译成本地代码有两种方式:JIT(Just In Time,即时编译) 和 AOT(Ahead Of Time,预先编译)。
JIT编译由 Dart 虚拟机载入源代码文件后,编译一句执行一句,直到执行完毕。
AOT 编译先将源代码文件编译成一个单独可执行的程序,需要时直接执行此程序即可。此执行程序可脱离 Dart 虚拟机运行(事实上,运行支撑环境已经被编译到可执行程序中了)。
(1)下载 Dart SDK
从 Dart 的官方网站下载 Dart 的 SDK(Software Development Kit,软件开发工具包),将其解压到某个文件夹,如:D:\dart-sdk
。
(2)环境配置
为了能够在任何位置执行到 dart 的命令,应将 dart 所在路径添加到系统或用户环境变量 PATH。以下命令将其路径添加到用户环境变量 PATH。
setx PATH "D:\dart-sdk\bin;%PATH%"
(3)安装程序编辑器
到 Visual Studio Code 官网 下载 Visual Studio Code,安装。
打开 Visual Studio Code,安装 dart 插件。
(4)测试开发环境
创建一个文件夹(如:D:\DartWork),用 Visual Studio Code 打开,在其中创建一个文件,如:test.dart,输入以下内容:
void main(){
print("Welcome to dart!");
}
然后运行。如果看到输出:
Welcome to dart!
说明开发平台搭建成功。
(1)num (number, 数值)
数值型,包括整型(int)和双精度浮点型(double)。
数值型的文字表达是:100,0x98ef(十六进制), 1.23,2.35e-3(),5.67E5()等等。
void main(){
print(0x98ef); // output: 39151
print(2.35e-3); // output: 0.00235
}
(2)String(字符串)
文字性信息是字符串类型。
字符串的文字表达,需要用引号括起来。如:
'单引号括起来的字符串'
"双引号括起来的字符串"
'单引号里有"双引号"是可以的'
"双引号里有'单引号'也是可以的"
"同级引号在文字里需要\"如此\"处理才行。"
void main(){
print("Hello!"); // output: Hello!
print('"No, thank you!", she said.'); // output: "No, thank you!", she said.
print("\"No, thank you!\", she said."); // output: "No, thank you!", she said.
}
(3)bool(布尔型,逻辑型)
布尔型的文字表达只有两个:true
(真),false
(假)。
void main(){
print(true); // output: true
print(false); // output: false
}
(4)list(列表,数组)
列表是一组数据(可限定所有单个数据的统一类型)。文字表达:用[]
将一组数据括起来,单个数据之间用逗号分隔。如:
[12,56,23,33,56,109,'ok']
["Hello","Hi",100,"What's up","Hello"]
(5)set(集合)
集合是一组不重复的数据(可限定所有单个数据的统一类型)。文字表达:用{}
将一组数据括起来,单个数据之间用逗号分隔。如:
{'ok',12,56,23,false,33,109}
{"Hello","Hi","What's up",true,100}
(6)map(映射)
映射在有的语言里称为字典、对象等。它是一组名称和值构成的数据,如:
{'name':'Billy','gender':'Male','birth_date':'1999-12-31','salary':3000}
(7)null
null 是个特殊的值,它表示这是个未知的、无意义的值。
变量(variable)是用于装载数据的命名容器,它对应着内存中某个存储位置。
变量命名规则:
1.变量只能由数字、字母、下划线和美元符($)组成;
2.注意:标识符开头不能是数字;
3.标识符不能是保留字和关键字;
4.变量的名字是区分大小写的;
5.标识符(变量名称)一定要见名思意“变量名称建议用名词,方法名称建议用动词。
变量声明时需要指明它能装载的数据类型(或 var, 指明让编译器根据第一次装载的数据来推断)。赋值是将数据装入变量容中。没有赋值的变量的值就是 null。如果允许一个变量的值是 null,在变量声明时就要说明,方法是在他的类型后加上一个问号。
以下是声明和赋值的例子。
Dart 是静态类型语言,是指变量只能装载指定的数据类型的数据。
num temperature; // 声明了一个用于装载数值(整数、小数均可)的容器,名称是 temperature。
temperature = 37; // 赋值,将右边的 37 赋给左边的变量 temperature
temperature = 37.5;
int count;
count = 10;
count = 10.5; // 错
double weight = 12; // 声明和赋值同时做了
var name = "Billy"; // 编译器被要求自行推断变量 name 的类型。在此它会被推断为字符串类型。
name = 12; // 错
String? gender; // gender 可以是 null
bool isReady = false; //
List data = [12.34,100,'OK',false];
List<String> seasons = ['sparing','summer','fall','winter']; // <类型>用于指定装载的数据类型
Set<String> colors = {'blue', 'green', 'red'};
Map emp_info = {'name': 'Bill', 'age': 20, 'education': 'master'};
运算符 | 含义 | 举例 |
---|---|---|
+ | 加 | 2 + 2 => 4 |
- | 减 | 100 - 80 => 20 |
* | 乘 | 2 * 10 => 20 |
/ | 除 | 100 / 20 => 5 |
% | 模除,取余 | 33 % 10 => 3 |
+= | 累加(a += b 相当于 a = a + b) | int a = 10; a+=5; a=>15 |
-= | 递减(a -= b 相当于 a = a - b) | int b = 10; b-=5; b=>5 |
++ | 增1 (a++ 相当于 a = a + 1) | int a=10; a++; a=>11 |
-- | 减1 (a-- 相当于 a = a - 1) | int a=10; a--; a=>9 |
运算优先级:[*, /, %] > [+, -]
运算符 | 含义 | 举例 |
---|---|---|
+ | 字符串相连 | "Hello, " + "Bill!" => "Hello, Bill!" |
${} | 变量替换 | var name= "Bill"; "Hello, ${name}!" => "Hello, Bill!" |
运算符 | 含义 | 举例 |
---|---|---|
+ | 列表相连 | [1,2] + [3,4] => [1,2,3,4] |
比较运算的结果为 bool 型数据:true(真) 或 false(假)。
运算符 | 含义 | 举例 |
---|---|---|
> | 大于 | 5.6>5 => true 5.6<5 => false |
>= | 大于或等于 | 100>=100 => true 100>=101 => false |
< | 小于 | |
<= | 小于或等于 | |
== | 等于 | 123==123 => true 123==123.00 => true "Hello"=="Hello" => true [1,2,3]==[1,2,3] => false {1,2,3}=={1,2,3} => false {'a':1,'b':2,'c':3}=={'a':1,'b':2,'c':3} => false |
!= | 不等于 |
逻辑运算的操作数是 bool 型数据:true(真) 或 false(假)。
逻辑运算的结果为 bool 型数据:true(真) 或 false(假)。
运算符 | 含义 | 举例 |
---|---|---|
&& | 与,并且 | true && true => true 相当于 1 × 1 = 1 true && false => false 相当于 1 × 0 = 0 false && true => false 相当于 0 × 1 = 0 false && false => false 相当于 0 × 0 = 0 结论:全真才真 |
|| | 或,或者 | true || true => true 相当于 1 + 1 = 1(非零就是真) true || false => true 相当于 1 + 0 = 1 false || true => true 相当于 0 + 1 = 1 false || false => false 相当于 0 + 0 = 0 结论:全假才假 |
! | 非,取反 | !true => false !false => true |
运算优先级:! > && > ||
位运算的操作数是整数,结果也是整数。数据位为 1 相当于 true,数据位为 0 相当于 false
运算符 | 含义 | 举例 |
---|---|---|
& | 按位与 | 138 & 15 => 10 (去高半字节,保留低半字节) 10001010 & 00001111 = 00001010 |
| | 按位或 |
面向对象是一种编程方法。
在面向对象的编程方法中,程序需要处理的一切事物都是一种生命体,他有自己的特征和所属物,并且有处理自己事务的能力。
该编程方法的思想是:不同的事物之间的互动完成了一个业务过程。它模拟的是人类社会中各个个体(人或单位或部门)之间的互动完成了事务管理过程。这些个体在协作中通过互通消息、处理自己的数据并交换数据来完成事务的处理。
在社会生活中,这些个体根据自己的职责被划分成了不同的类别。比如:学校、警察局、工厂、教务处、人事部等。
以下是一个典型的业务过程:
将事物以特征进行分类描述,就构成类。比如以下是对“会议”这种事物所进行的描述:
它有这样一些属性:会议名称、会议地点、召开时间、时长、参会人员等;
它有这样一些行为:登记与会者、统计与会者、宣布开始、宣布结束等。
而以下是一个具体的会议的描述:
会议名称:2021年度董事会议
会议地点:公司第一会议室
召开时间:2021年12月31日上午9:00
时长:3小时
参会人员:所有董事会成员
……
我们把对一类事物的共同描述成为“类”(class),而把一类事物中的具体个体叫做“对象”(object),也可称为实例(instance)。
因此,以上对会议的描述就是对“会议类”的描述,名为“2021年度董事会议”的会议就是一个“会议类”的具体“对象”或“实例”。
事物的分类是具有层次结构的,比如:“交通工具”是一个“类”,“汽车”也是一个“类”,“越野车”也是一个“类”,他们就具有了一种所属关系:“越野车”属于“汽车”的下级分类(术语叫“子类”),而“汽车”有又属于“交通工具”的一个下级分类。以后我们会看到,这种关系叫“继承”(inheritance)。
在Dart 中,所有的数据都是某种数据类的一个对象。因为是类的对象,它们就有了自己的属性和行为。
在程序中查看一个对象的属性的语法是:对象.属性
,可将“.”读作“的”,理解为“对象的属性”。
以下例子查看一个“双精度浮点数类(double)”的对象(temperature)的属性 isNegative (是否负值),用了temperature.isNegative
(温度的是否负数属性):
void main(){
double temperature = -18;
print(temperature.isNegative); // output: true
}
在程序中查看一个对象的属性的语法是:对象.属性
,可将“.”读作“的”,理解为“对象的属性”。
在程序中让对象进行某个行为的语法是:对象.行为([数据])
,其中的“数据”是传递给对象处理的数据,有的行为并不接收数据。
以下例子让一个 List 类的对象 colors 进行 add (添加)行为,并把数据 “blue” 传递给它。然后查看它的“length”(长度,数组中数据的个数)属性。
void main() {
var colors = ["red", "green"];
colors.add("blue");
print(colors.length); // output: 3
}
表达式是能够求出一个结果的代码。
如:amount = price * count;
age>18;
等。
语句是程序中一个完整的执行单元。语句可以是一条单独语句,也可以是多条语句构成的语句块(用大括号{}
括住)。
Dart的语句以;
号结束(语句块后面不需要分号)。
常见的语句有以下几种。
赋值语句用于给变量赋值:
变量 = 表达式;
作用是将右侧表达式的值赋予左侧的变量。
条件判断语句根据条件执行相应的语句或语句块。
if(条件){
... // 如果条件满足执行此部分代码
}
else{
... // 否则执行此部分代码
}
条件判断语句可以按照逻辑进行多条件组合或嵌套。如:
if(条件1){
...
}
else if(条件2){
...
}
else if(条件3){
...
}
...
else{
}
if(条件1){
if(条件2){
...
}
else{
...
}
}
else{
...
}
循环语句用于在某种条件下重复做某事。
它有以下几种形式。
a) 计数循环
如:
for(int i=0; i<10; i++){
... // 循环体
// 若碰到 continue; 语句,直接回到第 1 行
// 若碰到 break; 语句,直接跳出循环到第5行以后
}
循环过程:
b) 前置条件循环
while(条件满足){
... // 循环体
// 若碰到 continue; 语句,直接回到第 1 行
// 若碰到 break; 语句,直接跳出循环到第5行以后
}
循环过程:
b) 后置条件循环
do{
... // 循环体
// 若碰到 continue; 语句,直接回到第 1 行
// 若碰到 break; 语句,直接跳出循环到第5行以后
}while(条件);
循环过程:
根据表达式取值,决定执行的语句或语句块。
switch(表达式){
case 取值1:
...
break;
case 取值2:
...
break;
case 取值3:
...
break;
case 取值4:
case 取值5:
case 取值6:
... // 取值4、取值45、取值46均执行此部分代码
break;
default:
...
}
函数,是完成某种功能(算法)的可重复使用的程序片断。
函数有三个要素:函数名、接收的参数和返回的数据类型。
以下语句定义了一个接收两个整型数据(对位传递的位置参数a和b)、并返回一个整型数的名为add
的函数,用于得到两个整数的相加的值:
int add(int a, int b){
return a + b;
}
void main(){
print(add(10,20)); // output: 30
// add(10,20) 为函数调用,
// 参数对位传递:
// 将10(实际参数)赋予参数a(形式参数), 20(实际参数)赋予参数b(形式参数)
}
// 缺省参数写在最后,用[]号括住
int sum(int a, int b, [int c = 0, int d = 0]){
return a + b + c + d;
}
void main(){
print(sum(10,20)); // output: 30
print(sum(10,20,30)); // output: 60
print(sum(10,20,30,40)); // output: 100
}
命名参数需要在传递实际参数时指明形式参数的名称。
double get_payment(double price, {double discount:1.0, double deduction:0.0}){
return price * discount - deduction;
}
void main(){
print(get_payment(1000.00, deduction:100, discount:0.90)); // output : 800.0
// ------- ------------- -------------
// 位置参数 命名参数 命名参数
}
如果函数体只是一个表达式,可用箭头形式的方式进行定义。
int add(int a, int b) => a + b;
// 相当于:
// int add(int a, int b){
// return a + b;
// }
void main(){
print(add(10,20));
}
void main(){
var data = [10,20,16,22,36];
data.forEach((e)=>print(e));
data.forEach(
(e){
e = e*10;
print(e);
}
);
}
类是一种自定义的数据类型。
class Company{
String name = "";
String address = "";
String telephone = "";
// 构造函数
Company(String name, String address, String telephone){
this.name = name;
this.address = address;
this.telephone = telephone;
}
// 另一个构造函数(带别名)
Company.namedValues({String name:"", String address:"", String telephone:""}){
this.name = name;
this.address = address;
this.telephone = telephone;
}
String describe(){
return "Name: ${this.name}\nAddress: ${this.address}\nTelephone: ${this.telephone}";
}
}
void main(){
Company company1 = Company("New Tech 1","Northen Avenue #1900","1234-45678769");
Company company2 = Company.namedValues(
telephone:"1234-45678769",
name:"New Tech 2",
address:"Northen Avenue #1900"
);
print(company1.describe());
print(company2.describe());
}
用构造函数简洁写法(short-hand)改写以上程序。
class Company{
String name = "";
String address = "";
String telephone = "";
// 构造函数
Company(this.name, this.address, this.telephone);
// 另一个构造函数(带别名)
Company.namedValues({this.name:"", this.address:"", this.telephone:""});
// 或 Company.namedValues({this.name="", this.address="", this.telephone=""});
String describe(){
return "Name: ${this.name}\nAddress: ${this.address}\nTelephone: ${this.telephone}";
}
}
void main(){
Company company1 = Company("New Tech 1","Northen Avenue #1900","1234-45678769");
Company company2 = Company.namedValues(
telephone:"1234-45678769",
name:"New Tech 2",
address:"Northen Avenue #1900"
);
print(company1.describe());
print(company2.describe());
}
类的成员包括属性和方法。如果类的成员名称开始于下划线,它们对外界就是不可见的。
class Company {
String name = "";
String address = "";
String telephone = "";
Company(this.name, this.address, this.telephone);
String describe(){
return "Name: ${this.name}\nAddress: ${this.address}\nTelephone: ${this.telephone}";
}
}
void main(){
Company company = Company("","Northen Avenue #1900","1234-45678769");
print(company.describe());
// 以下随意修改成员变量
company.name = "";
company.address = "";
company.telephone = "";
print(company.describe());
}
为避免直接访问成员变量(主要避免非法赋值、越权取值等):
class Company {
String _name = "";
String _address = "";
String _telephone = "";
// setter
void set name(String name){
_checkEmpty("Name",name);
_name = name;
}
// getter
String get name => _name;
void set address(String address){
_checkEmpty("Address",address);
_address = address;
}
String get address => _address;
void set telephone(String telephone){
_checkEmpty("Telephone",telephone);
_telephone = telephone;
}
String get telephone => _telephone;
void _checkEmpty(name,value){
if(value.trim().isEmpty){
throw new Exception("${name} can't be empty!");
}
}
Company(String name, String address, String telephone){
if(name.trim().isEmpty || address.trim().isEmpty || telephone.trim().isEmpty){
throw Exception("All fields must not be empty!");
}
this._name = name;
this._address = address;
this._telephone = telephone;
}
String describe(){
return "Name: ${this._name}\nAddress: ${this._address}\nTelephone: ${this._telephone}";
}
}
void main(){
Company company = Company("New Tech 1","Northen Avenue #1900","1234-45678769");
//Company company = Company("","Northen Avenue #1900","1234-45678769");
company.name = "";
print(company.describe());
}
类的继承可实现代码复用。
class Vehicle {
String name = "";
String brand = "";
Vehicle(this.name, this.brand);
String describe(){
return "Vihecle: Name - ${this.name}, Brand - ${this.brand}";
}
}
class Car extends Vehicle{
int seatCount = 0;
Car(String name, String brand, int seatCount):super(name,brand){
this.seatCount = seatCount;
}
@override
String describe(){
return "Car: Name - ${this.name}, Brand - ${this.brand}, Seat Count - ${this.seatCount}";
}
}
class Truck extends Vehicle{
int loadInTons = 0;
Truck(String name, String brand, int loadInTons):super(name,brand){
this.loadInTons = loadInTons;
}
@override
String describe(){
return "Truck: Name - ${this.name}, Brand - ${this.brand}, Load - ${this.loadInTons} Tons";
}
}
void main(){
Car car = Car("CRV","HONDA",5);
print(car.describe());
Truck truck = Truck("BIG-FOOT","NISSAN",30);
print(truck.describe());
}
抽象类包含了一些无法实现的行为。
abstract class Shape{
double area(); // 无法实现的抽象行为
String describe(){ // 已实现有公共行为
return "I'm one of shapes.";
}
}
class Circle extends Shape{
double radius = 0;
Circle(this.radius);
@overrride
double area(){
return radius * radius * 3.14;
}
}
class Rectangle extends Shape{
double length = 0;
double width = 0;
Rectangle(this.length, this.width);
@override
double area(){
return length * width;
}
}
void main(){
Circle circle = Circle(10);
print(circle.area());
Rectangle rectangle = Rectangle(10,10);
print(rectangle.area());
}
在 dart 中,任何类的定义都隐含一种接口(方法集合)定义。如果一个类要实现另外一个类,意味着它要自己实现这另一个类的所有方法。
abstract class Shape{
double area();
String describe(){ // 这个方法的实现不会被“实现者”使用
return "I'm one of shapes.";
}
}
class Circle implements Shape{
double radius = 0;
Circle(this.radius);
double area(){
return radius * radius * 3.14;
}
}
class Rectangle implements Shape{
double length = 0;
double width = 0;
Rectangle(this.length, this.width);
double area(){
return length * width;
}
}
void main(){
Circle circle = Circle(10);
print(circle.area());
print(circle.describe()); // Error
Rectangle rectangle = Rectangle(10,10);
print(rectangle.area());
print(rectangle.describe()); // Error;
}
因此,可定义一个抽象类,在其中声明多个方法,作为一个让“实现者”遵循的契约:你要实现我,请实现我的所有方法!
可用父类(接口)声明的变量装载子类对象,从而实现同一方法进行不同的行为——多态。
注意:用父类变量装载子类对象后,子类对象特有的方法和属性将不可见。
abstract class Shape{
double area();
String describe();
}
class Circle extends Shape{
double radius = 0;
Circle(this.radius);
@override
double area(){
return radius * radius * 3.14;
}
@override
String describe(){
return "Circle: Radius - ${this.radius}";
}
}
class Rectangle extends Shape{
double length = 0;
double width = 0;
Rectangle(this.length, this.width);
@override
double area(){
return length * width;
}
@override
String describe(){
return "Rectangle: Length - ${this.length}, Width - ${this.width}";
}
}
void main(){
List<Shape> shapes = [];
Circle circle1 = Circle(10);
Rectangle rectangle1 = Rectangle(10,10);
Circle circle2 = Circle(12);
Rectangle rectangle2 = Rectangle(12,12);
Circle circle3 = Circle(14);
Rectangle rectangle3 = Rectangle(14,14);
shapes.addAll([circle1,rectangle1,circle2,rectangle2,circle3,rectangle3]);
shapes.forEach((s){
print(s.area());
print(s.describe());
});
}
用父类(接口)装载子类对象,可方便实现对象替换,从而灵活地实现功能替换。
以下程序中,只要把实现了 OutputDevice 类的接口的对象传递给 Article,它就会调用对象的 output 方法进行输出。
class Article {
String text = "";
OutputDevice? outputDevice;
Article(this.text, this.outputDevice);
void output(){
this.outputDevice?.output();
}
}
abstract class OutputDevice{
void output();
}
class Printer implements OutputDevice{
void output(){
print("Output to a printer!");
}
}
class Screen implements OutputDevice{
void output(){
print("Output to the screen!");
}
}
void main(){
OutputDevice printer = Printer();
OutputDevice screen = Screen();
Article article1 = Article("Hello!", printer);
article1.output();
Article article2 = Article("Hello!", screen);
article2.output();
}
Dart属于单继承,所有类的顶级类实 object 类。
下级类继承了上级类的所有特性(属性和行为)。
import 'package:http/http.dart' as http;
import 'package:http/retry.dart';
Future<void> main() async {
final client = RetryClient(http.Client());
try {
print(await client.read(Uri.parse('http://www.163.com')));
} finally {
client.close();
}
}
SELECT id, name, tavg, salary
FROM (SELECT id, name, salary,
AVG(salary) AS avg_salary
FROM employees
GROUP BY id) sre,
(SELECT (AVG(salary)) AS tavg
FROM employees) ta
WHERE sre.avg_salary > ta.tavg
;
SELECT name, price, havg
FROM (SELECT name, price, AVG(price) AS avg_price
FROM book
GROUP BY name) tab1,
(SELECT (AVG(price)) AS havg
FROM book) tab2
WHERE tab1.avg_price=tab2.havg
;