Dart介绍
将 Dart 和 JavaScript 做一个对比:
-
开发效率高
Dart 运行时和编译器支持 Flutter 的两个关键特性的组合:
- 基于 JIT 的快速开发周期:Flutter 在开发阶段采用,采用 JIT 模式,这样就避免了每次改动都要进行编译,极大的节省了开发时间;
- 基于 AOT 的发布包: Flutter 在发布时可以通过 AOT 生成高效的机器码以保证应用性能。而 JavaScript 则不具有这个能力。
-
高性能
Flutter 旨在提供流畅、高保真的的 UI 体验。为了实现这一点,Flutter 中需要能够在每个动画帧中运行大量的代码。这意味着需要一种既能提供高性能的语言,而不会出现会丢帧的周期性暂停,而 Dart 支持 AOT,在这一点上可以做的比 JavaScript 更好。
-
快速内存分配
Flutter 框架使用函数式流,这使得它在很大程度上依赖于底层的内存分配器。因此,拥有一个能够有效地处理琐碎任务的内存分配器将显得十分重要,在缺乏此功能的语言中,Flutter 将无法有效地工作。当然 Chrome V8 的 JavaScript 引擎在内存分配上也已经做的很好,事实上 Dart 开发团队的很多成员都是来自Chrome 团队的,所以在内存分配上 Dart 并不能作为超越 JavaScript 的优势,而对于Flutter来说,它需要这样的特性,而 Dart 也正好满足而已。
-
类型安全和空安全
由于 Dart 是类型安全的语言,且 2.12 版本后也支持了空安全特性,所以 Dart 支持静态类型检测,可以在编译前发现一些类型的错误,并排除潜在问题,这一点对于前端开发者来说可能会更具有吸引力。与之不同的,JavaScript 是一个弱类型语言,也因此前端社区出现了很多给 JavaScript 代码添加静态类型检测的扩展语言和工具,如:微软的 TypeScript 以及Facebook 的 Flow。相比之下,Dart 本身就支持静态类型,这是它的一个重要优势。
-
Dart 团队就在你身边
看似不起眼,实则举足轻重。由于有 Dart 团队的积极投入,Flutter 团队可以获得更多、更方便的支持,正如Flutter 官网所述“我们正与 Dart 社区进行密切合作,以改进 Dart 在 Flutter 中的使用。例如,当我们最初采用 Dart 时,该语言并没有提供生成原生二进制文件的工具链(这对于实现可预测的高性能具有很大的帮助),但是现在它实现了,因为 Dart 团队专门为 Flutter 构建了它。同样,Dart VM 之前已经针对吞吐量进行了优化,但团队现在正在优化 VM 的延迟时间,这对于 Flutter 的工作负载更为重要。”
软件安装
在这之前,我们知道”工欲善其事必先利其器“,所以需要安装相关工具。
Android studio安装
…
java安装
win7电脑看这篇博客。
https://blog.csdn.net/weixin_30979229/article/details/114453053?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164921053916782246441003%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=164921053916782246441003&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v1~rank_v31_ecpm-4-114453053.142v5pc_search_result_control_group,157v4control&utm_term=win7%E5%AE%89%E8%A3%85java&spm=1018.2226.3001.4187
Android studio中安装flutter与创建项目
首先,现在官网下载flutter
SDK包,在此包里面包含了Dart
的SDK
文件,下载路径如下:
在 Windows 操作系统上安装和配置 Flutter 开发环境 | Flutter 中文文档 - Flutter 中文开发者网站
点击蓝色按钮即可下载。
下载完成之后,将该文件包解压到你想安装目录的下面,我的安装目录是在D:\SoftwareFolder\
路径下,将其解压。解压完成之后就是运行,flutter提供了两种运行方式:
- 通过运行flutter目录下的
flutter_console.bat
文件,就可以在命令行中输入flutter命令了。
配置flutter
环境
- 配置环境变量,进入
flutter\bin
目录,将地址复制,然后打开环境变量窗口进行配置环境变量。
检测是否可用:
按快捷键win+R
输入cmd
命令,在命令行中输入flutter doctor
。
在Android studio中添加 Command-line Tools
打开 Android studio
,在stting
->appearance &Behavior
->System Settings
->Android SDK
中进行配置。
但是出现了错误,没关系,先解决第一个框里面的问题。
添加许可证
它提示Android toolchain - develop for Android devices
,
说明是Android的SDK协议没有添加许可证,
找到自己android—sdk的路径,比如:G:\studioAnZhuang\AndroidStudioSDK\tools\bin
,关键是**\tools\bin**。
在此目录打开 cmd 命令行 输入以下命令:
sdkmanager --licenses
然后一路同意y
就可以了,最后在命令行下输入flutter doctor 查看结果。
配置模拟器路径
之后关闭此命令行窗口,再次运行flutter doctor
发现还是这两个问题,于是看见它找出的问题是Unable to locate Android SKD
,这就要涉及到配置环境变量的问题,找到你的Android sdk
安装路径,比如我的安装路径如下图。
由于我不确定是不是这个路径,去环境变量里添加了这个之后,发现可行,把我激动坏了,环境变量配置如下所示。
关闭命令行窗口之后,再次运行flutter doctor
如下图所示。
不知道为啥,就是特别兴奋,感觉满满的成就感,哈哈哈,但是一想到还有一个问题没有解决,脑壳又大了。
于是分析了一下,发现是没有安装谷歌的原因,于是我通过下面链接去下载了google
浏览器。
它给我自动安装在了C盘,由于不知道怎么更改路径,就挺无语的,再次运行flutter doctor就没有报错了,如下图所示。
配置完这些东西之后,我们还需要去环境变量中添加2个路径如下图所示。
盘符名:安装文件夹\flutter_windows_2.5.3-stable\flutter\bin\cache\dart-sdk\bin
- dart的环境变量,在
flutter2.0
之后,官网的安装包里面就自带了dart的sdk文件,所以,我们只需要找到它的文件位置,将它的环境变量设置好就可以开始肝代码了。将下面的路径添加到系统环境变量中。
盘符名:安装文件夹\flutter_windows_2.5.3-stable\flutter.pub-cache\bin
- Pub下载的公共模块的执行路径,如果,没有
bin
文件夹,创建一个即可。
有了上面这些操作,我们就可以配置编译器,开始在编译器中肝代码了!!激动的心,颤抖的手,终于要开始了。
创建项目
Android studio中创建项目
在Android stduio中安装插件:
重启软件,
创建Dart项目
首先,打开Android studio
软件,点击File
> New
>New Flutter Project...
选择Dart
添加Dart sdk
文件位置,如下如图所示。
点击Next
,创建项目名和项目地址,之后点击Finish
,即可创建项目。
创建Dart文件
首先,找到Project
面板,在空白区域右击,选择new
>Dart File
>输入你的文件名,即可创建你的dart
文件。
写一段代码测试是否可以运行。
写完之后,点击mian
方法旁边的
按钮运行,并查看结果如下图。
如果发现无法运行,请点击下面图中的第一个框,进行选择模拟器,再点击运行。
VScode安装
首次,在VScode
官网进行下载,链接如下。
安装特别简单,若有不会,请访问下面链接。
安装之后,我们需要在软件中安装flutter
插件和run
插件,如下图所示。
flutter
插件安装好之后,重启一下vscode就可以开始创建文件,编写代码。run
插件可以直接点击右上角的
,前提是你需要在文档中写了Dart
程序代码。
VScode中创建项目
-
首先,你需要在你所需要创建项目的目录下使用
cmd
命令,进入命令行,使用flutter create flutter_app
。 -
flutter_app
是你需要创建的项目名。 -
在vscode打开项目,找到
android
目录下的build.gradle
文件并且打开。
在打开的文件中,将repositories{...}
下的google()
和mavenCentral()
注释,因为我们要换源,使用国内的镜像源。将下面3个源替换原来的两个源。
maven { url ‘https://maven.aliyun.com/repository/google’}
maven { url ‘https://maven.aliyun.com/repository/jcenter’ }
maven { url ‘https://maven.aliyun.com/repository/public’ }
之后打开你的flutter
安装目录,找到目录下的packages > flutter_tools > gradle >flutter.gradle>
的文件,双击在vscode中打开,将repositories{...}
下面的代码替换。保存,重新打开vscode。
添加第三方包(依赖)
说完了安装方法,现在介绍两种添加Flutter
第三方包的两种方法:
- 在
flutter
项目的pubspec.yaml
文件中添加:
在该文件中找到dependencies:
所在的行,并且需要知道它的版本号,所以,打开https://pub.dev/
,在这里面你可以搜索到所有开源的第三方包。
例如添加一个provider
的包:
将版本号复制下来,粘贴到该文件中的dependencies
处,点击Pub get
,在下面的Messges
窗口中可以看到是否添加成功,若成功之后,可以在External Libraries
中查看文件的位置。
- 在命令行中添加:
添加成功!
Dart基础
语法基础
简单介绍
首先,我们需要知道程序是都是从main
方法开始执行,于是有了固定的写法,如下所示。
// 定义一个输出方法
void printInteger(int num){
print("hello world $num"); // 输入语句,将文本输出到控制台
// $num 用于将形参的值放进来
}
// 在主函数中输出
void main(){
var number = 3000; // 定义一个活动变量,可以是任意类型值
printInteger(number); // 调用方法
}
从上面可以看出:
-
在main方法后面是一对小括号与一对大括号。
-
大括号里面放置的是需要执行的程序语句。
-
函数和方法前面都有类型声明,
void
关键字表示该方法无返回值
,int
是整形数字。 -
两根斜杠为注释内容,程序不会执行被注释的内容。
-
打印使用print(
JavaScript
中使用console.log()
)。 -
每行代码结束时,必须写结束分号。
-
字符串通过引号包起来,支持模板字符串。
-
用
var
声明的变量。其数据类型是动态的。 -
声明函数不需要关键字(
JavaScript
中使用function
关键字声明函数)。
注释
在Dart
中,注释有两种写法,和javascript
的注释方法一样,如下所示。
- 单行注释,以
//
两根斜杠为注释。
// 这行文本被注释
- 多行注释,以
/* 这里边是文本内容 */
,以两根斜杠为边,里面再放两个星号,在星号中的文本就是注释内容。
/*
这行文本被注释
这行文本被注释
*/
- 文档注释,使用三跟斜杠用于文档注释,可以通过
dartdoc
将注释转换为文档(文档注释支持markdown
语法)。
/// 这是文档注释
变量
-
变量是一个引用,Dart万物皆对象,变量存储的是对象的引用。
-
声明变量
-
明确指定类型:
int age = 18;
-
不明确类型:
-
var age = 18;
-
dynamic age = 18;
-
Object age = 18
。 -
Object
是Dart
所有对象的根基类,也就是说在 Dart 中所有类型都是Object
的子类(包括Function和Null),所以任何类型的数据都可以赋值给Object
声明的对象。dynamic
与Object
声明的变量都可以赋值任意对象,且后期可以改变赋值的类型,这和var
是不同的,如:var t; dynamic t; Object x; var t = "ha"; t = "hi world"; x = 'Hello Object'; //下面代码没有问题 t = 1000; // 出错 //下面代码没有问题 t = 1000; x = 1000;
-
dynamic
与Object
不同的是dynamic
声明的对象编译器会提供所有可能的组合,而Object
声明的对象只能使用Object
的属性与方法, 否则编译器会报错。dynamic a; Object b = ""; main() { a = ""; printLengths(); } printLengths() { // 正常 print(a.length); // 报错 The getter 'length' is not defined for the class 'Object' print(b.length); }
-
dynamic
的这个特点使得我们在使用它时需要格外注意,这很容易引入一个运行时错误,比如下面代码在编译时不会报错,而在运行时会报错:print(a.xx); // a是字符串,没有"xx"属性,编译时不会报错,运行时会报错
-
-
-
变量名大小写敏感(age与Age是两个不同的变量)。
-
Dart
变量的值不会进行隐式转换(null不会自动 转换成false)。
void main() {
//定义变量
var name1="张三";
print(name1); // 输出字符串 张三
// 指定类型的变量
String name2 = "李四";
print(name2); // 输出String类型的字符串 李四
// 任意类型的变量
dynamic name3 = "王五";
print(name3); // 输出字符串 王五
// 任意类型的变量
Object name4 = "赵六";
print(name4); // 输出字符串 赵六
}
常量
-
常量就是值不可以改变的变量(一旦声明,其值不能改变)。
-
声明常量
-
使用
const
关键字进行声明变量。const age = 18;
-
使用
final
关键字进行声明变量。final age = 18;
-
const
与final
的区别const time=DateTime.now();
//报错-无法将运行时得知分配给const
变量。final time = DateTime.now();
//成功-可以将运行时的值分配给final
变量。
-
void main(List<String> args) {
// const定义常量
const age = 18;
// age = 19; // 报错,常量不能再次赋值
print(age); // 输出整形 18
// final定义常量
final age1 = 18;
// age1 = 19; // 报错,常量不能再次赋值
print(age1);
// 报错-无法将运行时得知分配给`const`变量
// Cannot invoke a non-'const' constructor where a const expression is expected.
// const time1=DateTime.now();
// 成功-可以将运行时的值分配给`final`变量
final time2=DateTime.now();
print(time2);
}
数据类型
Number类型
- Dart中的数字由三个关键字描述
num
数字类型(既可以是整数,也可以是小数)int
表示整数(必须是整数)double
表示浮点数(既可以是整数,也可以是小数)
- 常用API
- https://api.dart.cn/stable/dart-core/num-class.html
- https://api.dart.cn/stable/dart-core/int-class.html
- https://api.dart.cn/stable/dart-core/double-class.html
void main(List<String> args) {
// 定义一个整型变量
int num1 = 123;
print(num1); // 123
// 定义一个浮点数变量
double num2 = 123;
print(num2); // 123.0
// 定义一个数值变量,它包含了int类型和double类型
num num3 = 123;
print(num3); // 123
num num4 = 123.0;
print(num4); // 123.0
}
常用API
String类型
字符串定义
首先,我们需要知道怎么定义字符串,字符串定义的引号都是成对出现,字符串的定义方法如下:
-
使用var的方式进行定义。
var str1 = 'this is str1'; // 单引号形式 var str2 = "this is str2"; // 双引号新式
// 输出结果如下所示 this is str1 this is str2
-
使用String定义字符串,3个引号包括换行符。
String str1 = 'this is str1'; // 单引号形式 String str2 = "this is str2"; // 双引号新式 String str3 = ''' this is str3 this is str3'''; // 实现多行显示,原样显示
// 输出结果如下所示 this is str1 this is str2 this is str3 this is str3
-
正则表达式。
- RegExp(r’正则表达式’)
- RegExp(r’\d+')
字符串拼接
首先,我们需要定义两个字符串变量,然后将他们拼接起来。
- 使用
+
加号将他们连接。
// 字符串拼接
String str1 = "张三想:";
String str2 = "吃饭";
print(str1+str2);
//或者
String str3 = str1 + str2;
print(str3);
// 效果如下
张三想:吃饭
张三想:吃饭
- 使用
$
方式进行拼接操作。
String str1 = "张三想:";
String str2 = "吃饭";
print("$str1 $str2");
张三想: 吃饭
字符串的常见操作
-
使用字符分割,结果是一个数组类型的值。
.split("字符")
– 根据字符进行分割。
// 字符串分割 String str1 = "hello world"; print(str1.split(""));; // 按照空值进行分割
[h, e, l, l, o, , w, o, r, l, d]
-
字符串裁切
.trim()
– 将字符串前后空格裁切掉。.trimLeft()
– 将字符串的左边空格裁切。.trimLeft()
– 将字符串的右边空格裁切。
// 字符串裁切 String str1 = " hello world "; print(str1.trim()); // 删除前后空格 print(str1.trimLeft()); // 删除左边空格 print(str1.trimRight()); // 删除右边空格
hello world hello world hello world
-
判断字符串是否为空
.isEmpty
– 判断字符串是否为空,返回值是 true或 false。.isNotEmpty
– 判断字符串是否不为空,返回值是 true或 false。
// 判断字符串是否为空
String str1 = ""; // 定义空字符串
print(str1.isEmpty);
print(str1.isNotEmpty);
true
false
-
字符串替换
.replaceAll(旧字符,新字符)
,而.replaceFirst()
只查找第一个并替换。
// 字符串替换 String str1 = "hello dart,hello world"; print(str1.replaceAll("hello", "你好"));
-
查找字符
.contains('字符串')
– 在字符串中查找字符,返回值为true 或 false。
// 字符查找
String str1 = "hello dart,hello world";
print(str1.contains("l"));
true
- 定位字符
.indexOf(字符)
– 用于在找到字符的索引位置,返回索引下标,从左开始查找。.lastIndexOf(字符)
– 找到字符的索引位置,返回索引下标,从右往左查找。
// 定位字符
String str1 = "hello dart,hello world";
print(str1.indexOf('ll'));
print(str1.lastIndexOf("or"));
2
18
bool类型
-
布尔类型只有
true
和false
。 -
Dart通过bool关键字来表示布尔类型。
-
对变量进行判断时,要显式的检查布尔值
- if (varname){ … }
- if (varname == 0){ … }
- if (varname == null){ … }
// 定义一个true值
bool b1 = true;
print(b1);
// 定义一个false值
bool b2 = false;
print(b2);
true
false
显式的判断
-
与if语句,将条件
true
赋值给一个变量保存,将其放入if
的判断中进行判断,使用如下:bool b3 = true; if (b3) { print("条件成立!"); }else{ print("条件不成立"); }
条件成立!
-
判断字符是否为 NaN。
var b1 =0/0; print(b1.isNaN);
true
-
判断是否为空值。
// 判断是否为空值 var flag; if (flag == Null) { print("是空值"); } else { print("不是空值"); }
List数据类型
List用法
List 就是Dart中的数组,由List
对象表示。List有两种声明方式:
-
字面量方式
-
创建列表。
-
List list = [1,2,"张三","不法分子"]; // 不限定元素的数据类型
-
泛型方法限定列表类型。
-
List list = <int>[1,2,3]; // 泛型,限定元素的类型是int
-
-
构造函数方式
-
创建一个不限定长度的列表。
List list = new List.empty(growable:true); // 不限制长度的空列表
[]
-
填充指定长度的列表。
List list = new List.filled(3,6); // 声明指定长度的填充列表
[6, 6, 6]
-
-
扩展操作符(…)
-
将第一个列表插入到另一个列表中。
var list = [1,2,3]; var list2 = [0,...list];
[0, 1, 2, 3]
-
使用
?
先进行判断是否可以扩展,在下面列子中用空值进行试验。// 判断是否可以扩展 var list; // var list1 = [123,...list]; //报错:type 'Null' is not a subtype of type 'Iterable<dynamic>' var list2 = [234,...?list]; print(list2);
-
List常用API
-
添加元素
-
.add(数据)
– 向列表中添加元素,可以是字符串、数组、各种数据类型。// 添加元素 List list = [12,23,34]; list.add(1.3); list.add("字符串"); list.add([true,false]); print(list);
[12, 23, 34, 1.3, 字符串, [true, false]]
-
.addAll(iterable)
– iterable是一个可迭代的对象,可以在里面放置多个元素。// 添加元素 List list = [12,23,34]; list.addAll(["name",123,["张三",18]]); print(list);
[12, 23, 34, name, 123, [张三, 18]]
-
-
在指定位置添加元素。
-
.insert(index,element)
– index为元素下标,element为元素// 在指定位置添加元素 List list = [12,23,34,45,56]; list.insert(3, "张三"); // 在下标为3的地方添加“张三” print(list);
[12, 23, 34, 张三, 45, 56]
-
-
清空列表
-
.clear()
– 清空列表。// 清空列表 List list = [12,23,34,45,56]; list.clear(); print(list);
[]
-
-
连接元素
.join('-')
- -将列表中的元素使用 - 连接。
-
删除元素
-
.remove(元素名)
– 参数为需要删除的元素名。List list = [12,23,34,45,56]; list.remove(23); print(list);
[12, 34, 45, 56]
-
.removeAt(下标)
– 根据下标删除元素。// 根据下标删除元素 List list = [12,23,34,45,56]; list.removeAt(list.length-1); print(list);
[12, 23, 34, 45]
-
-
返回数组长度
-
.length
– 可以查看数组有少个元素。// 查看数组长度 List list = [12,23,34,45,56]; print(list.length);
5
-
-
创建一个固定长度的列表
-
.filled(元素个数,填充的字符)
--所接收的元素类型一定是List
类型,创建的列表长度是固定的,不可以修改他的长度。List list = List.filled(3,6); // 声明指定长度的填充列表 print(list);
[6, 6, 6]
-
指定创建指定类型与长度的列表。
// 创建指定类型的快速填充 List list = List<String>.filled(3,""); // 所创建的列表中只能添加int类型的元素。 list[0]="张三"; print(list);
[张三, , ]
-
-
列表反转
-
.reversed
– 将列表中的元素翻转,得到的结果是一个元组,可以使用在后面加上.toList()
方法.// 列表的反转 List list = [12,23,34,45,56]; print(list.reversed); // 变为列表 print(list.reversed.toList());
(56, 45, 34, 23, 12) [56, 45, 34, 23, 12]
-
遍历列表
-
forEach()
- 遍历列表
-
map()
- 遍历并处理元素,然后生成新的列表
-
where()
- 返回满足条件的数据
-
any()
- 只有一项满足条件,即返回true
-
every()
- 判断是否每一项都满足条件,都满足条件才返回true
-
使用
for
循环进行遍历。// for 循环进行遍历 var list = [1,2,3]; for (var i = 0; i < list.length; i++) { print(list[i]); }
1 2 3
-
for … in 循环。
// for in 循环进行遍历 var list = [1,2,3]; for (var item in list) { print(item); }
1 2 3
-
forEach 使用匿名函数进行遍历,其中element是一个形参,使用print可将他打印出来。
// forEach var list = [1,2,3]; list.forEach((element) { print(element); });
1 2 3
-
使用map()将列表中的每个元素进行相乘,得到一个处理之后的列表,注意:
=>
函数只能右一行,可以省略大括号。// map var list = [1,2,3]; list.map((e)=> e*e); // 也可以写成这样list.map((e)=> {e*e}); print(list); }
[1, 2, 3]
-
where()返回符合条件的元素
//where()返回符合条件的元素 // 判断数字是否为奇数 var list = [1,2,3,4,5,6,7,8]; bool isOdd(n) => n%2==1; // 箭头函数,判断数字是否为奇数 var oddNum = list.where((element) => isOdd(element)); // 返回结果是一个元组类型 print(oddNum.toList());
[1, 3, 5, 7]
- 第五行代码中,调用
isOdd()
函数进行判断,将判断的结果返回给list,并重复判断,直到不满足条件为止。而element
则是列表中的每一个元素。
- 第五行代码中,调用
-
使用any()检测是否有奇数,返回值为true或false。有一个也是true。
// 使用any() 检测是否奇数 var list = [1,2,3,4,5,6,7,8]; bool isOdd(n) => n%2==1; // 箭头函数,判断数字是否为奇数 print(list.any(isOdd));
true
-
使用every()来判断是否都是奇数。
// 使用every()来判断是否都是奇数 var list = [1,2,3,4,5,6,7,8]; bool isOdd(n) => n%2==1; // 箭头函数,判断数字是否为奇数 print(list.every(isOdd));
false
-
使用扩展方式,通过
.expend()
方法进行降维处理。// 扩展,使用expend进行降维 var list = [[1,2,3],[4,5,6]]; var flattened = list.expand((element) => element).toList(); print(flattened);
-
折叠--
.fold(initialValue, (previousValue, element) => null)
,initialValue
为初始值,对列表中的每一个元素,做一个累计的操作。
// 折叠
var list = [1,2,3,4,5,6];
int result = list.fold(2, (previousValue, element) => previousValue+element);
print(result);
Set类型
-
Set是一个无序的,元素唯一的集合,不能有重复值。
-
Set有字面量和构造函数两种声明方式(字面量中用大括号)。
-
无法通过下标取值。
-
具有集合特有的操作。比如:求交集、并集、差集等。
-
字面量
// 字面量 var nums = <int>{1,2,3}; print(nums);
{1, 2, 3}
-
构造函数的形式创建集合
// 构造函数创建集合 var fruits = new Set(); // 使用构造函数的形式创建集合 fruits.add("张三"); // 向集合中添加添加元素 fruits.add("李四"); fruits.add("王五"); print(fruits); // 打印集合 print(fruits.toList()); // 集合转换为表格形式
{张三, 李四, 王五} [张三, 李四, 王五]
-
列表转换为集合
// 表格转换为集合,重复的元素会被过滤掉 List list = [1,2,2,3,4,4]; // 定义列表 print(list.toSet()); // 列表转换为集合
{1, 2, 3, 4}
-
集合特有的操作,交集、差集、并集。
// 集合特有操作 var name1 = new Set(); name1.addAll(['张山','李四',"王五"]); var name2 = new Set(); name2.addAll(["王五","赵六","马七"]); // 求交集,共同有的元素会被显示出来了。 print(name1.intersection(name2)); // 求并集,将他们合并,共同的元素只留一个。 print(name1.union(name2)); // 求差集,第一个中有的,而第二个中没有的元素。 print(name1.difference(name2)); // 返回第一个元素 print(name1.first); // 返回最后一个元素 print(name1.last);
Map类型
-
Map是一个无序的键值对(key-value)映射。通常被作为哈希或字典。]
-
Map的键值对是成对出现的。
-
声明方式:
-
var map = {key1:value1,key2:value2};
-
var map = new Map();
-
map['key'] = value;
-
-
字面量形式
// 字面量形式 var person = { 'name':'张三', 'age':18, 'information':'法外狂徒', }; print(person);
{name: 张三, age: 18, information: 法外狂徒}
-
构造函数
// 构造函数 var p = new Map(); p['name'] = "张三"; // 向map中添加元素 p['age'] = 18; p['info'] = '法外狂徒'; print(p); // 访问属性 print(p['name']); // 访问name的值
{name: 张三, age: 18, information: 法外狂徒} {name: 张三, age: 18, info: 法外狂徒} 张三
-
判断Map中的Key和Value是否存在,返回值为true或false。
var p = new Map(); p['name'] = "张三"; p['age'] = 18; p['info'] = '法外狂徒'; print(p); // 判断Map中的Key是否存在 print(p.containsKey('name')); // 判断Map中的Value是否存在 print(p.containsValue('张山'));
true false
-
.putIfAbsent()
赋值,如果 Key 不存在,我们才赋值(如果key已存在,则不赋值)。var p = new Map(); p['name'] = "张三"; p['age'] = 18; p['info'] = '法外狂徒'; print(p); //如果 Key 不存在,向Key赋值 p.putIfAbsent('score', () => 100); print(p);
{name: 张三, age: 18, info: 法外狂徒, score: 100}
-
.keys
和.values
获取map中所有的key 和 value。// 获取所有的key 和 value var person = {'name':'张三','age':18,'information':'法外狂徒'}; print(person.keys); print(person.values);
(name, age, information) (张三, 18, 法外狂徒)
-
删除元素
-
.remove(key)
– 根据key 进行删除。// 删除元素 var person = {'name':'张三','age':18,'information':'法外狂徒'}; person.remove('name'); print(person);
{age: 18, information: 法外狂徒}
-
.removeWhere()
– 根据条件进行删除。// 根据条件进行删除 var person = {'name':'张三','age':18,'information':'法外狂徒'}; person.removeWhere((key, value) => key=='name'); print(person);
{age: 18, information: 法外狂徒}
-
其他数据类型
-
Runes(符文)
- Runes对象是一个32位字符对象。它可以把文字转换成符号表情或特定的文字。
- print(‘\u{1f44d}’) => 👍
- https://copychar.cc/
-
Symbol
- 在Dart 中符号用
#
开头来表示的标识符。
- 在Dart 中符号用
-
dynamic
- 动态数据类型
运算符(operator)
运算符是一种告诉编译器执行特定的数学或逻辑操作的符号。Dart语言内置了丰富的运算符,并提供了以下类型的运算符:算术运算符、关系运算符、类型判断运算符、赋值运算符、逻辑运算符、按位和移位运算符、条件表达式、级联运算符以及其他运算符。
-
地板除(~/)-- 相除之后,向下取整。
-
类型判断运算符(is |is!)-- 判断某种变量是否属于某种类型。
-
避空运算符(?? | ??=) – 如果变量为空,才会对变量赋值,变量不为空,不对其做任何操作。
-
条件属性访问(?.) – 先判断属性是否存在,存在的话再去访问。
-
级联运算符(…)
-
myObject.myMethod(); // 返回myMethod的返回值
-
myObject..myMethod(); // 返回myMethod()对象的引用
-
-
~/
地板除,相除之后,向下取整// 地板除 print(5/3); // 除法 print(5~/3); // 地板除
1.6666666666666667 1
is
和is!
类型判断运算符
// 类型判断运算符 List list = []; // 定义一个列表 // is运算符 if (list is List) { // 判断是否是列表 print("是List"); } else { print("不是List"); } // is! 运算符 if (list is! List) { print("不是列表"); } else { print("是列表"); }
是List 是列表
-
??
和??=
避空运算符// 避空运算符 print(1 ?? 3); //返回1,因为1不为空 print(null ?? 11); // 返回11,因为第一个参数为null var name; //name没有赋值,为空。 print(name ?? "name为空"); // 赋值避空运算符 name ??= "张三"; //进行判断,如果为空,将张三赋值给name print(name);
1 11 name为空 张三
-
条件属性运算符
// 条件属性运算符(判断可能为空的属性) var m = new Map(); print(m.length); // 返回0 var obj; // print(obj.length); //The getter 'length' was called on null. print(obj?.length); // 返回null
0 null
-
级联运算符
// 级联运算符 // 普通添加删除数据 Set s =new Set(); s.add("张三"); s.add("李四"); s.add([1,2,3]); s.removeWhere((element) => element=="张三"); print(s); // 级联添加删除数据 Set set = new Set(); set..add("张山") ..add("赵陆") ..add([1,2,3]) ..removeWhere((element) => element=="张三"); print(set);
算数运算符(Arithmetic Operators)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cpQLAH98-1638454824693)(F:\flutter笔记\Dart_imges\image-20211120170520611.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j755TOBt-1638454824693)(F:\flutter笔记\Dart_imges\image-20211120170555298.png)]
// 算数运算符
print(2 + 3 == 5);
print(2 - 3 == -1);
print(2 * 3 == 6);
print(5 / 2 == 2.5); // 结果是一个浮点数
print(5 ~/ 2 == 2); // 结果是一个整数
print(5 % 2 == 1); // 取余
print('5/2 = ${5 ~/ 2} r ${5 % 2}' == '5/2 = 2 r 1');
// 自增自减运算符
var a, b;
a = 0;
b = ++a; // 在 b 赋值前将 a 增加 1。
print(a == b); // 1 == 1
a = 0;
b = a++; // 在 b 赋值后将 a 增加 1。
print(a != b); // 1 != 0
a = 0;
b = --a; // 在 b 赋值前将 a 减少 1。
print(a == b); // -1 == -1
a = 0;
b = a--; // 在 b 赋值后将 a 减少 1。
print(a != b); // -1 != 0
关系运算符(Relational operators)
用法基本上和其他语言相同。
类型判断运算符
赋值运算符
逻辑运算符
按位和移位运算符
其他运算符
三个点 和 两个点
使用...
可以进行拼接操作,两个..
表示级联操作。
三个点
List a = ["张三","李四","王五"];
List b = ["赵六","马奇","黄八"];
List c = [...a,...b];
print(c);
// 结果为:[张三, 李四, 王五, 赵六, 马奇, 黄八]
Map map1 = {'张三':'1','李四':'2','王五':'3',};
Map map2 = {'赵六':'4','马奇':'5','黄八':'6',};
Map map3 = {...map2,...map1};
print(map3);
// 结果为:{赵六: 4, 马奇: 5, 黄八: 6, 张三: 1, 李四: 2, 王五: 3}
两个点
class Test {
//正常写法
A printfA() {
var a = A();
a.a();
a.b();
a.c();
return a;
}
//级联写法
A printfA() {
return A()..a()..b()..c();
}
}
class A {
a() {}
b() {}
c() {}
}
函数
声明函数
- 直接声明
- Dart中声明函数不需要function关键字
- 箭头函数
- Dart中的箭头中,函数体只能写一行且不能带有结束的分号。
- Dart中的箭头函数,只是函数的一种简写形式。
- 匿名函数
- 立即执行函数
声明函数和调用函数
// 定义函数
void printInfo(){ // 无参函数,无返回值
print("hello world!");
}
int getNum(){ // 有返回值,无参函数
// return "张三"; // 不能返回String类型值
return 18; // int类型声明的函数只能返回int类型的值
}
void main(List<String> args) {
printInfo(); // 调用函数
int a = getNum(); // 接收函数返回值
print(a);
}
hello world!
18
- void 表示无返回值。
- return表示返回,但需要根据函数类型进行返回。也就是说函数是什么类型,返回值就是什么类型,而用于接收它的变量类型等级必须高于int类型。
- 调用函数,如果函数是有参函数,需要为函数传参,才能调用。
匿名函数
void main(List<String> args) {
// 匿名函数
var myPrint = (value){ // 定义一个匿名函数
print(value);
};
List fruits = ["苹果","香蕉","西瓜"];// 定义一个列表、
// fruits.forEach((element) {element});
fruits.forEach(myPrint);
}
苹果
香蕉
西瓜
- 第三行定义了一个匿名函数,并且用myPrint变量接收。
- 第七行定义了一个列表。
- 在第9行中使用
myPrint
替换了(element) {element}
,原因是在.forEach()
方法中传入的也是一个匿名函数。
箭头函数
只能一行显示的简单函数,并且没有分号。
// 箭头函数
List fruits = ["苹果","香蕉","西瓜"];
fruits.forEach((element) =>{
print(element) // 箭头函数中不能有分号,并且只能有一行
});
fruits.forEach((element) =>print(element)); // 只能有一行
苹果
香蕉
西瓜
苹果
香蕉
西瓜
立即执行函数
立即执行函数就是立即执行的函数。
// 立即执行函数
(
(int n){
print(n);
}
)(1223556);
1223556
- 使用一对小括号扩起来的匿名函数。
- 在小括号后面可以传递参数。
函数参数
- 必填参数
- 参数类型 参数名称
- 可选参数
- 放在必选参数后面
- 通过中括号包起来
- 带默认值的可选参数
- 命名参数
- 用大括号包起来
- 调用函数时,命名参数的名称与声明函数中的名称保持一致。
- 函数参数
- 把一个函数的实例当做参数传递给另一个参数
必填参数
// 定义一个String类型的必选参数
String userInfo(String name){
return '你好:$name';
}
void main(List<String> args) {
// 必填参数
// String res = userInfo(123); // 传入的值必须和定义值类型一样
String res = userInfo("张三");
print(res);
}
你好:张三
- 传入的值必须和定义值类型一样。
- 定义多个函数时,函数名不能相同,没有Java中的重载机制。
可选参数
// 可选参数
// // 必须给age定一个默认值,如果为null会和int类型冲突。或者使用动态类型 dynamic 和 Object类型
// String userInfo(String name,[int age=0]) // int类型
// String userInfo(String name,[dynamic age]) // Object类型
String userInfo(String name,[Object age=0]){
return '你好:$name,年龄:$age';
}
void main(List<String> args) {
// 必填参数
// String res = userInfo(123); // 传入的值必须和定义值类型一样
String res =userInfo("张三",18);
print(res);
}
-
形参类型必须与实参类型一致。
-
可选参数用中括号包裹起来。
命名参数
// 命名参数
String userInfo(String name,{int age=0}){ // 使用花括符进行定义,int类型必须赋值
return '你好:$name,年龄:$age';
}
void main(List<String> args) {
// 命名参数调用时,必须与声明的形参一致
String res =userInfo("张三",age:18); // 使用键值对的形式进行传递参数
print(res);
你好:张三,年龄:18
- 命名参数使用花括号包裹起来。
- 调用时使用
属性名:属性值
的形式进行传参。
函数参数
// 函数参数
var myPrint = (value){ // 定义一个匿名函数
print(value);
};
List fruits = ["苹果","香蕉","西瓜"];// 定义一个列表、
// 将匿名函数传递给forEach函数
fruits.forEach(myPrint);
苹果
香蕉
西瓜
- 将匿名函数用值得形式作为forEach函数的值进行传递。
作用域与闭包
函数作用域
- 内层可以访问外层所定义的内容。
- 外层不可以访问内层所定义的内容。
闭包
- Dart中闭包的实现方式与JavaScript中完全一致。
- 使用时机:既能重用变量,又保护变量不被污染。
- 实现原理:外层函数被调用后,外层函数的作用域对象(AO)被内层函数引用着,导致外层函数的作用域对象无法释放,从而形成闭包。
parent(){
var money = 1000;
return (){
money-=100;
print(money);
};
}
var p = parent(); // 将parent函数保存为p函数
p(); // 每次调用完之后只释放return里面的匿名函数的地址,并没有parent函数的地址。
p(); // 所以可以一直调用一直减
p();
900
800
700
异步函数
- JavaScript中,异步调用通过Promise来实现
- async(异步)函数返回一个Promise(承诺)。await(等待)用于等待Promise。
- Dart中,异步调用通过Future(未来)来实现。
- async函数返回一个Future,await用于等待Fature。
…
类与对象
类-简介
- 类是通过class声明的代码段,包含属性和方法。
- 属性:用来描述类的变量。
- 方法:类中的函数。
- 对象是类的实例化结果(var obj=new MyClass())
- 在>=Dart 2.12的版本中添加了late关键字,在类中定义的空属性需要使用late关键字
- 类名首字母大写,其余字母单词首字母也必须大写。
- 类相当于一个模板,对象就是根据模板做出来的东西。
- 类只是抽象的说说,并不会去实现,而对象需要具体干出这些活。
- 就像你女朋友就是一个类,只知道指挥,而你就是她的对象,通过她发出的指令去做,啥都要干。
- 如果类特别多的话,可以将类单独放入一个文件中,用调用方式进行使用。
- 编程方式
- 面向对象编程(OOP)
- 面向过程编程(POP)
- JavaScript就是 面向过程编程,它的class只是语法糖,语法糖让程序更加简洁,有更高的可读性。
- JavaScript中没有类。
- Dart中所有的内容都是对象。
声明类
-
使用
class
关键字进行定义类,类名后面是一对花括符,如:class 类名{};
-
实例化类使用
new
关键字。 -
在
new
对象的时候类名后面需要写一对小括号,比如:类型 对象名 = new 类名();
// 声明类
class Person{
// 类的属性
String name = "张三";
// 类的方法
void getInfo(){
print("我是$name");
}
}
void main(List<String> args) {
// 实例化类,得到一个对象
Person p = new Person();
// 访问类中的属性
print(p.name);
// 访问类中的方法
p.getInfo();
}
张三
我是张三
dart中所有的内容都是对象。
为什么这么说呢?看下面例子。
// new一个Map对象,Map是Dart中内置的一个类,不需要自己创建
Map m = new Map();
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cFrApKDO-1638454824699)(F:\flutter笔记\Dart_imges\image-20211121170336802.png)]
- 第一个大框中,在vscode中,类的属性是通过[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-utIzZydm-1638454824700)(F:\flutter笔记\Dart_imges\image-20211121170527252.png)]来定义的。
- 第二个大框中,在vscode里,类的方法是通过[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zPUCmBD3-1638454824701)(F:\flutter笔记\Dart_imges\image-20211121170922049.png)]来定义的。
- 基本上每个东西自己的属性和方法。
将类放入单独的文件中
在当前目录中创建一个lib
目录,在该目录中创建一个叫lei.dart
文件,在文件中写入一个类,如下所示:
// lei.dart
// 创建一个人类
class Person{
int a =10;
late int b; // 创建一个空值b
// 有参构造函数
Person(this.a,this.b){
print('${a} $b');
}
// 命名构造函数
Person.info(){
print("这是命名构造函数");
}
}
// 创建一个动物类
class Animal{
late String name;
late String info;
Animal(this.name,this.info){
print("传入的动物是:$name ,种类是:$info");
}
}
在上面类的上一级目录创建一个名为Factory_Constuctor.dart
的文件,并写入下面代码,主要是实现类的调用。
import 'lib/lei.dart';
void main(List<String> args) {
// Person p = new Person(123,456);
Person p = new Person.info();
int a1 = p.a;
print("a为:$a1");
Animal animal = new Animal("小狗", "泰迪");
print(animal.name);
}
这是命名构造函数
a为:10
传入的动物是:小狗 ,种类是:泰迪
小狗
- 使用`import ''方式导入模块。
构造器(构造函数)
默认构造函数
- 与类同名的函数称为构造函数,在实例化时,自动被调用。
- 构造函数中设置形参称为有参构造函数,反之为无参构造函数。
- this指向的是类中的属性,目的是为了避免混淆。
// 构造函数
class Point{
// 定义类属性
num x=0,y=0;
// 声明普通构造函数,在创建对象时,就会自动执行构造函数
Point(num x, num y){ // 构造函数与类同名,并设置为有参构造函数
print("这是构造函数");
// 打印
print("参数值$x $y");
// 为了避免有歧义,使用this指向类中属性
this.x=10; //指向第4行的x,并改变x的值为10
this.y=20; //指向第4行的y,并改变y的值为20
}
}
void main(List<String> args) {
Point p = new Point(40,30); // 创建对象,并给构造函数传递参数
print("属性值:");
print(p.x); // 打印属性x的值
print(p.y);
}
这是构造函数
参数值40 30
属性值:
10
20
- 构造函数简写
class Point{
// 定义类属性
num x=0,y=0;
// 简写构造函数
Point(this.x, this.y); // 简写构造函数,需要加分号结尾。
}
void main(List<String> args) {
Point p = new Point(40,30); // 创建对象,并给构造函数传递参数
print("属性值:");
print(p.x); // 打印属性x的值
print(p.y);
}
属性值:
40
30
命名构造函数
- 默认构造函数
- 与类名同名的函数,在实例化时,自动被调用。
- 命名构造函数
- 在类中使用命名构造函器(类名.函数名)实现多个构造器,可以提供额外的清晰度。
class Point{
// 定义类属性
num x=0, y=0;
// 简写构造函数
Point(this.x,this.y);
// 命名构造函数
Point.origin(){ // origin-原始的
x = 0;
y = 0;
}
Point.fromJson({x:0,y:0}){
this.x = x;
this.y = y;
}
}
void main(List<String> args) {
// 默认坐标
Point p1 = new Point.origin();
print(p1.x);
// 手动设置坐标
Point p2 = new Point.fromJson(x: 20,y: 30);
print(p2.x);
}
0
20
常量构造函数
- 如果类生产的对象不会改变,可以通过常量构造函数使这些对象成为编译时常量。
- 常量编译快,因为常量不改变。
- 创建对象时可以不使用
new
关键字。
class Point{
num x=0;
num y=0;
// 构造函数简写
Point(this.x,this.y);
}
class ImmutablePoint{
// 属性必须通过 final 声明
final num x;
final num y;
// 常量构造函数,必须通过 const 声明,并且常量声明的函数不能有函数体
const ImmutablePoint(this.x,this.y);
}
void main(List<String> args) {
var p1 = new Point(1, 3);
var p2 = new Point(1, 3);
print(p1 == p2);
// 常量构造函数,可以当做普通构造函数使用
var p3 = new ImmutablePoint(3,4);
var p4 = new ImmutablePoint(3,4);
print(p3 ==p4);
// 声明不可变对象,必须通过 const 关键字,而new出来的对象是指向不同的地址,所以不相等
var p5 = const ImmutablePoint(3,4);
var p6 = const ImmutablePoint(3,4);
print(p5 ==p6); // true
// 创建对象可以不用 new 关键字进行实现
var p7 = ImmutablePoint(3,4);
var p8 = ImmutablePoint(3,4);
print(p3 ==p4); // false
}
私有与公有
分为私有属性和私有方法,就是在属性名或者方法名前面加一个短下划线_
,例如:_name
和_Animal()
方法就是被定义为私有的属性和方法。
- 私有属性只能在当前类中访问。
在lib
目录下创建一个名为private.dart
的文件,里面写一下代码:
// 创建一个动物类
class Animal{
late String _name; // 定义私有属性
late String info;
// ignore: unused_element
_Animal(_name,info){
print("传入的动物是:$this._name ,种类是:$info");
}
int _aaa(){ // 定义私有方法
return 100;
}
}
// 创建一个人类
// 创建一个人类
class Person{
late String name;
late String info;
Person(this.name,this.info){
print(this.name);
print(this.info);
}
}
在privaDemo.dart
文件中调用。
import 'lib/private.dart';
void main(List<String> args) {
Animal animal = new Animal("晓白", "牧羊犬");
// var aa = animal._aaa(); // 不能调用私有方法
// String name = animal._name; // 不能访问私有属性
print(animal.info);
Person p = new Person("张三","法外狂徒");
print("name为:${p.name}");
}
传入的动物是:晓白 ,种类是:牧羊犬
牧羊犬
张三
法外狂徒
name为:张三
工厂构造函数
通过 factory
声明,工厂函数不会自动生成实例,而是通过代码来决定返回的实例。
getter 与 setter
- get方法,注意方法名之后不加小括号。
- set方法,注意set方法需要加小括号。
class Rect{
late num _height;
late num _width;
Rect(this._height,this._width);
// get方法,注意方法名之后不加小括号
get getarea{
return this._height* this._height;
}
// set方法,注意set方法需要加小括号
set setHeight(value){
this._height = value;
}
}
void main(List<String> args) {
Rect rect = new Rect(10, 20);
// 得到getarea值
print("矩形面积为:${rect.getarea}");
// 调用set方法为height设置值
rect.setHeight=30;
print("面积为:${rect.getarea}");
}
矩形面积为:100
面积为:900
类的初始化列表
- 先赋值,在进行实例化构造函数。
- 将需要赋值的属性写在构造函数小括号后面。
class Rect{
late num height;
late num width;
// 初始化列表
Rect():height=3,width=20{
print("height为:${this.height},width为:${this.width}");
}
}
void main(List<String> args) {
// 创建对象
Rect rect = new Rect();
}
height为:3,width为:20
静态成员
- 使用
static
关键字来实现类级别的变量和函数。 - 静态方法不能访问非静态成员,非静态方法可以访问静态成员。
- 静态方法和静态属性不能由对象进行访问,只能由类直接访问。
import 'dart:ffi';
class Person{
static String name = "张三"; // 定义静态属性
int age = 18; // 定义非静态方法
// 定义静态方法
static void show(){
print("我的名字叫:${name}");
}
void printInfo(){
print("${name}的信息");
print("年龄为:${this.age}");
// 调用静态方法
show();
}
static void printUserInfo(){
print(name); // 访问静态属性
show(); // 调用静态方法
// print(age); // 静态方法无法范文非静态属性
// this.printInfo(); // 静态方法无法访问非静态方法
// printInfo(); // 静态方法无法访问非静态方法
}
}
void main(List<String> args) {
// Person person = new Person(); // 创建对象
// person.show(); // 被修饰的静态方法不能由创建的对象进行访问
// 由类名进行访问
print(Person.name);
Person.show();
// 非静态方法与非静态属性的使用
Person person = new Person();
person.printInfo();
print(person.age);
// 调用静态方法
Person.printUserInfo();
}
张三
我的名字叫:张三
张三的信息
年龄为:18
我的名字叫:张三
18
张三
我的名字叫:张三
继承
- 子类使用
extends
关键字来继承父类。 - 子类会继承父类里面可见的属性和方法,但是不会继承构造函数。
- 子类能复写父类的方法 getter 和setter。
class Person{
String name ="张三";
num age = 20;
void printInfo(){
print("姓名:${this.name},年龄:${this.age}");
}
}
// 创建一个男人类,并继承人类
class Man extends Person{
}
void main(List<String> args) {
// 创建一个男人对象
Man man = new Man();
print(man.name); // 调用人类的name
man.printInfo(); // 调用父类的方法
}
张三
姓名:张三,年龄:20
super关键字的使用
- 重写父类构造函数,为父类构造函数进行传参。
- 因为父类里面的属性没有赋值,而子类又需要调用父类的属性,只有通过构造函数进行赋值。
- 在复写父类的方法时,记得加上
@override
关键词。
class Person{
late String name;
late num age;
// 构造函数
Person(this.name,this.age);
void printInfo(){
print("Person类");
print("姓名:${this.name},年龄:${this.age}");
}
}
// 创建一个男人类,并继承人类
class Man extends Person{
// 定义子类的属性
late String sex;
// 重写父类构造函数
Man(String name, num age, String sex) : super(name, age){ // 实例化构造函数的时候,将子类的name与age传递给父类的构造函数
this.sex =sex; // 将子类的构造函数里的sex赋值给子类sex属性
}
// 复写父类方法
@override
void printInfo(){
print("man类");
print("姓名:${this.name},年龄:${this.age},性别:${this.sex}");
}
// 定义子类的方法
void run(){
print("儿子类子跑");
}
}
void main(List<String> args) {
// 创建父类对象
Person person = new Person("张三",18);
person.printInfo();
// Person person1 = new Person("李四",56);
// person1.printInfo();
// 创建子类对象
Man man =new Man("张大娃", 32,"男");
man.printInfo(); // 通过构造函数,找到父类的构造函数,使用父类的方法进行打印。
man.run(); // 子类调用子类的方法
}
Person类
姓名:张三,年龄:18
man类
姓名:张大娃,年龄:32,性别:男
儿子类子跑
使用super
为命名构造函数传参。
class Person{
late String name;
late num age;
// 命名构造函数
Person.xxx(this.name,this.age);
// 创建一个男人类,并继承人类
class Man extends Person{
// 定义子类的属性
late String sex;
// 重写父类构造函数
Man(String name, num age, String sex) : super.xxx(name, age){ // 实例化命名构造函数的时候,将子类的name与age传递给父类的构造函数
this.sex =sex; // 将子类的构造函数里的sex赋值给子类sex属性
}
// 复写父类方法
@override
void printInfo(){
print("man类");
print("姓名:${this.name},年龄:${this.age},性别:${this.sex}");
}
void main(List<String> args) {
Person person = new Person.xxx("张三",18); // 命名构造函数传参
person.printInfo();
Man man =new Man("张大娃", 32,"男");
man.printInfo(); // 通过构造函数,找到父类的构造函数,使用父类的方法进行打印。
man.run(); // 子类调用子类的方法
}
Person类
姓名:张三,年龄:18
man类
姓名:张大娃,年龄:32,性别:男
-
子类调用父类方法,在子类的方法名前面加上一个
supper
关键字,例如:supper.run()
,调用父类的run()方法
。 -
子类中使用父类的属性时,可以使用
this
关键词进行调用。
抽象类
- 抽象类主要用于定义标准。
- 抽象类通过
abstract
关键字来定义。 - Dart中的抽象方法不能用
abstract
声明,Dart中没有方法体的方法我们称为抽象方法。 - 如果子类继承抽象类必须得实现里面的抽象方法。
- 如果把抽象类当作接口实现的话。必须实现抽象类里面定义的所有属性和方法。
- 抽象类不能被实例化,只有继承他的子类可以实例化。
- extends 抽象类和 implements 的区别:
- 如果复用抽象类里面的方法,并且要用抽象方法约束子类的话,我们要用extends继承抽象类。
- 如果只是把抽象类当作标准的话,我们需要用implements实现抽象类。
// 定义抽象类
abstract class Animal{
eat(); // 抽象方法
run();
// 在抽象类中定义,该方法可以有方法体
printInfo(){
print("我来自抽象类");
}
}
// 子类继承父类必须实现父类中的所有方法
class Dog extends Animal{
@override
eat() {
print("小狗在啃骨头");
}
@override
run() {
print("小狗再跑");
}
}
// 子类继承父类必须实现父类中的所有方法
class Cat extends Animal{
@override
eat() {
print("小猫在吃饭");
}
@override
run() {
print("小猫在跑");
}
}
void main(List<String> args) {
Dog dog = new Dog();
dog.eat();
dog.printInfo(); // 调用抽象类的方法
Cat cat = new Cat();
cat.eat();
cat.printInfo(); // 调用抽象类的方法
}
小狗在啃骨头
我来自抽象类
小猫在吃饭
我来自抽象类
多态
- 多态就是父类定义的一个方法不去实现,让他的子类去实现,每个子类有不同的表现。
- 子类的实例赋值给父类。
// 定义抽象类
abstract class Animal{
eat(); // 抽象方法
}
// 子类继承父类必须实现父类中的所有方法
class Dog extends Animal{
@override
eat() {
print("小狗在啃骨头");
}
//
run(){
print("小狗正在跑");
}
}
// 子类继承父类必须实现父类中的所有方法
class Cat extends Animal{
@override
eat() {
print("小猫在吃饭");
}
//
run(){
print("小狗正在跑");
}
}
void main(List<String> args) {
// 子类的一个实例赋值给父类的应用,也就是所在下面创建的对象就属于父类,而run方法时子类中定义的,所以父类无法调用子类的run方法。
Animal dog = new Dog();
dog.eat();
// dog.run(); // 无法调用run方法
Animal cat = new Cat();
cat.eat();
// cat.run(); // 无法调用run方法,父类中没有run 方法
}
小狗在啃骨头
小狗正在跑
小猫在吃饭
接口
- Dart的接口没有
interface
关键字定义接口,普通类或抽象类都可以作为接口被实现。 - 使用
implement
关键字进行实现。 - 如果实现的类是普通类,会将普通类和抽象类中的属性的方法全部重写一边。
- 抽象类可以定义抽象方法,普通类不可以,普通类不可以,所以一般如果要实现象Java接口那样的方式,一般会使用抽象类。
- 建议使用抽象类定义接口。
- 接口就是约定、规范。
// 定义接口
abstract class Person{
late String name;
eat();
run();
info(String str);
}
// 继承Person接口
class Man implements Person{
@override
late String name;
@override
eat() {
print("男人在吃饭");
}
@override
info(String str) {
print("${str}的信息");
}
@override
run() {
print("男人在奔跑");
}
Man(String str);
}
// 继承Person接口
class Women implements Person{
@override
late String name;
@override
eat() {
print("女人在吃饭");
}
@override
info(name) {
print("${name}的信息");
}
@override
run() {
print("女人在奔跑");
}
// Women(this.name);
}
void main(List<String> args) {
// 创建man对象
Man man = new Man("张三");
man.info("张三");
// 创建women对象
Women women = new Women();
women.info("张如玉");
women.run();
}
可以将这些接口、类、main
进行分离,分别放在不同的Dart
文件中,如果要导入这些模块,需要使用improt
关键字进行导入。
多接口 mixins新特性
- mixins的中文意思是混入,就是在类中混入其他功能。
- 在Dart中可以使用
mixins
实现类似多继承的功能。 - 作为
mixins
的类只能继承自Object
,不能继承其他类。 - 作为
mixins
的类不能有构造函数。 - 一个类可以
mixins
多个mixins
类。 mixins
绝不是继承,也不是接口,而是一种全新的特性。
小结:
多继承:
使用with后面的类:
被继承的类不能继承其他类
被继承的类中不能有构造函数
with
class X with a,b{
...
}
也可以
class X extends c with a,b{
...
}
抽象类:
抽象类不能实例化,继承必须实现内部抽象方法和属性
抽象类可以继承extends(复用其中普通方法),可以实现implements(当模板)
抽象类中的方法不加abstract
abstract class x{
int a;
int b;
void f(); 抽象方法
void ff(){...}; 普通方法
}
继承该抽象类的类,只用实现抽象方法,普通方法和属性不用重写
多态:父类引用子类,父类的方法引用子类的方法,只能调用父类中存在的方法,不能调用子类中父类没有的方法
Animal c=new Dog(); c只能调用Animal中的方法,且引用子类Dog中的相同方法
接口:
使用abstract定义抽象类为接口,操作和抽象类一样
抽象类的所有属性和函数都必须重写
abstract class x{
int a;
int b;
void f(); 抽象方法
void ff(){...}; 普通方法
}
class c implements x{
int a;
int b;
void f(){}
void ff(){}
}
多接口:
class c implements a,b{...}
泛型
- 泛型就是解决 类、 接口、 方法的复用性、以及对不特定数据类型的支持(类型校验)。
- 不指定类型就是放弃了类型校验。想传入什么类型就可以传入什么类型。
T getValue<T>(T value){..方法体..return..}
- 第一个T表示返回类型,第二个T表示函数类型,第三个T表示参数类型。
泛型方法
// 定义泛型方法,一般使用大写 T 表示
T getValue<T>(T value){
return value;
}
void main(List<String> args) {
// 传入int类型参数
var d = getValue(123);
print(d);
// 传入string类型参数并指定类型。
var str =getValue<String>("张三");
print(str);
}
123
张三
泛型类
- 泛型就是一种通用的类型格式,一般用在集合中,用来指定该集合中应该存储的对象格式。
- 泛型一般使用大写的单个字符来表示,通常来说是E, T, S, K 和 V等。
- 巧妙的使用泛型还能够减少我们的代码量,因为泛型可以代表一类通用的类型。
class Mylist<T>{ // 定义一个泛型类
List list = <T>[]; // 定义一个泛型列表
// 定义一个泛型方法,负责向列表中添加数据
void add(T value){
this.list.add(value);
}
// 返回列表
List getList(){
return list;
}
}
void main(List<String> args) {
// 限定为String类型的列表
Mylist l2 = Mylist<String>();
l2.add("张三"); // 添加一个string参数
l2.add("李四");
// l2.add(123); // 在创建对象时限定为String类型,只能传入String数据
print(l2.getList()); // 打印列表
// 限定为int类型的列表
Mylist l3 = Mylist<int>();
l3.add(123);
l3.add(456);
print(l3.getList());
}
[1, 张三, false]
。。。。。。。。
有时间再更新
。。。。。。。。