DART概述
概念
Dart 是由谷歌开发的通用的编程语言,它常用于构建web、服务器、桌面和移动应用程序。
Dart是一种简洁、清晰、基于类的面向对象的语言,它是基于JavaScript的但是结构要比JavaScript要多。
Dart是一种面向对象的、类定义的、垃圾回收语言。它支持接口、mixin、类对象,具有化泛型、静态类型等。
- 在dart中,能够放在变量中的所有内容都是对象,每个对象都是一个类的实例。甚至于数字、函数和null值都是对象,并且所有对象都继承自Object类;
- Dart是强类型语言,但类型标识是可选的,因为Dart可以推断类型,所以可以直接用数据类型定义变量,但是也可以用var声明变量进行类型推断;
- 能够放在变量中的所有内容都是对象,所以如果一个变量没有初始化值,那它的默认值就为null;
- 在dart中,即使是函数也是对象,并且具有类型Function类型;
- 与Java不同,Dart没有关键字public、protected和private。如想设置私有变量或函数,则变量和函数名以下划线(_)开头;
- dart中,没有函数重载;
特性
1.Dart是AOT(Ahead Of Time)编译的,可编译成快速,可预测的本地代码。
2.Dart可以JIT(Just In Time)编译,开发周期快,为Flutter热重载提供基础。
3.Dart可以轻松创建60fps运行的流畅动画和转场。
4.Dart的声明式编程布局,易于阅读和可视化,不需要单独的声明式布局语言,如:XML,JSX。
5.Dart非常易于学习,具有静态和动态语言,编程人员都熟悉的特性。
Dart的内置库
包名 | 描述 |
---|---|
dart:asynv | 异步编程,提供Future,Stream类 |
dart:collection | 集合 |
dart:convert | 不同类型的字符编码解码 |
dart:core | Dart语言核心功能,内置类型 |
dart:html | 网页开发用到的库 |
dart:io | 文件读写,IO相关 |
dart:math | 数字常量以及函数,随机算法等 |
dart:svg | 事件和动画矢量图支持 |
变量
命名
在Dart语言当中,定义变量的方式是
dataType viriableName = Initial Valute
这里其实和Java是一样的,不仅如此连命名的方式也和Java相同,都是采用驼峰命名的方式。
声明
在声明的时候,除了基本数据类型之外,还有三种变量的声明: var
,dynamic
,Object
var:如果没有初始值的时候,var可以变成任意类型。
dynamic:动态任意类型,编译阶段不检查类型。
Object: 动态任意类型,在编译阶段检查类型。
和var的区别:
var如果有初始值,那么类型会被锁定。
void main(){
// a 的数据类型可以随意改变,这种类型就是 dynamic
var a ;
a = 10;
a = "dart";
// 这里我们使用 dynamic 声明
dynamic d = 10;
d = 'dart';
}
默认值
1.没有初始值的变量会自动获取一个默认值null。
2.一切皆为对象,对象的默认值是null。
私有变量
- 与Java不同,Dart没有关键字public、protected和private。如想设置私有变量或函数,则变量和函数名以下划线(_)开头
final和const
表示不可改变
const
变量是一个编译时常量,final
变量在第一次使用时被初始化
相同点:
- 声明的类型可以省略
- 初始化后不能再赋值
- 不能和var同时使用
不同点:
- 类级别常量,使用static const
- const可使用其他const 常量的值来初始化其值
- 使用const赋值声明,const可以省略
- 可以更改非final、非const变量的值。即使曾经具有const值
- const导致的不可变性是可以传递的
- 相同的const常量不会再内存中重复创建
- const需要是编译时常量
基本数据类型
8种基本数据类型:
Number
数值型里面有包含了int型和doubule两种类型,这两种和Java类似,其中int是整数型,double是64-bit双精度浮点数,这两种都是Number类型的子类。
void main() {
num a = 10; // 整形
a = 20.1; // 浮点型
print(a);// 20.1
int i = 10;
// i = 10.1; // 这个地方会报错,因为将 int 型的数据改为 double 型 向下转型
// i = int.parse('10.1'); // 字符串转换为int类型
i = 10.1.round();// 通过round()方法将double转换为int
print(i); // 10
double d = 20.1;
d = 20; // 这个地方不会报错,因为将 double 型的数据改为 int 型,向上转型
// 向上转型 : (小范围)---》(大范围),这种属于自动转换
// 向下转型 : (大范围)---》(小范围),这种属于强制转换
print(d); // 20.0
}
数值型的操作
运算符: +、 - 、* 、/ 、 ~/ 、 %
常用属性: isNaN、isEven、isOdd
常用方法:abs()、round()、floorl()、ceil()、toInt()、toDouble()
String
Dart字符串是UTF-16编码的字符序列。
1.可以使用单引号或者双引号来创建字符
void main() {
print("This is a String");
print('This is also a stirng');
}
这两个是一样的。
单双引号成对嵌套
void main() {
String str = '单引号中的"双引号"';
String str1 = "双引号中的'单引号'";
print(str); // 单引号中的"双引号"
print(str1); //双引号中的'单引号'
String str2 = '单引号中的 \'单引号\' ';
String str3 = "双引号中的 \"双引号\" ";
print(str2); //单引号中的 '单引号'
print(str3); //双引号中的 "双引号"
}
2.字符串拼接
void main() {
String s1 = "First string.....";
String s2 = " Second string";
print(s1 + s2); // First string..... Second string
// 使用空格即可拼接字符串
// 使用的是空格拼接,多个空格也可以
String str1 = '单引号空格字符串' '拼接' '~'; // 单引号字符串空格拼接~
// 使用换行符以及空格
String str2 = '单引号符串'
'换行了'
'再加空格'
'拼接'; // 单引号符串换行了再加空格拼接
// 单双引号 空格拼接
String str3 = "单双引号空格字符串" '拼接' "~"; // 单双引号字符串空格拼接~
// 单双引号 换行符以及空格
String str4 = "单双引号符串"
'换行了'
'再加空格'
'拼接'; // 单双引号符串换行了再加空格拼接
// 下面这两种情况会报错
// String blockStr4 = '单引号''''空格';
// String blockStr5 = "双引号""""空格";
}
3.字符插值
${exprsssion},如果表达式是一个标识符,可以省略{},如果表达式的结果为一个对象,Dart会调用对象的toString()函数来获取一个字符串。
void main() {
String s1 = "First string.";
print("String 后面拼接 $s1");// String 后面拼接 First string.
print("The sum of 1 and 1 equals ${1 + 1}.");// // The sum of 1 and 1 equals 2.
}
4.多行显示
使用双引号创建多行字符
还可以使用(’)和(")
var s = 'Firtst'
'Second'
"Third";
print(s);// FirtstSecondThird
使用三引表示多行字符
var multilineString = """This is a
multiline string
consistiong of
multiple lines""";
print(multilineString);
/* This is a
multiline string
consistiong of
multiple lines */
5.使用r前缀创建“原始raw”字符串
String s4 ="adbchi\ndfafa";
print(s4);// 转译 /n
String s5 =r"adbchi\ndfafa";
print(s5);// 不转译 /n
//输出的结果
adbchi
dfafa
adbchi\ndfafa
Bool
Dart的bool和Java类似只有两种类型,一种是true一种是false,但是,不同的是bool对象未初始化的默认值是null。
List
-
实例化数组
// 使用构造创建 var list = new List(); //创建一个int类型的list List intlist = [1, 2, 3]; // 创建一个常量List, 不可以改变的List List constList = const[10, 7, 23];
-
Dart就可以直接打印list包含list的元素,java中直接打印list结果是地址值。
var list = [0, 1, 2, 3, 4, 5, 6]; print(list); // [0, 1, 2, 3, 4, 5, 6]
-
Dart中List的下标索引和java一样都是从0开始。
-
Dart中List也支持泛型,这点和java一样,同时还可以进行泛型的判断。
var list1 = List<String>(); print(list1 is List<String>); // true 这里的 is 是一种类型判断
-
有增删改查的操作,支持倒叙,自带顺序,洗牌,可以使用+将两个List合并。
var list = [0, 1, 2, 3, 4, 5, 6]; print(list); var list2 = [7, 8, 9, 10, 11]; print(list + list2); //[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
-
数组中的常用操作
// 在数组中可以存放多个不同类型的对象 var list = [1, 2, 3, 'Flutter', true]; print(list); // 输出的结果:[1, 2, 3, Flutter, true] // 修改数组下标为2 的值 list[2] = 'Dart'; // 输出的结果:[1, 2, Dart, Flutter, true] // 从下标0开始,下标2结束(不包括2),修改之间的值为字符a list.fillRange(0, 2, 'a'); // 输出的结果:[a, a, Dart, Flutter, true] // 获取数组的长度 print(list.length); // 输出的结果:5 // 向数组中添加元素 list.add("value"); // [a, a, Dart, Flutter, true, value] var addList=['8','9']; list.addAll(addList);//[a, a, Dart, Flutter, true, value,8,9] // 向数组中的指定位置添加元素 list.insert(1, 'element'); // [a, element, a, Dart, Flutter, true, value] // 向数组中指定位置加入一组元素 list.insertAll(3, ['1','2','3','4']);// [a, element, a, 1, 2, 3, 4, Dart, Flutter, true, value] // 移除数组中的元素 list.remove("value");//[a, element, a, 1, 2, 3, 4, Dart, Flutter, true] // 移除数组中指定的元素 list.removeAt(1);// [a, a, 1, 2, 3, 4, Dart, Flutter, true] // 判断数组中是否有某个元素 print(list.indexOf("element")); // 输出结果:-1 // 可以使用 sort() 函数来排序, 但是由于我们使用定义的数组类型不一样,就不能使用 var intlist = [1, 2, 5, 6 ,3]; // 根据语法提示: List.sort([(int, int) → int compare]) → void intlist.sort( (a, b) => a.compareTo(b) );// [1, 2, 3, 5, 6] //数组翻转 var newList1=list.reversed.toList();// [true, Flutter, Dart, 4, 3, 2, 1, a, a] var bools=list.isEmpty;//判断集合是否为空 var bools2=list.isNotEmpty;//判断集合是否不为空 var str=list.join('***');//集合转字符串 可以用任意字符拼接 // a***a***1***2***3***4***Dart***Flutter***true var newList2=str.split('***');//字符串转集合 // [a, a, 1, 2, 3, 4, Dart, Flutter, true]
-
遍历
var list = [1, 2, 3, 'Flutter', true]; //两种遍历方式 list.forEach((element) { print(element); }); for (var item in list) { print(item); }
Set
Dart里面的Set和Java类似,也是不能存放重复的元素。
set 是有序的
-
两种初始化方式
var setName = <dataType>{} Set<dataType> setName ={}
-
和 JAVA 语言的区别:
difference:返回set1集合里面有但是set2里面没有的元素集合Set<String> set1 = {"1","2","3","4","5"}; Set<String> set2 = {"1","2","3"}; print(set1.difference(set2)); // {4, 5}
intersection:返回set1和set2的交集
Set set1 = {“1”,“2”,“3”,“4”,“5”};
Set set2 = {“1”,“2”,“3”};
print(set1.intersection(set2)); // {1, 2, 3}
**union**:返回set1和set2的并集
```dart
Set<String> set1 = {"1","2","3","4","5"};
Set<String> set2 = {"1","2","3","6"};
print(set1.union(set2)); // {1, 2, 3, 4, 5, 6}
retainAll:set1只保留某些元素(要保留的元素要在原set中存在)
Map
和Java类似
创建 Map 集合
void main(){
// 创建Map
var language = {'fisrt': 'dart', 'second': 'java'};
// 创建不可变的 Map
var constLanguage = const {'fisrt': 'dart', 'second': 'java'};
// 通过构造器创建
var initLanguage = new Map();
}
Map 中常用的方法
void main(){
// 创建Map
var map = {'fisrt': 'dart', 'second': 'java'};
// 获取长度
print(map.length); // 输出结果:2
// 判断是否为空
print(map.isEmpty); // 输出结果:false
print(map.isNotEmpty); // 输出结果:true
// 获取到所有的 key
print(map.keys); // 输出结果:(fisrt, second)
// 获取到所有的 values
print(map.values); // 输出结果:(dart, java)
// 判断是否包含某个key
print(map.containsKey("key")); // 输出结果:false
// 判断是否包含某个value
print(map.containsValue("key")); // 输出结果:false
// 添加一个新的元素进去
map['third'] = 'key';
print(map); // 输出结果:{fisrt: dart, second: java, third: key}
// 循环打印 代码提示:Map.forEach((String, String) → void f) → void
map.forEach( (key, value) => getMap(key, value) );
}
void getMap(key, value){
print("key:${key}, value:${value}");
}
void main() {
var list2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
//如果所有的数据都满足条件的就返回true,否则返回false
var f = list2.every((value) {
return value > 0;
});
print(f); // true
//如果所有的数据至少有一项满足条件就返回true,否则返回false
var f2 = list2.any((value) {
return value > 10;
});
print(f2); // false
//循环遍历,输出满足条件的数据
var newList5 = list2.where((value) {
return value > 5;
});
print(newList5.toList()); //[6, 7, 8, 9, 10]
//集合进阶
List list3 = [
{
'province': '河北',
'city': ['石家庄', '廊坊', '辛集'],
},
{
'province': '北京',
'city': ['朝阳区', '海淀区', '昌平区'],
},
{
'province': '山东',
'city': ['青岛', '淄博', '济南'],
}
];
for (int i = 0; i < list3.length; i++) {
print(list3[i]);
}
/* {province: 河北, city: [石家庄, 廊坊, 辛集]}
{province: 北京, city: [朝阳区, 海淀区, 昌平区]}
{province: 山东, city: [青岛, 淄博, 济南]} */
for (int i = 0; i < list3.length; i++) {
print(list3[i]['province'] + " ");
for (int j = 0; j < list3[i]['city'].length; j++) {
print(" " + list3[i]['city'][j]);
}
}
/*
河北
石家庄
廊坊
辛集
北京
朝阳区
海淀区
昌平区
山东
青岛
淄博
济南
*/
}
Map遍历
map.forEach((key, value) {
print("map----"+key+' : '+value);
});
//forin循环输出数据
for(var item in mapList2){
print("----"+item);
}
Runes
Runes在Dart当中是字符的UTF-32的编码
Main(){
Runes runes = new Runes('\u{1f605} \u6211‘);
var str1 = String.fromCharCodes(runes);
print(str1); // 😅 我
}
Symbol
Symbol标识符,主要是反射的作用,现在在mirrors模块已经被移除了。
类型转换
-
int / double
类似于 java 的类型转换
向上转型 : (小范围)—》(大范围),这种属于自动转换
向下转型 : (大范围)—》(小范围),这种属于强制转换int i = 10; // i = 10.1; // 这个地方会报错,因为将 int 型的数据改为 double 型 向下转型 // i = int.parse('10.1'); // 字符串转换为int类型 i = 10.1.round();// 通过round()方法将double转换为int print(i); // 10 double d = 20.1; d = 20; // 这个地方不会报错,因为将 double 型的数据改为 int 型,向上转型 print(d); // 20.0
-
int / string
int j = 10; String jstr = j.toString(); int z = int.parse('66'); // int z = int.parse('66.66'); // 报错,类型不匹配 print(jstr); // 10 print(z); // 66
-
double / string
double f = double.parse('9.9'); double ff = double.parse('9'); String fstr = 3.1415926.toStringAsFixed(6); // 保留6位小数 String ffstr = 3.1415926.toString(); print(f); // 9.9 print(ff); // 9.0 print(fstr); //3.141593 print(ffstr); // 3.1415926
assert
assert 是语言内置的断言函数,仅在检查模式下有效
在开发过程中, 除非条件为真,否则会引发异常。(断言失败则程序立刻终止)。
// 检查是否为空
var fullName = '';
assert(fullName.isEmpty);
// 为0检查
var hitPoints = 0;
assert(hitPoints <= 0);
// 检查是否为 null.
var unicorn;
assert(unicorn == null);
// 检查是否为 NaN.
var iMeantToDoThis = 0 / 0;
assert(iMeantToDoThis.isNaN);
函数
定义
总体来说,和java类似。
void main() {
firstMethod();
secondMethod();
thirdMethod();
}
void firstMethod(){
print('定义了一个方法');
}
String getPrint(){
return '带有返回值的方法';
}
int thirdMethod(int x,int y){
return x+y;//必填带参函数
}
其他几个特殊点:
-
可在函数内定义
-
定义函数时可以省略类型
-
支持缩写语法=>
int add(int a, int b) => a + b;
可选参数
用大括号进行声明可选参数,可选参数没有顺序,可以设置默认值,不传就是默认值
-
可以选择命名参数
-
可以选择位置参数
-
可以添加默认参数
printer(num n, {String s1, String s2}) { print(n); // 75 print(s1); // hello print(s2); // null } main() { printer(75, s1: 'hello'); }
printer(num n, {String s1, String s2}) { print(n); // 75 print(s1); // null print(s2); // null } main() { printer(75); }
printer(num n, {String s1, String s2}) { print(n); // 75 print(s1); // hello print(s2); // there
}
main() { printer(75, s1: ‘hello’, s2: ‘there’); }
```dart
printer(num n, {String s1 = '这是默认S1', String s2})) {
print(n); // 75
print(s1); // 这是默认S1
print(s2); // null
}
main() { printer(75); }
String mysteryMessage(String who, [String what, String where]){
var message = '$who';
if(what != null && where == null){
message = '$message said $what';
} else if (where != null){
message = '$message said $what at $where';
}
return message;
}
main() {
var result = mysteryMessage('Billy', 'howdy', 'the ranch');
print(result); // Billy said howdy at the ranch
}
匿名函数
可以赋值给变量,通过变量调用
可以在其他函数当中直接调用或者传递给其他函数
//赋值给变量
//无参匿名函数
var anonFunc1 = () => print('无参匿名函数');
anonFunc1(); // 输出结果: 无参匿名函数
//有参匿名函数
var anonFunc = (name) => 'I am $name';
print(anonFunc('damon')); // 输出结果: I am damon
//通过()调用,不推荐
// (()=>print('不推荐'))();
//匿名函数传参
List test(List list, String func(str)) {
for (var i = 0; i < list.length; i++) {
list[i] = func(list[i]);
}
return list;
}
var list = ['d', 'a', 'm', 'o', 'n'];
//String * int, Dart和Python可以这样用
print(test(list, (str) => str * 2)); //[dd, aa, mm, oo, nn]
//List.forEach()就用的匿名函数
List list1 = [11, 12, 13];
list1.forEach((item) => print('$item'));
//返回Function对象(闭包)
Function makeAddFunc(int x) {
x++;
return (int y) => x + y; // 返回的是 function 对象
}
var addFunc = makeAddFunc(2);
print(addFunc(3)); // 6
void main() {
// 函数别名
MyFunc myFunc;
//可以指向任何同签名的函数
myFunc = subtsract;
myFunc(4, 2); // subtsract: 2
myFunc = divide;
myFunc(4, 2); // divide: 2.0
//typedef 作为参数传递给函数
calculator(4, 2, subtsract); // subtsract: 2
}
// 函数的类型是Function类型,typedef就是给Function取个别名,相当于进行一次简单的封装
typedef MyFunc(int a, int b);
//根据MyFunc相同的函数签名定义两个函数
subtsract(int a, int b) {
print('subtsract: ${a - b}');
}
divide(int a, int b) {
print('divide: ${a / b}');
}
//typedef 也可以作为参数传递给函数
calculator(int a, int b, MyFunc func) {
func(a, b);
}
逻辑运算
操作符
操作符大部分和Java相同。标红的是不同的
?.
条件成员访问 和 . 类似,但是左边的操作对象不能为 null,例如 foo?.bar 如果 foo 为 null 则返回 null,否则返回 bar 成员。
String a;
print(a?.length); // null
String a="aaa";
print(a?.length); // 3
~/
取商操作符
被除数 ÷ 除数 = 商 … 余数,A ~/ B = C,这个C就是商。相当于Java里的 /
int a = 5;
print(a ~/ 2); // 2.5
print(a / 2); // 2
as、is、is!
类型判定操作
类型判定操作符:as、is、is!在运行时判定对象类型
void main() {
//as 类型转换
num iNum = 1;
num dNum = 1.0;
int i = iNum as int;
double d = dNum as double;
print([i, d]); // [1, 1.0]
// String s = iNum as String;
//is 如果对象是指定的类型返回 True
print(iNum is int); // false
Child child;
Child child1 = new Child();
print(child is Parent); // false //child is Null
print(child1 is Parent); // false
//is! 如果对象是指定的类型返回 False
print(iNum is! int); // false
}
class Child {}
class Parent {}
…
级联操作符
连续调用多个函数以及访问成员变量。
两个点的级联语法不是一个操作符。 只是一个 Dart 特殊语法。
StringBuffer sb = new StringBuffer();
sb
..write('dongnao')
..write('flutter')
..write('\n')
..writeln('damon');
print(sb); //输出结果:
dongnaoflutter
damon
流程控制语句
if else
for,forEach,for-in
while,do-while
break,continue
switch case
assert
大部分使用方法都是和Java相同的。不同的有
for-in
var colorList = ['black','red','yellow'];
for(var i in colorList){
print(i);
}
输出:
I/flutter (31601): black
I/flutter (31601): red
I/flutter (31601): yellow
assert
断言,它可以为代码执行设置条件,用于bool条件为false时中断正常的运行。
assert(condition,optional,message)
var variable;
print(variable);
assert(variable!=null);
variable = 6;
print(variable);
输出:
[ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: 'package:flutter_app/main.dart': Failed assertion: line 50 pos 10: 'variable!=null': is not true.
异常
不管是Java语言还是Dart语言,都有异常,以及异常的捕获,但是不同的是dart中的异常都是非检查异常,方法可以不声明可能抛出的异常,也不要求捕获任何异常。
Dart提供了Exception和Error类型以及一些子类型来定义异常。不过,还可以自定义异常,只要抛出非空对象作为异常即可,不要求必须是Exception和Error对象,但是一般来说都是抛出Exception和Error类型。
Exception类型
名称 | 说明 |
---|---|
DeferredLoadException | 延迟加载异常 |
FormartException | 格式化异常 |
IntegerDivisionByZeroException | 整数除零异常 |
IOException | IO异常 |
IsolateSpawnException | 隔离产生异常 |
NullRejectionException | 空拒绝异常 |
OSError | 操作系统错误 |
TimeoutException | 超时异常 |
Error类型
名称 | 说明 |
---|---|
AbstractClassInstantiationError | 抽象类实例化错误 |
ArgumentError | 参数错误 |
AssertionError | 断言错误 |
AsyncError | 异步错误 |
CastError | Cast错误 |
ConcurrentModificationError | 并发修改错误 |
CyclicInitializationError | 周期初始错误 |
FallThroughError | Fall Through错误 |
JsonUnsupportedObjectError | json不支持错误 |
NoSuchMethodError | 没有这个方法错误 |
NullThrownError | Null错误 |
OutOfMemoryError | 内存溢出错误 |
RemoteError | 远程错误 |
StackOverflowError | 堆栈溢出错误 |
StateError | 状态错误 |
TypeError | 类型错误 |
UnimplementedError | 未实现的错误 |
UnsupportedError | 不支持的错误 |
异常抛出
异常的抛出和Java还是很相像的。
//抛出Exception对象
throw new FormatException('格式异常');
//抛出Error对象
throw new NullThrownError();
//抛出任意非null对象
// throw '这是一个异常';
异常捕获
try { throw new NullThrownError();
// throw new OutOfMemoryError();
} on OutOfMemoryError {
//on 指定异常类型
print('没有内存了');
// rethrow; //把捕获的异常给 重新抛出
} on Error {
//捕获Error类型
print('Unknown error catched');
} on Exception catch (e) {
//捕获Exception类型
print('Unknown exception catched');
} catch (e, s) {
//catch() 可以带有一个或者两个参数, 第一个参数为抛出的异常对象, 第二个为StackTrace对象堆栈信息
print(e);
print(s);
}
}
类
类的概念和Java当中类似。
但是也有很大的区别。
类的构造
//java中写法
class P {
double x;
double y;
P(int x, int y) {
this.x = x;
this.y = y;
}
}
//dart建议写法
class P {
num x;
num y;
Point(this.x, this.y);
}
重定向构造函数
class P {
num x;
num y;
Point(this.x, this.y);
//重定向构造函数,使用冒号调用其他构造函数
P.alongXAxis(num x) : this(x, 0);
}
类的Get和Set方法
class Rectangle {
num left;
num top;
num width;
num height;
Rectangle(this.left, this.top, this.width, this.height);
num get right => left + width;
set right(num value) => left = value - width;
num get bottom => top + height;
set bottom(num value) => top = value - height;
}
命名构造函数
class Rect{
int _height;//定义成私有成员(前面加下划线),私有成员不能被外部直接访问
int _width;
Rect(this._height,this._width);//Dart默认构造函数 只能声明一次,Java可以声明多个
Rect.heigthSet(this._height);//命名构造函数 可以声明多个
Rect.widthSet(this._width);//命名构造函数 可以声明多个
Rect.allSet(this._width,this._height);//命名构造函数 可以声明多个
Rect.allSet2():_height=5,_width=5;//带默认参数的构造方法
//set get属性方法,外部通过访问属性的形式调用该方法
set setHeight(int value){
this._height=value;
}
//getter属性方法,不用括号,外部通过访问属性的形式调用该方法
get getHeight{
return _height;
}
//getter属性方法,不用括号,外部通过访问属性的形式调用该方法
get Area{
return _height*_width;
}
//定义私有方法
int _area(){
return _height*_width;
}
int getArea(){
return _area();
}
String getMessage(){
return('获取传入的高,宽值:$_height $_width 面积计算为:');
}
}
抽象 和 继承
// 定义父类
abstract class Person{
String name;
int age;
Person(this.name, this.age);
Person.second(this.name, this.age);
void show() {
print('$name $age');
}
//抽象方法定义规则,要求子类必须继承
void marry();
}
// 子类继承,必须实现抽象方法
class Boy extends Person{
double height;
Boy(String name, int age,double height):super(name,age){
this.height=height;
}
//方法重写
@override
void show(){
// super.show();
print('$name $age $height');
}
void playBoy(){
print('男生喜欢打王者荣耀');
}
@override
void marry() {
print('我是一个男生,要找一个女朋友结婚');
}
}
封装 和 多态
dart中的多态体现在方法重写,向上转型
比如上面Boy子类中的方法重写,我们也可以再定义一个Girl子类重写;
向上转型,向下转型这里做一个简单的介绍,具体代码可在我的资源中下载去看
向上转型可以提取公共方法,向下转型可以转化到子类,调用子类方法,向上转型也是一种封装
- A is B 判断A是否是B类型
- A as B 把A类型转换为B类型
if(p is Boy){
Boy boy=p as Boy;
boy.playBoy();
}
接口
dart中类就是接口,和声明抽象类一样,一般是用abstract修饰
abstract class Person {
void run();
}
class Student implements Person {
@override
void run() {
print("run 执行了。。。")
}
}
Mixins
Mixins是Dart里面的一个全新概念,简单来说,用来复用多个类之间的代码,减少耦合,换句话来说,可以从中扩展方法(或变量)而不扩展类。
- 通过mixins实现类似多继承,但是mixins不属于继承,也不属于接口,就是一种新的特性
- 作为mixins的类只能继承于最终父类Object,不可以继承别的类
- mixins类不能有构造函数
- 一个类可以继承于多个mixins
例如有以下关系
- 工程师 = 软件工程师 + 建筑工程师
- 教师 = 美术教师 + IT教师
- 工作者 = 教师 + 工程师
如果我们用Java来实现
//工作者
abstract class Worker {
public abstract void doWork();//工作者需要工作
}
//工程师
class Engineer extends Worker {
@Override
public void doWork() {
System.out.println("工程师在工作");
}
}
//教师
class Teacher extends Worker {
@Override
public void doWork() {
System.out.println("教师在教学");
}
}
//软件工程师
class SoftwareEngineer extends Engineer {}
//建筑工程师
class BuildingEngineer extends Engineer {}
//美术教师
class ArtTeacher extends Teacher {}
//IT教师
class ITTeacher extends Teacher {}
如果用Dart来实现
//工作者
abstract class Worker {
void doWork();//工作者需要工作
}
//工程师
class Engineer extends Worker {
void doWork() {
print('工程师在工作');
}
}
//教师
class Teacher extends Worker {
void doWork() {
print('教师在教学');
}
}
//软件工程师
class SoftwareEngineer extends Engineer {}
//建筑工程师
class BuildingEngineer extends Engineer {}
//美术教师
class ArtTeacher extends Teacher {}
//IT教师
class ITTeacher extends Teacher {}
从以上来看,似乎Java和Dart没有什么特别大的区别,因为Dart也是单继承。
下面,我们再把场景丰富一下
通过图形或表格可以看出,软件工程师和IT教师都具备修电脑的能力,建筑工程师和美术教师都具备手绘的能力,但是这些能力都是他们特有的,不是工程师或者教师具备的能力,所以不能在他们的父类中实现。
如果使用Java,那么我们自然就想到了使用interface。
interface CanFixComputer {
void fixComputer();
}
interface CanDesignSoftware {
void designSoftware();
}
//软件工程师
class SoftwareEngineer extends Engineer implements CanFixComputer, CanDesignSoftware {
@Override
public void fixComputer() {
System.out.println("修电脑");
}
@Override
public void designSoftware() {
System.out.println("设计软件");
}
}
//IT教师
class ITTeacher extends Teacher implements CanFixComputer {
@Override
public void fixComputer() {
System.out.println("修电脑");
}
}
但是,我们知道Dart当中没有interface的概念,但并不意味着这门语言没有接口,事实上,Dart任何一个类都是接口,你可以实现任何一个类,只需要重写那个类里面的所有具体方法。
如果要用Dart来实现的话,只需要将interface修改成abstract class。
但是我们发现,fixComputer这个接口被继承了两次,并且两次的实现都是一样的,这里就出现了代码重复和冗余的问题。怎么办呢?在java中我们有接口的default实现来解决这个问题(这是一个java8出现的不得已的方案。)
但是,有了mixins之后,这件事就变得不那么难了。
abstract class CanFixComputer {
void fixComputer() {
print('修电脑');
}
}
abstract class CanDesignSoftware {
void designSoftware() {
print('设计软件');
}
}
//软件工程师
class SoftwareEngineer extends Engineer
with CanFixComputer, CanDesignSoftware {}
//IT教师
class ITTeacher extends Teacher with CanFixComputer {}
main() {
ITTeacher itTeacher = new ITTeacher();
itTeacher.doWork();
itTeacher.fixComputer();
SoftwareEngineer softwareEngineer = new SoftwareEngineer();
softwareEngineer.doWork();
softwareEngineer.fixComputer();
softwareEngineer.designSoftware();
}
通过以上代码,我们可以看到这里不用implements,更不是extends,而是with。
每个具有某项特性的类不再需要具体去实现同样的功能,接口是没法实现功能的,而通过继承的方式虽然能实现功能,但已经有父类,同时不是一个父类,又不能多继承,所以这个时候,Dart的Mixin机制就比Java的接口会高效,开发上层的只需要关心当前需要什么特性,而开发功能模块的关心具体要实现什么功能。
关于顺序的理解
既然是with,那应该也会有顺序的区别。
class First {
void doPrint() {
print('First');
}
}
class Second {
void doPrint() {
print('Second');
}
}
class Father {
void doPrint() {
print('Father');
}
}
class Son1 extends Father with First,Second {
void doPrint() {
print('Son1');
}
}
class Son2 extends Father with First implements Second {
void doPrint() {
print('Son2');
}
}
main() {
Son1 son1 = new Son1();
son1.doPrint(); // Son1
Son2 son2 = new Son2();
son2.doPrint(); // Son2
}
通过这里,我们可以看到
可以看到,无论是extends、implements还是mixin,优先级最高的是在具体类中的方法。
下一个例子:
class First {
void doPrint() {
print('First');
}
}
class Second {
void doPrint() {
print('Second');
}
}
class Father {
void doPrint() {
print('Father');
}
}
class Son1 extends Father with First,Second {
}
class Son2 extends Father with First implements Second {
}
main() {
Son1 son1 = new Son1();
son1.doPrint(); // Second
Son2 son2 = new Son2();
son2.doPrint(); // First
}
其实在Son2中implements只是说要实现他的doPrint()方法,这个时候其实具体实现是First中Mixin的具体实现。
而Mixin的具体顺序也是可以从代码倒过来看的,最后mixin的优先级是最高的。
泛型
在Dart当中,有很多的容器对象,在创建对象时都可以定义泛型类型,这一点和Java是一样的。
var list = List<String>();
list.add('aaa');
list.add('bbb');
list.add('ccc');
print(list);
var map = Map<int, String>();
map[1] = 'aaaa';
map[2] = 'bbbb';
map[3] = 'cccc';
print(map);
和Java的区别
- Java中的泛型信息是编译时的,泛型信息在运行时是不存在的。
- Dart的泛型类型是固化的,在运行时也有可以判断的具体类型。
异步
Future
Future与JavaScript中的Promise非常相似,表示一个异步操作的最终完成(或失败)及其结果值的表示。简单来说,它就是用于处理异步操作的,异步处理成功了就执行成功的操作,异步处理失败了就捕获错误或者停止后续操作。一个Future只会对应一个结果,要么成功,要么失败。
因为Flutter返回的都是一个Fluter对象,自然就可以采用链式方法。
- Future.then 任务执行完后的子任务
- Future.delayed 延迟执行
- Future.catchError 如果异步任务发生错误,我们可以在catchError中捕获错误。
- Future.whenComplete 完成的时候调用。
- Future.wait 等待多个异步任务都执行结束后才进行一些操作。
Async/await
如果业务逻辑中有大量异步依赖的情况,将会出现上面这种在回调里面套回调的情况,过多的嵌套会导致的代码可读性下降以及出错率提高,并且非常难维护,这个问题被形象的称为回调地狱(Callback Hell)。
这个问题在JS当中十分的突出,Dart几乎是同样的问题,然后把相关方法平移过来。所以功能和用法也几乎是相同的。
async用来表示函数是异步的,定义的函数会返回一个Future对象,可以使用then方法添加回调函数。
await 后面是一个Future,表示等待该异步任务完成,异步完成后才会往下走;await必须出现在 async 函数内部。
只有async方法才能使用await关键字调用方法
如果调用别的async方法必须使用await关键字。
Stream
Stream 也是用于接收异步事件数据,和Future 不同的是,它可以接收多个异步操作的结果(成功或失败)。 也就是说,在执行异步任务时,可以通过多次触发成功或失败事件来传递结果数据或错误异常。 Stream 常用于会多次读取数据的异步任务场景,如网络内容下载、文件读写等。
Stream.fromFutures([
// 1秒后返回结果
Future.delayed(new Duration(seconds: 1), () {
return "hello 1";
}),
// 抛出一个异常
Future.delayed(new Duration(seconds: 2),(){
throw AssertionError("Error");
}),
// 3秒后返回结果
Future.delayed(new Duration(seconds: 3), () {
return "hello 3";
})
]).listen((data){
print(data);
}, onError: (e){
print(e.message);
},onDone: (){
});
// 输出
I/flutter (17666): hello 1
I/flutter (17666): Error
I/flutter (17666): hello 3
Isolates-隔离
所有Dart代码都在隔离区内运行,而不是线程。每个隔离区都有自己的内存堆,确保不会从任何其他隔离区访问隔离区的状态。