1. 认识DartGoogle为Flutter选择了Dart语言已经是既
1. 认识Dart
Google为Flutter选择了Dart语言已经是既定的事实,无论你多么想用你熟悉的语言,比如JavaScript、TypeScript、ArkTS等来开发Flutter,至少目前都是不可以的。
- Dart 是由谷歌开发的计算机编程语言,它可以被应用于 Web/服务器/移动应用和物联网等领域开发。
- Dart 也是 Flutter 的基础,Dart 作为 Flutter 应用程序的编程语言,为驱动应用运行提供了环境。
- 因此, 要学习Flutter, 则首先得会Dart, Dart 目前最新稳定版本:v3.4.0
好消息:如果你会 JavaScript、 Typescript、Java 中任何一门语言,你将很快且很容易就能学会Dart的使用。
2. 搭建Dart开发环境
为什么需要安装Dart呢?
事实上如果提前安装了Flutter SDK,它已经内置了Dart了,我们完全可以直接使用Flutter去进行Dart的编写并且运行。
但是,如果想单独学习Dart,并且运行自己的Dart代码,最好去安装一个Dart SDK。
要在本地开发Dart程序, 首先需要安装Dart SDK
官方文档: Get the Dart SDK | Dart , 中文文档: Dart 编程语言主页 | Dart 中文文档 | Dart
无论是什么操作系统,安装方式都是有两种:通过工具安装
和直接下载SDK,配置环境变量
a.通过工具安装
- Windows可以通过Chocolatey
- macOS可以通过homebrew
- 具体安装操作官网网站有详细的解释
b.直接下载SDK,配置环境变量
- 下载地址:https://dart.dev/tools/sdk/archive
- 我采用了这个安装方式。
- 下载完成后,根据路径配置环境变量即可。
1. Windows环境安装Dart
1.1 下载压缩包, 然后解压放在任意盘符下(注意不要是中文目录下)
1.2 找到bin目录, 复制完整路径, 配置环境变量
1.3 cmd 窗口执行 dart --version 看到dart-sdk的版本代表OK
2. Mac环境安装Dart
- macOS支持的架构: x64、ARM64
2.1 下载并解压Dart SDK,放到电脑的某个目录下面(不要是中文)
2.2 配置环境变量
打开终端,使用文本编辑器(如vi
或nano
)编辑~/.zshrc
和~/.bash_profile
文件。在文件的末尾添加以下行,将Dart SDK的路径添加到系统的PATH中:
在终端中运行vim ~/.zshrc
命令
export PATH="/path/to/dart/sdk/bin:$PATH"
注意:
- 将
/path/to/dart/sdk/bin
替换为你解压Dart SDK的实际路径。 - 保存并关闭文件:保存并关闭文本编辑器(按下esc,输入:wq)
2.3 使环境变量生效
在终端中运行source ~/.zshrc
命令,使新的环境变量设置立即生效。
2.4 验证安装
运行dart --version
命令来检查Dart的版本信息,如果正确显示版本信息,则表示Dart已经成功安装。
3. Dart初体验
3.1 VSCode中安装常用插件
- Dart插件:可以帮助我们在VSCode中更加高效的编写Dart代码
提供了友好的代码提示,代码高亮,以及代码的重构、运行和重载
- Flutter插件:为Flutter开发准备的
- Code Runner:可以点击右上角的按钮快速运行代码
3.2 第一个Dart程序
- 创建dart文件
- dart文件名必须以
.dart
结尾:01-第一个dart程序.dart
- 编写dart代码
需求:打印字符串 'hello dart!'
// 程序的入口:main函数
void main() {// 需求:打印字符串 'hello itcast'print('hello itcast'); // hello itcast
}
- 执行dart代码
- 方式一:终端中执行:终端打开dart文件所在目录
dart 01-dart初体验.dart
- 方式二:
-
- VSCode中执行
-
- VSCode中查看代码执行结果
小结:
1、Dart语言的入口也是main函数,并且必须显示的进行定义;
2、Dart的入口函数main
是没有返回值的;
3、定义字符串的时候,可以使用单引号或双引号;
4、每行语句必须使用分号结尾,很多语言并不需要分号,比如JavaScript;
4. Dart基础语法
4.1 变量和常量(存储数据)
4.1.1 变量(存储并修改数据)
需求:存储一个姓名和年龄并修改年龄?
实现:变量关键字:var
要点:
- 声明变量:var 变量名 = 表达式;
- 修改变量:变量名 = 新值;
- 类型推断:var关键字声明的变量支持类型推断,修改变量时会检查之前存储数据的类型
void main() {// 1. 声明变量:var 变量名 = 表达式;var name = 'itheima';print(name); // itheimavar age = 17;print(age); // 17// 2. 修改变量:变量名 = 新值;age = 18;print(age); // 18// 3. 类型推断:var关键字声明的变量支持类型推断,修改变量时会检查之前存储数据的类型// 报错:A value of type 'String' can't be assigned to a variable of type 'int'.// age = 'itcast';
}
4.1.2 常量(存储不变的数据)
需求:存储不可被修改的数据
实现:常量关键字:const
和 final
区别:final是运行时常量,值在运行时赋值;const是编译期常量,值在编译时赋值;
void main() {// 1. const声明常量const num1 = 10;print(num1); // 10// 报错:Constant variables can't be assigned a value.// num1 = 20;// 2. final声明常量final num2 = 30;print(num2); // 30// 报错:The final variable 'num2' can only be set once.// num2 = 40;// 3. const和final的区别:var x = 1;var y = 2;// 3.1 final:运行时常量,值在运行时赋值final ret1 = x + y;print(ret1);// 3.2 const:编译期常量,值在编译时赋值// 报错:Const variables must be initialized with a constant value.// const ret2 = x + y;const ret3 = 1 + 2;
}
注意: const 和 final的区别
final:运行时常量,值在运行时赋值
const:编译期常量,值在编译时赋值
4.2 数据类型(可存储数据类型)
4.2.1 num(数字)
需求:存储并修改整数和小数
实现:关键字:num
、int
、double
注意点:
num类型的变量既可以存整数也可以存小数
int类型的值可以赋值给double类型的变量,但是double类型的值不能赋值给int类型的变量
void main() {// 1. num类型 (整数、小数)num n1 = 10;print(n1); // 10n1 = 10.1;print(n1); // 10.1// 2. int类型 (整数)int n2 = 20;print(n2); // 20// 测试:将小数赋值给int// 报错:A value of type 'double' can't be assigned to a variable of type 'int'.// n2 = 20.2;// 3. double类型 (小数)double n3 = 30.3;print(n3); // 30.3// 测试:将整数赋值给doublen3 = 30;print(n3); // 30.0
}
4.2.2 String(字符串)
Dart字符串是UTF-16编码单元的序列。您可以使用单引号或双引号创建一个字符串
需求:声明字符串,修改字符串,拼接字符串
注意:模板字符串支持运算
实现:关键字:String
void main() {// 1. 定义字符串String name = '张三';String gender = "男";// 2. 修改字符串name = '李四';gender = '女';print(name);print(gender);// 3. 字符串拼接double money = 99991.99;String intro = '姓名:$name,性别:$gender,钱包:${money * 100}';print(intro);// 4. 字符串换行String tag = '''通知明天天气不好注意放假''';print(tag);
}
4.2.3 bool(布尔)
需求:存储并修改真或假
实现:关键字:bool
void main() {// 登录成功:truebool isLogin = true;print(isLogin); // true// 登录失败:falseisLogin = false;print(isLogin); // false
}
4.2.4 List(列表)
需求:使用一个变量有序的存储多个值
实现:列表(数组)关键字:List
- 定义列表 List 变量名 = [元素1, 元素2, ..., 元素n];
-
- 1.1 需求:按序存储数字 1 3 5 7
- 1.2 需求:按序存储字符串 '居家' '美食' '服饰'
- 1.3 列表中可以存储任意类型的数据
void main() {// 1. 定义列表:List 变量名 = [元素1, 元素2, ..., 元素n];// 1.1 需求:按序存储数字 1 3 5 7List nums = [1, 3, 5, 7];print(nums);// 1.2 需求:按序存储字符串 '居家' '美食' '服饰'List categories = ['居家', '美食', '服饰'];print(categories);// 1.3 列表中可以存储任意类型的数据List ret = [18,18.8,'美食',true,['itcast', 10],{}];print(ret);
}
- 使用列表:查改增删
-
- 查询列表长度
- 查询指定的元素
- 修改:列表[索引] = 新值
- 新增:列表.add(新元素)、列表.addAll(新列表)
- 指定位置添加:列表.insert(索引, 内容');
- 删除:使用元素删除、使用索引删除
- 遍历列表:读取出列表中每一个元素remove,removeAt
// 2. 使用列表:查改增删
// 2.1 查询:
// 查询列表的长度(列表内部元素的个数)
int len = categories.length;
print(len); // 3
// 查询指定的元素: 列表[索引]
String category = categories[1];
print(category); // 美食// 2.2 修改:列表[索引] = 新值
categories[1] = '美食家';
print(categories); // [居家, 美食家, 服饰]// 2.3 新增:
// 一次新增一个元素:列表.add(新元素)
categories.add('母婴');
print(categories); // [居家, 美食家, 服饰, 母婴]
// 一次新增多个元素:列表.addAll(新列表)
categories.addAll(['办公', '生鲜']);
print(categories); // [居家, 美食家, 服饰, 母婴, 办公, 生鲜]
// 在指定位置新增元素
categories.insert(2, '教育');
print(categories); // [居家, 美食家, 教育, 服饰, 母婴, 办公, 生鲜]// 2.4 删除
// 使用元素删除
categories.remove('生鲜');
print(categories); // [居家, 美食家, 教育, 服饰, 母婴, 办公]
// 使用索引删除
categories.removeAt(4);
print(categories); // [居家, 美食家, 教育, 服饰, 办公]
- 遍历列表:读取出列表中每一个元素
// 3. 遍历列表:读取出列表中每一个元素
// element: 列表中读取出来的每一个元素
categories.forEach((element) {print(element);
});
4.2.5 Map(字典)
需求:声明键值对的集合
,并使用与之相关联的键从中获取值(类似JS中的对象)
实现:字典关键字:Map
- 存储商品分类的编号 和 名称
- 对字典数据进行查改增删
2.1 查询:字典[key]
2.2 修改:字典[key] = 新值
2.3 新增:字典[新key] = 新值 (注意:key必须是当前字典中不存在的key,如果key已存在就是修改)
2.4 删除:remove(key) (注意:如果key不存在,不会报错,也不会执行删除操作)
- 遍历字典
- 定义字典语法
Map 变量名 = {'键1': 值1,'键2': 值2,...,
};
- 例子:存储商品分类的编号 和 名称
void main() {// 1. 例子:存储商品分类的编号 和 名称Map category = {'id': 1,'name': '居家',};print(category); // {id: 1, name: 居家}
}
- 使用字典:查改增删
// 2. 使用字典:查改增删
// 2.1 查询:字典[key]
String name = category['name'];
print(name); // 居家// 2.2 修改:字典[key] = 新值
category['name'] = '美食';
print(category); // {id: 1, name: 美食}// 2.3 新增:字典[新key] = 新值
// 注意:key必须是当前字典中不存在的key,如果key已存在就是修改
category['price'] = 199.9;
print(category); // {id: 1, name: 美食, price: 199.9}// 2.4 删除:remove(key)
// 注意:如果key不存在,不会报错,也不会执行删除操作
category.remove('name');
print(category); // {id: 1, price: 199.9}
- 遍历字典
// 3. 遍历字典
category.forEach((key, value) {print('$key -- $value');// id -- 1// price -- 199.9
});
4.2.6 Dart空安全机制
如何尽早的发现并解决null带来的异常?
Dart提供了健全的空安全机制
,默认所有的变量都是非空的,如果某个变量得到了一个null,则代码在编译期就会报错
- 无法正常执行的代码:在代码编译期就会报错
- 解决办法:使用 ? 显示的指定变量可以为空
- 使用可以为空的变量
void main() {// 1. 可以正常执行的代码String name1 = 'itcast';print(name1.length);// 2. 无法正常执行的代码:在代码编译期就会报错// String name2;// The non-nullable local variable 'name2' must be assigned before it can be used.// 报错:Null check operator used on a null value// print(name2.length);// 3. 解决办法:使用 ? 显示的指定变量可以为空String? name3 = null;print(name3);// 4. 使用可以为空的变量// name3? : 表示非空检查,如果name3为空,不去调用属性或方法,如果name3不为空,就去调用属性或方法print(name3?.length);
}
4.3 运算符(数据如何做运算)
4.3.1 算术运算符
如何对数字做加减乘除等运算?
多了取整 ~/
void main() {int n1 = 10;int n2 = 3;// 加 +print(n1 + n2); // 13// 减 -print(n1 - n2); // 7// 乘 *print(n1 * n2); // 30// 除 /print(n1 / n2); // 3.3333333333333335// 取整:取除法结果的整数部分 ~/print(n1 ~/ n2); // 3// 取模:取除法结果的余数 %print(n1 % n2); // 1// 案例:计算购物车商品总价格:商品A一件,每件289.0元;商品B二件,每件39.0元double total = 289.0 + (2 * 39.0);print(total); // 367.0
}
4.3.2 赋值运算符(同TS)
如何对数字做赋值运算?
void main() {// 等于 =int n1 = 10;// 加等于 +=// n1 = n1 + 5;n1 += 5;print(n1); // 15// 减等于 -=n1 -= 5;print(n1); // 10// 乘等于 *=n1 *= 5;print(n1); // 50// 除等于 /=// 注意:double类型的数据才能做除等于的操作// A value of type 'double' can't be assigned to a variable of type 'int'.// n1 /= 5;double n2 = 50;n2 /= 5;print(n2); // 10.0// 取余等于 %=int n3 = 10;n3 %= 3;print(n3); // 1// 自增:在原有数值上加1 ++int a = 10;a++;print(a); // 11// 自减:在原有数值上减1 --int b = 20;b--;print(b); // 19
}
4.3.3 比较运算符
如何比较数字大小?
没有 === 和 !== 运算符
void main() {int n1 = 10;int n2 = 20;// 大于 >print(n1 > n2); // false// 小于 <print(n1 < n2); // true// 大于等于 >=print(n1 >= n2); // false// 小于等于 <=print(n1 <= n2); // true// 等于 ==print(n1 == n2); // falseprint('itcast' == 'itheima'); // false// 不等于 !=print(n1 != n2); // trueprint('itcast' != 'itheima'); // true
}
4.3.4 逻辑运算符(同TS)
如果表示数据之间的逻辑关系?
void main() {// 年龄int age = 33;// 工作年限int years = 10;// 1. 逻辑与:一假则假// 年龄大于28岁,并且工作年限大于4年bool ret1 = age > 35 && years > 4;print(ret1); // false// 2. 逻辑或:一真则真// 年龄大于23岁,或者工作年限大于2年bool ret2 = age > 35 || years > 2;print(ret2); // true// 3. 逻辑非:真变假,假变真print(!true); // falseprint(!false); // true// 工作年限不小于9年bool ret3 = years >= 9;// bool ret3 = !(years < 9);print(ret3); // true
}
4.4 流程控制(选择或重复执行)
4.1.1 if分支语句(同TS)
单分支、双分支、多分支
void main() {// 1. if单分支语句// 准备高考成绩,如果分数大于等于700分,则输出 '恭喜考入黑马程序员'int score1 = 699;if (score1 >= 700) {print('恭喜考入黑马程序员');}// 2. if双分支语句// 准备高考成绩,如果分数大于等于700分,则输出 '恭喜考入黑马程序员',反之,则输出 '继续努力'int score2 = 699;if (score2 >= 700) {print('恭喜考入黑马程序员');} else {print('继续努力');}// 3. if多分支语句// 根据学生分数划分学生成绩等级:// 优秀:分数大于等于90分// 良好:分数小于90分,且大于等于80分// 中等:分数小于80分,且大于等于60分// 不及格:分数小于60分int score3 = 58;if (score3 >= 90) {print('优秀');} else if (score3 >= 80) {print('良好');} else if (score3 >= 60) {print('中等');} else {print('不及格');}
}
4.1.2 三元运算符(同TS)
简化简单的if双分支语句
void main() {// 需求:准备高考成绩,如果分数大于等于700分,则输出 '恭喜考入黑马程序员',反之,则输出 '继续努力'// 思考:以下代码可以简化吗?int score1 = 699;// if (score1 >= 700) {// print('恭喜考入黑马程序员');// } else {// print('继续努力');// }// 1. 使用三元运算符简化if双语句:条件表达式 ? 表达式1 : 表达式2score1 >= 700 ? print('恭喜考入黑马程序员') : print('继续努力');// 2. 思考:以下代码适合使用三元运算符改写吗?int score2 = 88;if (score2 >= 90) {print('优秀');} else if (score2 >= 80) {print('良好');} else if (score2 >= 60) {print('中等');} else {print('不及格');}
}
4.1.3 switch case 语句(同TS)
如果分支很多,且条件是判断相等,则switch case 语句性能比 if 分支语句要好
void main() {// 根据订单状态,打印出订单状态描述信息// 订单状态:1为待付款、2为待发货、3为待收货、4为待评价int orderState = 3;switch (orderState) {case 1:print('待付款');break;case 2:print('待发货');break;case 3:print('待收货');break;case 4:print('待评价');break;default:print('其他');}
}
4.1.4 循环语句(同TS)
如何让代码重复执行?
循环语句:while循环
和 for循环
void main() {// 1. while循环// 重复打印10次 '月薪过万'int n = 0;while (n < 10) {print('$n -- 月薪过万');n++;}// 2. for循环// 重复打印5次 '李白姓白'for (var i = 0; i < 5; i++) {print('$i -- 李白姓白');}// 3. 使用循环遍历列表// 3.1 遍历列表:for循环List categories = ['居家', '美食', '服饰'];for (var i = 0; i < categories.length; i++) {String name = categories[i];print(name);}// 3.2 遍历列表:for ... infor (var item in categories) {// item就是遍历出来的元素print(item);}// 4. 终止循环// 4.1 break:中断整个循环for (var i = 0; i < 5; i++) {if (i == 2) {// 吃到第三个苹果发现了虫子,剩下的苹果没胃口都不吃了break;}print('我把第 ${i + 1} 个苹果吃了');}// 4.2 continue:跳过本次循环直接进入下一次循环for (var i = 0; i < 5; i++) {if (i == 2) {// 吃到第三个桃子发现了虫子,第三个桃子不吃了,剩下的桃子接着吃continue;}print('我把第 ${i + 1} 个桃子吃了');}
}
4.5 基础语法 - 综合案例
需求:计算购物车数据中,被勾选商品的总价
void main() {// 准备购物车数据List carts = [{"count": 2, "price": 10.0, "selected": true},{"count": 1, "price": 30.0, "selected": false},{"count": 5, "price": 20.0, "selected": true}];// 记录总金额double totalAmount = 0.0;// 遍历购物车数据carts.forEach((element) {// 读取商品的勾选状态bool selected = element['selected'];// 如果商品被勾选 ,读取该商品的单价和数量,并计算价格小计if (selected) {double amount = element['count'] * element['price'];// 累加价格小计,计算总价totalAmount += amount;}});print(totalAmount);
}
五、函数(复用代码)
5.1 函数的定义
- 定义函数:无参数无返回值函数
- 定义函数:有参数有返回值函数
- 函数的特点:
- 返回值类型和参数类型是可以省略的
void main() {// 2. 调用无参数无返回值函数func();// 4.调用有参数有返回值函数 int ret = sum(10, 20);print(ret); // 30
}// 1. 定义函数:无参数无返回值函数
void func() {print('这是一个无参数无返回值函数');
}// 3. 定义函数:有参数有返回值函数
// 需求:定义函数,计算任意两个整数的和,并返回计算结果
int sum(int a, int b) {int ret = a + b;return ret;
}
- 函数的特点:
-
- 返回值类型和参数类型是可以省略的
void main() {// 2. 调用无参数无返回值函数// func();// 4. 调用有参数有返回值函数int ret = sum(10, 20);print(ret); // 30
}// 1. 定义函数:无参数无返回值函数
func() {print('这是一个无参数无返回值函数');
}// 3. 定义函数:有参数有返回值函数
// 需求:定义函数,计算任意两个整数的和,并返回计算结果
// 特点2:返回值类型和参数类型是可以省略的
sum(a, b) {int ret = a + b;return ret;
}
5.2 函数的参数
函数的参数可以分为:必传参数(位置参数)、可选参数(关键字参数)
注意点:必传参数不能为空,可选参数可以为空,且参数都可以设置默认值
void main() {printString('张三丰');printString('李四', age: 18);printString('王五', location: '昌平区');printString('赵六', age: 18, location: '海淀区');
}/*** name:必传参数* age:可选参数* location:可选参数,并有默认值*/
void printString(String name, {int? age, String? location = '昌平区'}) {print('$name - $age - $location');
}
5.3 函数对象
函数可以作为对象赋值给其他变量
函数可以作为参数传递给其他函数
void main() {// 1.2 定义一个变量接收函数// var f = funcDemo1;Function f = funcDemo1;f();// 2.2 函数作为参数funcDemo2(funcDemo3);
}// 1.1 函数可以作为对象赋值给其他变量
void funcDemo1() {print('funcDemo1');
}// 2.1 函数可以作为参数传递给其他函数
void funcDemo2(Function func) {// 调用外界传入的函数func();
}// 定义作为参数的函数: 把funcDemo3传入到funcDemo2
void funcDemo3() {print('funcDemo3');
}
5.4 匿名函数
- 匿名函数赋值给变量,并调用
- 可以作为参数传递给其他函数去调用(回调函数)
void main() {// 匿名函数// 1. 匿名函数赋值给变量,并调用Function f = () {print('这是一个匿名函数');};f();// 2. 可以作为参数传递给其他函数去调用(回调函数)funcDemo(() {print('这个匿名函数是个参数');});
}// 定义一个接收函数作为参数的函数
void funcDemo(Function func) {func();
}
5.5 箭头函数
当函数体只有一行代码时,可以使用箭头函数简写
void main() {int ret1 = sum1(10, 20);print(ret1);int ret2 = sum2(30, 40);print(ret2);
}// 思考:以下代码可以简写吗?
sum1(a, b) {return a + b; // 函数体只有一行代码
}// 箭头函数简写函数体:简写只有一行代码的函数体
sum2(a, b) => a + b;
5.6 函数 - 综合案例
需求:计算购物车中商品是否全选
void main() {// 准备购物车数据List carts = [{"count": 2, "price": 10.0, "selected": true},{"count": 1, "price": 30.0, "selected": false},{"count": 5, "price": 20.0, "selected": true}];// 调用封装的函数bool isSelectedAll = getSelectedState(carts);if (isSelectedAll) {print('全选');} else {print('非全选');}
}// 核心逻辑:只要有任何一个商品是未勾选的,那么就是非全选
bool getSelectedState(List carts) {// 购物车初始的状态:假设默认是全选bool isSelectedAll = true;carts.forEach((element) {bool selected = element['selected'];// 核心代码:只要有任何一个商品是非勾选的,则购物车就是非全选if (selected == false) {isSelectedAll = selected;}});// 返回是否全选结果return isSelectedAll;
}
六、类(面向对象编程)
6.1 类的定义
需求:定义Person类,属性:名字和年龄,方法:吃饭
void main() {// 创建Person对象Person person = Person();// 1.1 属性赋值person.name = 'itheima';person.age = 17;// 1.2 读取属性print(person.name); // itheimaprint(person.age); // 17// 2.1 调用方法person.eat(); // 我是干饭人
}// 定义Person类,属性:名字和年龄,方法:吃饭
class Person {// 1. 属性String? name;int? age;// 2. 方法void eat() {print('我是干饭人');}
}
存在问题: 我们new一个对象时, 不能直接给对象绑定属性?
答案: 借助构造函数
6.2 构造函数
6.2.1 默认构造函数
无参数,默认隐藏
void main() {// 使用默认的构造函数创建对象Person person1 = Person();// 属性赋值person1.name = '张三';person1.age = 18;// 读取属性print(person1.name); // 张三print(person1.age); // 18// 调用方法person1.eat();
}// 定义Person类,属性:名字和年龄,方法:吃饭
class Person {// 默认的构造函数(无参数,默认隐藏)Person() {print('我是默认的构造函数');}// 属性String? name;int? age;// 方法void eat() {print('我是干饭人');}
}
6.2.2 自定义与类同名构造函数
自定义与类同名的构造函数时,可以有参数
注意点:与类同名的构造函数只能有一个,如果自定义了该类名构造函数,那么默认的构造函数就失效
void main() {// 使用自定义与类同名构造函数创建对象Person person2 = Person('李四', 19);// 读取属性print(person2.name); // 李四print(person2.age); // 19// 调用方法person2.eat();
}// 定义Person类,属性:名字和年龄,方法:吃饭
class Person {// 自定义与类同名构造函数:可以有参数// Person(String name, int age) {// this.name = name;// this.age = age;// }// 简写自定义与类同名构造函数:自定义与类同名构造函数时,如果函数的参数和类的属性同名可以简写Person(this.name, this.age);// 属性String? name;int? age;// 方法void eat() {print('我是干饭人');}
}
6.2.3 命名构造函数
实际开发中, 经常会发现这么一种写法: 类名.方法(参数...)
, 然后返回一个实例化对象, 这种写法在Dart中被称为命名构造函数
void main() {// 使用命名构造函数创建对象Person person3 = Person.withInfo('王五', 20);// // 读取属性print(person3.name); // 王五print(person3.age); // 20// 调用方法person3.eat();
}// 定义Person类,属性:名字和年龄,方法:吃饭
class Person {// 定义命名构造函数// Person.withInfo(String name, int age) {// this.name = name;// this.age = age;// }// 简写命名构造函数Person.withInfo(this.name, this.age);// 属性String? name;int? age;// 方法void eat() {print('我是干饭人');}
}
6.3 私有属性和方法
公有属性和方法:供类自身或者其他外部文件和类使用的属性和方法
私有属性和方法:仅供自身使用的属性和方法,其他外部文件和类无法访问
class Dog {// 公有属性String? name;// 私有属性int? _age;// 公有方法void eat() {print('dog eat');}// 私有方法void _run() {print('dog run');}
}
其他dart文件中使用:28-类-私有属性和方法2-使用.dart
// 导入Dart文件、库
import 'lib/Dog.dart';void main() {// 创建Dog对象Dog dog = Dog();dog.name = '旺财';print(dog.name);// 私有属性调用失败// print(dog._age);dog.eat();// 私有方法调用失败// dog._run();
}
6.4 继承(extends)
- [思考]:如下定义的的两个类
Man
和Woman
是否有重复的部分,是否可以优化?
void main() {// 创建男人对象Man man = Man('李雷', 13);print(man.name);man.eat();// 创建女人对象Woman woman = Woman('韩梅梅', 14);print(woman.name);woman.eat();
}/// 男人类
class Man {Man(this.name, this.age);String? name;int? age;void eat() {print('$name -- eat');}
}/// 女人类
class Woman {Woman(this.name, this.age);String? name;int? age;void eat() {print('$name -- eat');}
}
- 使用继承优化代码:继承的基本使用
[思考]:如下定义的的两个类Man和Woman是否有重复的部分,是否可以优化?
[解决]:定义父类Person
,用于封装公共的属性和方法,Man
和Woman
类作为子类去继承父类Person
的属性和方法
void main() {// 创建男人对象Man man = Man('李雷', 13);print(man.name);man.eat();// 创建女人对象Woman woman = Woman('韩梅梅', 14);print(woman.name);woman.eat();
}/// 人类:父类
class Person {Person(this.name, this.age);String? name;int? age;void eat() {print('$name -- eat');}
}/// 男人类:子类
class Man extends Person {// 定义子类构造函数// Man(String name, int age) : super(name, age);Man(super.name, super.age);
}/// 女人类:子类
class Woman extends Person {// 定义子类构造函数Woman(super.name, super.age);
}
- 提示:子类中可以重写父类的方法
void main() {// 创建男人对象Man man = Man('李雷', 13);print(man.name);man.eat();// 创建女人对象Woman woman = Woman('韩梅梅', 14);print(woman.name);woman.eat();
}/// 人类:父类
class Person {Person(this.name, this.age);String? name;int? age;void eat() {print('$name -- eat');}
}/// 男人类:子类
class Man extends Person {// 定义子类构造函数// Man(String name, int age) : super(name, age);Man(super.name, super.age);// 提示:子类中可以重写父类的方法,执行子类自己的逻辑@overridevoid eat() {print('我是$name,我爱吃肉');}
}/// 女人类:子类
class Woman extends Person {// 定义子类构造函数Woman(super.name, super.age);// 提示:子类中可以重写父类的方法,执行子类自己的逻辑@overridevoid eat() {print('我是$name,我爱吃蔬菜');}
}
6.5 混入(mixin、with)
- [思考]:以下代码如何让子类
Woman
也有唱歌的方法?
void main() {// 创建男人对象Man man = Man('李雷', 13);print(man.name);man.eat();man.sing();// 创建女人对象Woman woman = Woman('韩梅梅', 14);print(woman.name);woman.eat();
}/// 人类
class Person {Person(this.name, this.age);String? name;int? age;void eat() {print('$name -- eat');}
}/// 男人类
class Man extends Person {// 定义子类构造函数Man(super.name, super.age);// 唱歌的方法void sing() {print('$name -- 爱唱歌');}
}/// 女人类
class Woman extends Person {// 定义子类构造函数Woman(super.name, super.age);
}
- 使用Mixin扩展优化代码:Mixin扩展的基本使用
[思考]:如何让子类Woman
也有唱歌的方法?
[解决]:
方式1:将唱歌的方法,定义到子类Woman中(代码冗余)
方式2:将唱歌的方法,定义到父类Person中(代码扩展性不好,唱歌的方法不能被其他类复用)
方式3:使用mixin扩展一个类,扩展类中定义唱歌的方法
[mixin] 表示一个没有构造函数的类,这个类的方法可以组合到其他类中实现代码复用
[mixin] 可以同时对某个类设置多个扩展类,也可以扩展属性
void main() {// 创建男人对象Man man = Man('李雷', 13);// 继承的属性和方法print(man.name);man.eat();// 通过mixin扩展的方法man.sing(man.name);// 创建女人对象Woman woman = Woman('韩梅梅', 14);// 继承的属性和方法print(woman.name);woman.eat();// 通过mixin扩展的方法woman.sing(woman.name);woman.dance(woman.name);woman.danceType = '街舞';print(woman.danceType);
}/// 人类:父类
class Person {Person(this.name, this.age);String? name;int? age;void eat() {print('$name -- eat');}
}// 唱歌方法的扩展类
mixin SingMixin {// 唱歌的方法void sing(name) {print('$name -- 爱唱歌');}
}// 跳舞方法的扩展类
mixin DanceMixin {// 舞种String? danceType;// 跳舞的方法void dance(String? name) {print('$name -- 爱跳舞');}
}/// 男人类:子类
class Man extends Person with SingMixin {// 定义子类构造函数Man(super.name, super.age);
}/// 女人类:子类
class Woman extends Person with SingMixin, DanceMixin {// 定义子类构造函数Woman(super.name, super.age);
}
七、异步编程
7.1 Dart是单线程的
7.1.1 程序中的耗时操作
开发中的耗时操作
- 在开发中,我们经常会遇到一些耗时的操作需要完成,比如网络请求、文件读取等等;
- 如果我们的主线程一直在等待这些耗时的操作完成,那么就会进行阻塞,无法响应其它事件,比如用户的点击;
- 显然,我们不能这么干!!
如何处理耗时的操作呢?
- 针对如何处理耗时的操作,不同的语言有不同的处理方式。
- 处理方式一: 多线程,比如Java、C++、鸿蒙中,我们普遍的做法是开启一个新的线程(Thread),在新的线程中完成这些异步的操作,再通过线程间通信的方式,将拿到的数据传递给主线程。
- 处理方式二: 单线程+事件循环,比如JavaScript、Dart都是基于单线程加事件循环来完成耗时操作的处理。
接下来, 我们一起来看看在Dart中如何去处理一些耗时的操作吧!
7.2 Future
7.2.1 同步网络请求
我们先来看一个例子吧:
- 在这个例子中,我使用getNetworkData来模拟了一个网络请求;
- 该网络请求需要5秒钟的时间,之后返回数据;
import 'dart:io';void main() {print('开始执行main函数');print(getNetworkData());print('这是不能被阻塞的代码');
}String getNetworkData() {sleep(Duration(seconds: 5));return '返回的网络数据';
}
这段代码会运行怎么的结果呢?
- getNetworkData会阻塞main函数的执行
开始执行main函数
// 等待5秒
返回的网络数据
这是不能被阻塞的代码
显然,上面的代码不是我们想要的执行效果,因为网络请求阻塞了main函数,那么意味着其后所有的代码都无法正常的继续执行。
7.2.2 Future基本使用
我们来对上面的代码进行改进,代码如下:
- 和刚才的代码唯一的区别在于使用了Future对象来将耗时的操作放在了其中传入的函数中;
- 稍后,我们会讲解它具体的一些API,我们就暂时知道我创建了一个Future实例即可;
import 'dart:io';void main() {print('开始执行main函数');print(getNetworkData());print('这是不能被阻塞的代码');
}Future<String> getNetworkData() {return Future<String>(() {sleep(Duration(seconds: 5));return '返回的网络数据';});
}
我们来看一下代码的运行结果:
- 这一次的代码顺序执行,没有出现任何的阻塞现象;
- 和之前直接打印结果不同,这次我们打印了一个Future实例;
- 结论:我们将一个耗时的操作隔离了起来,这个操作不会再影响我们的主线程执行了。
- 问题:我们如何去拿到最终的结果呢?
开始执行main函数
Instance of 'Future<String>'
这是不能被阻塞的代码
7.2.3 Future链式调用
有了Future之后,如何去获取请求到的结果:通过.then的回调
import 'dart:io';void main() {print('开始执行main函数');// print(getNetworkData());getNetworkData().then((value) {print(value);});print('这是不能被阻塞的代码');
}Future<String> getNetworkData() {return Future<String>(() {sleep(Duration(seconds: 3));return '返回的网络数据';});
}
上面代码的执行结果:
开始执行main函数
这是不能被阻塞的代码
// 3s后执行下面的代码
返回的网络数据
执行中出现异常
如果调用过程中出现了异常,拿不到结果,如何获取到异常的信息呢?
import 'dart:io';void main() {print('开始执行main函数');getNetworkData().then((value) {print(value);}).catchError((e) {print(e);});print('这是不能被阻塞的代码');
}Future<String> getNetworkData() {return Future<String>(() {sleep(Duration(seconds: 3));// 不再返回结果,而是出现异常// return '返回的网络数据';throw Exception('网络请求出现错误');});
}
上面代码的执行结果:
开始执行main函数
这是不能被阻塞的代码
// 3s后没有拿到结果,但是我们捕获到了异常
Exception: 网络请求出现错误
补充一:上面内容的小结
我们通过一个案例来学习了一些Future的使用过程:
1、创建一个Future(可能是我们创建的,也可能是调用内部API或者第三方API获取到的一个Future,总之你需要获取到一个Future实例,Future通常会对一些异步的操作进行封装);
2、通过.then(成功回调函数)的方式来监听Future内部执行完成时获取到的结果;
3、通过.catchError(失败或异常回调函数)的方式来监听Future内部执行失败或者出现异常时的错误信息;
补充二:Future的两种状态
事实上Future在执行的整个过程中,我们通常把它划分成了两种状态:
状态一:未完成状态(uncompleted)
- 执行Future内部的操作时(在上面的案例中就是具体的网络请求过程,我们使用了延迟来模拟),我们称这个过程为未完成状态
状态二:完成状态(completed)
- 当Future内部的操作执行完成,通常会返回一个值,或者抛出一个异常。
- 这两种情况,我们都称Future为完成状态。
Dart官网有对这两种状态解析,之所以拿出来说是为了区别于Promise的三种状态
7.2.4 Future链式调用小练习
例子:用户先登录,登录成功之后拿到token,然后再保存token到本地
// 用户先登录,登录成功之后拿到token,然后再保存token到本地
import 'dart:io';void main() {print('开始执行main函数');login().then((token) {setToken(token).then((res) {if (res == 'ok') {print('本地存储token成功, token是$token');}}).catchError((e) {print(e);});}).catchError((e) {print(e);});print('这是不能被阻塞的代码');
}// 1. 模拟耗时的登录操作
Future<String> login() {return Future<String>(() {sleep(Duration(seconds: 3));print('假装登录成功');String token = '666888';return token;});
}// 2. 模拟耗时的本地存储操作
Future<String> setToken(String token) {return Future<String>(() {sleep(Duration(seconds: 3));print('假装本地存储token');return 'ok';});
}
打印结果如下:
开始执行main函数
这是不能被阻塞的代码
// 3s后打印
假装登录成功
// 3s后打印
假装本地存储token
本地存储token成功, token是666888
7.3 async和await
如果你已经完全搞懂了Future,那么学习await、async应该没有什么难度。
await、async是什么呢?
- 它们是Dart中的关键字
- 它们可以让我们用
同步的代码格式
,去实现异步的调用过程
。 - 并且,通常一个async的函数会返回一个Future。
我们已经知道,Future可以做到不阻塞我们的线程,让线程继续执行,并且在完成某个操作时改变自己的状态,并且回调then或者errorCatch回调。
如何生成一个Future呢?
- 1、通过我们前面学习的Future构造函数,或者后面学习的Future其他API都可以。
- 2、还有一种就是通过async的函数。
通常使用 async await
解决Future链式调用带来的回调地狱的问题
// 用户先登录,登录成功之后拿到token,然后再保存token到本地
import 'dart:io';void main() {print('开始执行main函数');doLogin();print('这是不能被阻塞的代码');
}void doLogin() async {String token = await login();String res = await setToken(token);if (res == 'ok') {print('本地存储token $token 成功');}
}// 1. 模拟耗时的登录操作
Future<String> login() {return Future<String>(() {sleep(Duration(seconds: 3));print('假装登录成功');String token = '666888';return token;});
}// 2. 模拟耗时的本地存储操作
Future<String> setToken(String token) {return Future<String>(() {sleep(Duration(seconds: 3));print('假装本地存储token');return 'ok';});
}
八、泛型
[思考]:为什么List
和Map
中可以存储任意类型的数据?
[原因]:Dart在封装List和Map时,使用了泛型去限定了List和Map中数据的类型为dynamic
类型
[泛型]:可用于限定数据的类型,比如可以限定List和Map中数据的类型
abstract class List<E> implements EfficientLengthIterable<E>
[问题演示]:保存商品分类名称时,不应该出现100、true这样类型的数据
List categories = ['居家', '美食', 100, true];
[解决]:使用[泛型]限定List或者Map中元素的类型
void main() {// 1. 使用泛型限定List中元素的类型// 1.1 让列表中的元素只能是字符串类型// List categories = ['居家', '美食', 100, true];// List<String> categories = ['居家', '美食'];// 1.2 让列表中的元素只能是数字类型// List<num> nums = [100, 89, 10.99];// 2. 使用泛型限定Map中键和值的类型// 2.1 键和值都可以是任意类型Map categories1 = {1: 2, true: false, 'name': '张三', 'id': 1};print(categories1);// 2.2 键和值都只能是字符串类型Map<String, String> categories2 = {'id': '1','name': '居家',};print(categories2);// 2.3 键字符串类型, 值是任意类型Map<String, dynamic> categories3 = {'id': 1, 'name': '居家', 'isMan': true};print(categories3);
}
[泛型的作用]:在程序设计中提供一种机制,使得代码能够在编写时不指定具体类型,从而实现更灵活、可复用、类型安全的代码结构,能适应多种不同类型的数据处理需求。
比如:List和Map中的元素使用泛型限定为可以是任意类型的,从而不需要单独去封装存储某一种数据类型的List和Map
/*泛型的作用:使用泛型可以减少重复的代码封装函数:接收字符串就返回字符串,接收数字就返回数字,接收bool就返回bool*/
void main() {// 1. 普通封装// String demoString(String str) {// return str;// }// int demoInt(int a) {// return a;// }// bool demoBool(bool b) {// return b;// }// 2. 基于泛型封装T demo<T>(T parm) {return parm;}// 调用String ret1 = demo<String>('itcast');print(ret1);int ret2 = demo<int>(17);print(ret2);bool ret3 = demo<bool>(true);print(ret3);
}
九、异常处理
void main() {// 1. 捕获异常:try catch// try {// dynamic name = 'itheima';// name.haha();// } catch (e) {// print(e);// } finally {// // 无论是否有异常都会执行这个代码块// print('finally');// }// 2. 手动抛出异常:判断字符串是否相等,如果不相等手动抛出异常try {String str = 'itcast';if (str == 'yjh') {print('ok');} else {// 手动抛出异常throw Exception('字符串不相等');}} catch (e) {print(e);}
}
📎dart_code.zip
面试参考话术:
- 说一下 flutter?flutter 你会吗?
前提:flutter 项目都是比较早了 ,1 年及以前:
- flutter 是一个跨平台的开发框架,由谷歌推出,一次开发发布多平台,ios,安卓,鸿蒙,web 都是可以适配的。
- 开发的语言用的是 Dart,和 ArkTS 非常的类似,但是也有不同的地方,我挑几个说一下:
-
- 声明变量:
-
-
- dart:var,const,final
- ArkTS:let,const
-
-
- 类型
-
-
- 基本类型差不多
- ArkTS 中的 Record 在dart中叫做 map
- ArkTS 中的 数组在 dart 中叫 list
-
-
- 异步管理
-
-
- ArkTS 中用的是 Promise,可以简写为 async 和 await
- dart 中是 future,也可以简写为 async 和 await
- 他俩基本一样
-
-
- 还有一些 运算符,类子类的语法都是一样的,所以当时学习 ArkTS,包括之前学习前端的时候
- 之前学习 dart 的经验给了我很多的帮助
- 而且 dart,flutter 现在 ai 的语料已经很充足了,就算现在让我写,也问题不大
相关文章:
1. 认识DartGoogle为Flutter选择了Dart语言已经是既
1. 认识Dart Google为Flutter选择了Dart语言已经是既定的事实,无论你多么想用你熟悉的语言,比如JavaScript、TypeScript、ArkTS等来开发Flutter,至少目前都是不可以的。 Dart 是由谷歌开发的计算机编程语言,它可以被应用于 Web/…...
学习设计模式《三》——适配器模式
一、基础概念 适配器模式的本质是【转换匹配,复用功能】; 适配器模式定义:将一个类的接口转换为客户希望的另外一个接口;适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 适配器模式的目的:复用…...
【Java面试系列】Spring Boot微服务架构下的分布式事务处理与性能优化 - 2025-04-19详解 - 3-5年Java开发必备知识
【Java面试系列】Spring Boot微服务架构下的分布式事务处理与性能优化 - 2025-04-19详解 - 3-5年Java开发必备知识 引言 在微服务架构中,分布式事务处理和性能优化是面试中高频出现的主题。随着系统规模的扩大,如何保证数据一致性和系统性能成为开发者…...
Elasticsearch只返回指定的字段(用_source)
在Elasticsearch中,当你想要查询文档但不返回所有字段,只返回指定的字段(比如这里的id字段),你可以使用_source参数来实现这一点。但是,有一点需要注意:Elasticsearch的_source字段默认是返回的…...
【Linux “sed“ 命令详解】
本章目录: 1. 命令简介sed 的优势: 2. 命令的基本语法和用法基本语法:参数说明:常见用法场景:示例1:替换文本示例2:删除空行示例3:从命令输出中处理内容 3. 命令的常用选项及参数常用命令动作&a…...
JMETER使用
接口测试流程: 1.获取接口文档,熟悉接口业务 2.编写接口测试用例以及评审 正例:输入正常的参数,验证接口能否正常返回 反例:权限异常(为空、错误、过期)、参数异常(为空、长度异常、类型异常)、其他异常(黑名单、调用次数限制)、兼容异常(一个接口被多种…...
JavaWeb 课堂笔记 —— 13 MySQL 事务
本系列为笔者学习JavaWeb的课堂笔记,视频资源为B站黑马程序员出品的《黑马程序员JavaWeb开发教程,实现javaweb企业开发全流程(涵盖SpringMyBatisSpringMVCSpringBoot等)》,章节分布参考视频教程,为同样学习…...
离线安装elasticdump并导入和导出数据
离线安装elasticdump 在 CentOS 或 RHEL 系统上安装 elasticdump,你可以使用 npm(Node.js 的包管理器)来安装,因为 elasticdump 是一个基于 Node.js 的工具。以下是步骤 先在外网环境下安装 下载nodejs和npm(注意x8…...
WhatTheDuck:一个基于浏览器的CSV查询工具
今天给大家介绍一个不错的小工具:WhatTheDuck。它是一个免费开源的 Web 应用程序,允许用户上传 CSV 文件并针对其内容执行 SQL 查询分析。 WhatTheDuck 支持 SQL 代码自动完成以及语法高亮。 WhatTheDuck 将上传的数据存储为 DuckDB 内存表,继…...
关于数字信号与图像处理——基于Matlab的图像增强技术
本篇博客是在做数字信号与图像处理实验中的收获。 具体内容包括:根据给定的代码放入Matlab中分别进行两次运行测试——比较并观察运行后的实验结果与原图像的不同点——画出IJ的直方图,并比较二者差异。接下来会对每一步进行具体讲解。 题目:…...
MySQL数据库 - 锁
锁 此笔记参考黑马教程,仅学习使用,如有侵权,联系必删 文章目录 锁1. 概述1.1 介绍1.2 分类 2. 全局锁2.1 介绍2.2 语法2.3 特点(弊端) 3. 表级锁3.1 介绍3.2 表锁3.3 元数据锁(meta data lock࿰…...
免费多平台运行器,手机畅玩经典主机大作
软件介绍 飞鸟模拟器是一款面向安卓设备的免费游戏平台,支持PS2/PSP/NDS等十余种经典主机游戏运行。 该软件突破传统模拟器复杂操作模式,采用智能核心加载技术,用户只需双击主程序即可开启游戏之旅,真正实现"即下即玩"…...
计算机软考中级 知识点记忆——排序算法 冒泡排序-插入排序- 归并排序等 各种排序算法知识点整理
一、📌 分类与比较 排序算法 最优时间复杂度 平均时间复杂度 最坏时间复杂度 空间复杂度 稳定性 应用场景与特点 算法策略 冒泡排序 O(n) O(n) O(n) O(1) 稳定 简单易实现,适用于小规模数据排序。 交换排序策略 插入排序 O(n) O(n) O…...
STC32G12K128单片机GPIO模式SPI操作NorFlash并实现FatFS文件系统
STC32G12K128单片机GPIO模式SPI操作NorFlash并实现FatFS文件系统 Norflash简介NorFlash操作驱动代码文件系统测试代码 Norflash简介 NOR Flash是一种类型的非易失性存储器,它允许在不移除电源的情况下保留数据。NOR Flash的名字来源于其内部结构中使用的NOR逻辑门。…...
uniapp-x 二维码生成
支持X,二维码生成,支持微信小程序,android,ios,网页 - DCloud 插件市场 免费的单纯用爱发电的...
当HTTP遇到SQL注入:Java开发者的攻防实战手册
一、从HTTP请求到数据库查询:漏洞如何产生? 危险的参数拼接:Servlet中的经典错误 漏洞代码重现: public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {String category = request.getParameter("…...
[dp20_完全背包] 介绍 | 零钱兑换
目录 1. 完全背包 题解 背包必须装满 2.零钱兑换 题解 1. 完全背包 链接: DP42 【模板】完全背包 描述 你有一个背包,最多能容纳的体积是V。 现在有n种物品,每种物品有任意多个,第i种物品的体积为vivi ,价值为wiwi。 &a…...
精打细算 - GPU 监控
精打细算 - GPU 监控 在上一篇,咱们历经千辛万苦,终于让应用程序在 Pod 的“驾驶舱”里成功地“点火”并用上了 GPU。太棒了!但是,车开起来是一回事,知道车速多少、油耗多少、引擎水温是否正常,则是另一回事,而且同样重要,对吧? 我们的 GPU 应用跑起来了,但新的问题…...
故障诊断 | CNN-BiGRU-Attention故障诊断
效果一览 摘要 在现代工业生产中,设备的稳定运行至关重要,故障诊断作为保障设备安全、高效运行的关键技术,其准确性和及时性直接影响着生产效率与成本[[doc_refer_1]][[doc_refer_2]]。随着工业设备复杂性的不断增加,传统故障诊断方法已难以满足实际需求。深度学习技术凭借…...
单片机AIN0、AIN1引脚功能
目录 1. 模拟-数字转换器(ADC) 2. 交流电源(AC) 总结 这两部分有什么区别? 在这个电路图中,两个部分分别是模拟-数字转换器(ADC)和交流电源(AC)。以下是这…...
交换机与路由器的主要区别:深入分析其工作原理与应用场景
在现代网络架构中,交换机和路由器是两种至关重要的设备。它们在网络中扮演着不同的角色,但很多人对它们的工作原理和功能特性并不十分清楚。本文将深入分析交换机与路由器的主要区别,并探讨它们的工作原理和应用场景。 一、基本定义 1. 交换…...
uniApp小程序保存定制二维码到本地(V3)
这里的二维码组件用的 uv-ui 的二维码 可以按需引入 QRCode 二维码 | 我的资料管理-uv-ui 是全面兼容vue32、nvue、app、h5、小程序等多端的uni-app生态框架 <uv-qrcode ref"qrcode" :size"280" :value"payCodeUrl"></uv-qrcode>&l…...
手机投屏到电视方法
一、投屏软件 比如乐播投屏 二、视频软件 腾讯视频、爱奇艺 三、手机无线投屏功能 四、有线投屏 五、投屏器...
桌面应用UI开发方案
一、基于 Web 技术的跨平台方案 Electron Python/Go 特点: 技术栈:前端使用 HTML/CSS/JS,后端通过 Node.js 集成 Python/Go 模块或服务。 跨平台:支持 Windows、macOS、Linux 桌面端,适合开发桌面应用。 生态成熟&…...
FFmpeg+Nginx+VLC打造M3U8直播
一、视频直播的技术原理和架构方案 直播模型一般包括三个模块:主播方、服务器端和播放端 主播放创造视频,加美颜、水印、特效、采集后推送给直播服务器 播放端: 直播服务器端:收集主播端的视频推流,将其放大后推送给…...
山东科技大学深度学习考试回忆
目录 一、填空(五个空,十分) 二、选择题(五个,十分) 三、判断题(五个,五分) 四、论述题(四个,四十分) 五、计算题(二个ÿ…...
【Flutter动画深度解析】性能与美学的完美平衡之道
Flutter的动画系统是其UI框架中最引人注目的部分之一,它既能创造令人惊艳的视觉效果,又需要开发者对性能有深刻理解。本文将深入剖析Flutter动画的实现原理、性能优化策略以及设计美学,帮助你打造既流畅又美观的用户体验。 一、Flutter动画核…...
【嵌入式】——Linux系统远程操作和程序编译
目录 一、虚拟机配置网络设置 二、使用PuTTY登录新建的账户 1、在ubuntu下开启ssh服务 2、使用PuTTY连接 三、树莓派实现远程登录 四、树莓派使用VNC viewer登录 五、Linux使用talk聊天程序 1、使用linux自带的talk命令 2、使用c语言编写一个talk程序 一、虚拟机配置网络…...
零、HarmonyOS应用开发者基础学习总览
零、HarmonyOS应用开发者基础认证 1 整体学习内容概览 1 整体学习内容概览 通过系统化的课程学习,熟练掌握 DevEco Studio,ArkTS,ArkUI,预览器,模拟器,SDK 等 HarmonyOS 应用开发的关键概念,具…...
记录一次项目中使用pdf预览过程以及遇到问题以及如何解决
背景 项目中现有的pdf浏览解析不能正确解析展示一些pdf文件,要么内容一直在加载中展示不出来,要么展示的格式很凌乱 解决 方式一:(优点:比较无脑,缺点:不能解决遇到的一些特殊问题࿰…...
致远OA——自定义开发rest接口
文章目录 :apple: 业务流程 🍎 业务流程 代码案例: https://pan.quark.cn/s/57fa808c823f 官方文档: https://open.seeyoncloud.com/seeyonapi/781/https://open.seeyoncloud.com/v5devCTP/39/783.html 登录系统 —— 后台管理 —— 切换系…...
STL之vector基本操作
写在前面 我使用的编译器版本是 g 11.4.0 (Ubuntu 22.04 默认版本),支持C17的全部特性,支持C20的部分特性。 vector的作用 我们知道vector是动态数组(同时在堆上存储数组元素),我们在不确定数…...
dac直通线还是aoc直通线? sfp使用
"DAC直通线" 和 "AOC直通线" 都是高速互连线缆,用于数据中心、服务器、交换机等设备之间的高速互连。它们的选择主要取决于以下几个方面: 🔌 DAC(Direct Attach Cable,直连铜缆) 材质&…...
【Linux篇】探索进程间通信:如何使用匿名管道构建高效的进程池
从零开始:通过匿名管道实现进程池的基本原理 一. 进程间通信1.1 基本概念1.2 通信目的1.3 通信种类1.3.1 同步通信1.3.2 异步通信 1.4 如何通信 二. 管道2.1 什么是管道2.2 匿名管道2.2.1 pipe()2.2.2 示例代码:使用 pipe() 进行父子进程通信2.2.3 管道容…...
Mixture-of-Experts with Expert Choice Routing:专家混合模型与专家选择路由
摘要 稀疏激活的专家混合模型(MoE)允许在保持每个token或每个样本计算量不变的情况下,大幅增加参数数量。然而,糟糕的专家路由策略可能导致某些专家未被充分训练,从而使得专家在特定任务上过度或不足专业化。先前的研究通过使用top-k函数为每个token分配固定数量的专家,…...
ai学习中收藏网址【1】
https://github.com/xuwenhao/geektime-ai-course课程⾥所有的代码部分,通过 Jupyter Notebook 的形式放在了 GitHub 上 https://github.com/xuwenhao/geektime-ai-course 图片创作 https://www.midjourney.com/explore?tabtop 创建填⾊本 How to Create Midjour…...
【滑动窗口】最⼤连续 1 的个数 III(medium)
⼤连续 1 的个数 III(medium) 题⽬描述:解法(滑动窗⼝):算法思路:算法流程: C 算法代码:Java 算法代码: 题⽬链接:1004. 最⼤连续 1 的个数 III …...
ClawCloud的免费空间(github用户登录可以获得$5元/月的免费额度)
免费的空间 Welcome to ClawCloud Lets create your workspace 官网:ClawCloud | Cloud Infrastructure And Platform for Developers 区域选择新加坡 然后这个页面会变成新加坡区域,再按一次确定,就创建好了工作台。 初始界面࿰…...
sql之DML(insert、delete、truncate、update、replace))
🎯 本文专栏:MySQL深入浅出 🚀 作者主页:小度爱学习 数据库使用时,大多数情况下,开发者只会操作数据,也是就增删改查(CRUD)。 增删改查四条语句,最重要的是查…...
Spring Boot常用注解全解析:从入门到实战
🌱 Spring Boot常用注解全解析:从入门到实战 #SpringBoot核心 #注解详解 #开发技巧 #高效编程 一、核心启动与配置注解 1. SpringBootApplication 作用:标记主启动类,整合了Configuration、EnableAutoConfiguration和Component…...
Python 赋能区块链教育:打造去中心化学习平台
Python 赋能区块链教育:打造去中心化学习平台 引言 区块链技术正在重塑全球多个行业,而教育领域也不例外。传统的在线学习平台往往依赖中心化存储和管理模式,导致数据安全、用户隐私、资源共享等问题难以解决。而随着 Web 3.0 的发展,区块链在教育场景中的应用逐渐受到关…...
verilog float mult
module pipe_float_mul(input wire clk ,// 时钟信号input wire en ,// 使能信号input wire rst_n ,// 复位信号input wire round_cfg ,// 决…...
Android开发四大组件和生命周期及setFlags
文章目录 Android开发四大组件1. Activity(活动)2. Service(服务)3. BroadcastReceiver(广播接收器)4. ContentProvider(内容提供者)共同特点 Activity 生命周期详解完整的生命周期方…...
mysql的函数(第二期)
九、窗口函数(MySQL 8.0) 适用于对结果集的子集(窗口)进行计算,常用于数据分析场景。 ROW_NUMBER() 作用:为每一行生成唯一的序号。示例:按分数降序排名 SELECT n…...
MATLAB 控制系统设计与仿真 - 39
多变量系统控制器设计实例2 假如原系统对象中有位于虚轴上的极点,则不能直接应用鲁棒控制设计来设计控制器。 在这样的情况下,需引入一个新的变量p,使得 即可在对象模型中用p变量取代s变量,这样的变换称为双线性变换,…...
深入理解C++ 中的vector容器
一、引言 在C 的标准模板库(STL)中, vector 是一个极为常用且功能强大的序列容器。它就像是一个动态数组,既能享受数组随机访问元素的高效性,又能灵活地动态调整大小。在本文中,我们将深入探讨 vector …...
ESP-ADF外设子系统深度解析:esp_peripherals组件架构与核心设计(显示输出类外设之LED)
目录 ESP-ADF外设子系统深度解析:esp_peripherals组件架构与核心设计(显示输出类外设之LED)简介模块概述功能定义架构位置核心特性 LED外设分析LED外设概述LED外设功能特点常见应用场景LED外设架构图 LED外设API和数据结构公共API事件类型配置…...
[特殊字符] Kotlin与C的类型别名终极对决:typealias vs typedef,如何让代码脱胎换骨?
在 Kotlin 中,typealias 是一个非常实用的关键字,它可以为已有的类型定义一个新的名称,起到简化代码和提升可读性的作用。比如: // 定义一个复杂函数类型的别名 typealias ClickListener (View, Int) -> Unitfun setOnClickL…...
第9期:文本条件生成(CLIP + Diffusion)详解
“让我们用一句话,让模型画出一幅画。” 在前几期中我们学习了 Denoising Diffusion Probabilistic Models(DDPM)如何在无条件情况下生成图像。而在本期,我们将跨入更具挑战性但也更酷的领域 —— 文本条件图像生成(Te…...
8 编程笔记全攻略:Markdown 语法精讲、Typora 编辑器全指南(含安装激活、基础配置、快捷键详解、使用技巧)
1 妙笔在手,编程无忧! 1.1 编程为啥要做笔记?这答案绝了! 嘿,各位键盘魔法师!学编程不记笔记,就像吃火锅不配冰可乐 —— 爽到一半直接噎住!你以为自己脑子是顶配 SSD,结…...