一、dart基本语法
Dart介绍:
Dart是由谷歌开发的计算机编程语言,它可以被用于web、服务器、移动应用 和物联网等领域的开发。
Dart诞生于2011年,号称要取代JavaScript。
官网:https://dart.cn/
Dart环境搭建:
1、本地开发Dart程序,首先需要安装Dart Sdk
官方文档:https://dart.cn/get-dart
windows(推荐):http://www.gekorm.com/dart-windows/
mac:如果mac电脑没有安装brew这个工具首先第一步需要安装它:https://brew.sh/
brew tap dart-lang/dart brew install dart
Dart 开发工具:
Dart的开发工具有很多: IntelliJ IDEA 、 WebStorm、 Atom、Vscode等 以Vscode中配置Dart为例: vscode插件安装 dart、 code runner、 Code Runner 可以运行我们的文件
1.入口方式两种定义方式
main(){
var name = '信息技术'; var price= 23.55; print(name); //信息技术 print(price);//23.55 } //第二种方式:没有返回值 void main(){
var name = '信息技术'; var price= 23.55; print(name); //信息技术 print(price);//23.55 }
1.1变量声明
Dart 变量:var
dart是一个强大的脚本类语言,可以不预先定义变量类型 ,自动会类型推倒
dart中定义变量可以通过var关键字可以通过类型来申明变量
如:
main(){
var str='this is var'; String str='this is var'; //报错 int str=123; //报错 //注意: var 后就不要写类型 , 写了类型 不要var 两者都写 var a int = 5; 报错 var str1='this is a str'; str1='你好 str'; print(str1); int myNum=1234; myNum=4567; print(myNum); }
final name = 'Bob'; // Without a type annotation final String nickname = 'Bobby'; const bar = ; // Unit of pressure (dynes/cm2) const double atm = 1.01325 * bar; // Standard atmosphere
void main(){
//const常量 // const PI=3.14159; // PI=123.1243; //错误的写法 常量不可以修改 // print(PI); // final 常量 // final PI=3.14159; // PI=.; //错误写法 // print(PI); final a=new DateTime.now(); print(a); //2019-05-10 15:59:02. //const a=new DateTime.now(); //报错了 } //区别:final 可以开始不赋值 只能赋一次 ; 而final不仅有const的编译时常量的特性, //最重要的它是运行时常量,并且final是惰性初始化,即在运行时第一次使用前才初始化
void main(){
//字符串 // String str='你好dart'; // print(str); //数字类型 // int myNum=12354; // print(myNum); //dart里面有类型校验 // var str=''; // str=1234; // print(str); String str=""; print(str); int myNum=; print(myNum); }
2、dart 基本数据类型
2.1 常用数据类型
Numbers(数值): int, double
Strings(字符串):String
Booleans(布尔):bool
List(数组): 在Dart中,数组是列表对象,所以大多数人只是称它们为列表
Maps(字典): 通常来说,Map 是一个键值对相关的对象。 键和值可以是任何类型的对象。每个 键 只出现一次, 而一个值则可以出现多次
2.2、项目中用不到的数据类型 (用不到
)
<kbd>Runes </kbd> Rune是UTF-32编码的字符串。它可以通过文字转换成符号表情或者代表特定的文字。
main() {
var clapping = '\u{1f44f}'; print(clapping); print(clapping.codeUnits); print(clapping.runes.toList()); Runes input = new Runes( '\u2665 \u{1f605} \u{1f60e} \u{1f47b} \u{1f596} \u{1f44d}'); print(new String.fromCharCodes(input)); }
Symbols
2.2、数据类型示例
/* Dart数据类型:字符串类型 */ void main(){
//1、字符串定义的几种方式 // var str1='this is str1'; // var str2="this is str2"; // print(str1); // print(str2); // String str1='this is str1'; // String str2="this is str2"; // print(str1); // print(str2); // String str1='''this is str1 // this is str1 // this is str1 // '''; // print(str1); // String str1=""" // this is str1 // this is str1 // this is str1 // """; // print(str1); //2、字符串的拼接 String str1='你好'; String str2='Dart'; // print("$str1 $str2"); print(str1 + str2); print(str1 +" "+ str2); }
/* Dart数据类型:数值类型 int double */ void main(){
//1、int 必须是整型 int a=123; a=45; print(a); //2、double 既可以是整型 也可是浮点型 double b=23.5; b=24; print(b); //3、运算符 // + - * / % var c=a+b; print(c); }
/* Dart数据类型:布尔类型 bool 值true/false */ void main(){
//1、bool // bool flag1=true; // print(flag1); // bool flag2=false; // print(flag2); //2、条件判断语句 var flag=true; if(flag){
print('真'); }else{
print('假'); } var a=123; var b='123'; if(a==b){
print('a=b'); }else{
print('a!=b'); } }
/* Dart数据类型: List(数组/集合) */ void main() {
//1、第一种定义List的方式 var list1 = ["codeSE", 100, 'A', true]; print(list1); //[codeSE, 100, A, true] print(list1.length); //4 print(list1[0]); //codeSE //2、第二种定义List的方式 指定类型 var list2 = <String>['codeSE', 'ank']; var list3 = <int>[12, 34, 456, 9000]; //3、第三种定义List的方式 增加数据 ,通过[]创建的集合它的容量可以变化 var list4 = []; print(list4.length); list4.add("codeSE"); list4.add(123); list4.add(false); print(list4); //4、第四种定义List的方式 // var list6=new List(); //在新版本的dart(已被弃用) var list6 = List.filled(2, ""); //创建一个固定长度的集合 print(list6); //[,] print(list6[0]); // list6[0] = "codeSE"; //修改集合的内容 list6[1] = "李四"; print(list6); //[codeSE, 李四] // list6.add("王五"); //错误写法 通过List.filled创建的集合长度是固定 没法增加数据 //通过List.filled创建的集合长度是固定 var list7 = List.filled(2, ""); print(list7.length); //2 // list7.length = 0; //修改集合的长度 报错 var list8 = <String>["codeSE", "李四"]; print(list8.length); //2 list8.length = 0; //可以改变的 print(list8); //[] var list9 = List<String>.filled(2, ""); list9[0] = "string"; // list8[0]=222; //报错 print(list9); //[string, ] } }
/* Dart数据类型: Maps(字典) */ /* Dart数据类型: Maps(字典) */ void main() {
//第一种定义 Maps的方式 var person = {
"name": "codeSE", "age": 20, "work": ["程序员", "送外卖", "擦地板"] }; print(person); print(person["name"]); print(person["age"]); print(person["work"]); //第二种定义 Maps的方式 var p = new Map(); p["name"] = "codeSE"; p["age"] = 22; p["work"] = ["程序员", "送外卖", "擦地板"]; print(p); print(p["age"]); }
/* Dart判断数据类型 is 关键词来判断类型 */ void main() {
var str = '1234'; if (str is String) {
print('是string类型'); } else if (str is int) {
print('是int类型'); } else {
print('其他类型'); } var num = 123; if (num is String) {
print('是string类型'); } else if (num is int) {
print('是int类型'); } else {
print('其他类型'); } }
3、运算符 条件判断 类型转换
运算名 | 操作符 |
---|---|
算术运算符 | + - * / ~/ (取整) %(取余) |
关系运算符 | == != > < >= <= |
逻辑运算符 | ! && |
赋值运算符 | = ??= += -= *= /= %= ~/= |
条件表达式 | if else switch case |
三目运算符 | ??运算符: |
备注:??=
如果前面的值为空则赋值给他 var str= "";str??=="codeSE";
void main(List<String> args) {
int b = 0; b ??= 23; print(b); //2、条件运算符 var sex = "女1"; switch (sex) {
case "男": print('性别是男'); break; case "女": print('性别是女'); break; default: print('传入参数错误'); break; } bool flag = false; String i = flag ? '我是true' : '我是false'; print(i); //我是false //3、 ??运算符 var a; var b = a ?? 10; print(b); //10 var c = 22; var d = c ?? 10; print(d); //22
void main() {
// 1、Number与String类型之间的转换 // Number类型转换成String类型 toString() // String类型转成Number类型 int.parse() String str1 = '123'; var myNum1 = int.parse(str1); print(myNum1 is int); //true String str2 = '123.1'; var myNum2 = double.parse(str2); print(myNum2 is double); //true print(myNum2); //123.1 String price1 = '12'; var myNum3 = double.parse(price1); print(myNum3); //12.0 print(myNum3 is double); //true // 报错 String price2 = ''; // var myNum4 = double.parse(price2); // print(myNum4); // print(myNum4 is double); // try ... catch String price3 = ''; try {
var myNum5 = double.parse(price3); print(myNum5); } catch (err) {
print(0); //0 } var myNum6 = 12; var str3 = myNum6.toString(); print(str3 is String); //true // 2、其他类型转换成Booleans类型 // isEmpty:判断字符串是否为空 var str4 = ''; if (str4.isEmpty) {
print('str4空'); } else {
print('str4不为空'); } var myNum7 = 123; if (myNum7 == 0) {
print('0'); } else {
print('非0'); } var myNum8; if (myNum8 == 0) {
print('0'); } else {
print('非0'); } var myNum9; if (myNum9 == null) {
print('空'); } else {
print('非空'); } var myNum10 = 0 / 0; print(myNum10); //NaN if (myNum10.isNaN) {
print('NaN'); } }
4、循环语句
4.1 for循环
void main(List<String> args) {
/* for (int i = 1; i<=100; i++) { print(i); } //第一步,声明变量int i = 1; //第二步,判断i <=100 //第三步,print(i); //第四步,i++ //第五步 从第二步再来,直到判断为false */ var sum = 0, total = 1; for (var i = 1; i <= 10; i++) {
sum += i; total *= i; } print(sum); print(total); List list = [ {
"cate": '《阿里》', "news": [ {
"title": "支付宝"}, {
"title": "淘宝"}, {
"title": "蚂蚁森林"} ] }, {
"cate": '《腾讯》', "news": [ {
"title": "微信"}, {
"title": ""}, {
"title": "王者荣耀"} ] } ]; for (var i = 0; i < list.length; i++) {
print(list[i]["cate"]); print('-------------'); for (var j = 0; j < list[i]["news"].length; j++) {
print(list[i]["news"][j]["title"]); } } }
4.2 do while ,break
语法格式: while(表达式/循环条件){ } do{ 语句/循环体 }while(表达式/循环条件); 注意: 1、最后的分号不要忘记 2、循环条件中使用的变量需要经过初始化 3、循环体中,应有结束循环的条件,否则会造成死循环。
void main(List<String> args) {
//求1+2+3+4 ...+100的和 int i = 1; var sum = 0; while (i <= 100) {
sum += i; i++; } print(sum); //5050 int j = 1; var total = 0; do {
total += j; j++; } while (j <= 100); print(total); //5050 //while 和 do while的区别 第一次循环条件不成立的情况下 int k = 10; while (k < 2) {
print('执行代码'); } var m = 10; do {
print('执行代码'); } while (m < 2); }
break语句功能: 1、在switch语句中使流程跳出switch结构。 2、在循环语句中使流程跳出当前循环,遇到break 循环终止,后面代码也不会执行 强调: 1、如果在循环中已经执行了break语句,就不会执行循环体中位于break后的语句。 2、在多层循环中,一个break语句只能向外跳出一层 break可以用在switch case中 也可以用在 for 循环和 while循环中 continue语句的功能: 【注】只能在循环语句中使用,使本次循环结束,即跳过循环体重下面尚未执行的语句,接着进行下次的是否执行循环的判断。 continue可以用在for循环以及 while循环中,但是不建议用在while循环中,不小心容易死循环
main() {
//1、如果i等于4的话跳过 for (var i = 1; i <= 10; i++) {
if (i == 4) {
continue; /*跳过当前循环体 然后循环还会继续执行*/ } print(i); // 1 2 3 5 6 7 8 9 10 } //2、如果 i等于4的话跳出循环 for (var i = 1; i <= 10; i++) {
if (i == 4) {
break; /*跳出循环体*/ } print(i); // 1 2 3 } //3、break语句只能向外跳出一层 for (var i = 0; i < 5; i++) {
print('外层---$i'); for (var j = 0; j < 3; j++) {
if (j == 1) {
break; } print('里层$j'); } } //4、while循环 break跳出循环 var i = 1; while (i <= 10) {
if (i == 4) {
break; } print(i); i++; } // 1 2 3 var sex = "男"; switch (sex) {
case "男": print('男'); break; case "女": print('女'); break; default: print('默认'); } // 男 }
5、数组常用属性及方法
常用属性:
属性 | 描述 |
---|---|
length | 长度 |
reversed | 翻转 |
isEmpty | 是否为空 |
isNotEmpty | 是否不为空 |
void main(List<String> args) {
/* *常用属性: length 长度 reversed 翻转 isEmpty 是否为空 isNotEmpty 是否不为空 */ // var list1 = ['CodeSE', 100, '45', true]; List list1 = ['CodeSE', 100, '45', true]; print(list1.length); //4 print(list1.reversed); //(true, 45, 100, CodeSE) print(list1.isEmpty); //false print(list1.isNotEmpty); //true
常用方法:
方法 | 描述 |
---|---|
add | 增加 |
addAll | 拼接数组 |
indexOf | 查找 传入具体值 |
remove | 删除 传入具体值 |
removeAt | 删除 传入索引值 |
fillRange | 修改 |
insert(index,value); | 指定位置插入 |
insertAll(index,list) | 指定位置插入List |
toList() | 其他类型转换成List |
join() | List转换成字符串 |
split() | 字符串转化成List |
forEach | |
map | |
where | |
any | |
every |
/* *常用方法: add 增加 addAll 拼接数组 indexOf 查找 传入具体值 remove 删除 传入具体值 removeAt 删除 传入索引值 fillRange 修改 insert(index,value); 指定位置插入 insertAll(index,list) 指定位置插入List toList() 其他类型转换成List join() List转换成字符串 split() 字符串转化成List forEach map where any every */ var list2 = ['Java', 'JavaScript', "Python", "中国", '数据', '应用']; list2.add('CodeSE'); list2.addAll(['100', 'Blue']); print(list2); var newList2 = list2.reversed.toList(); //toList(),转化为数组 print(newList2); //[Blue, 100, CodeSE, 应用, 数据, 中国, Python, JavaScript, Java] print(list2.indexOf('Python')); //2 list2.remove('Java'); //按元素名称移除元素 list2.removeAt(2); //按元素所在的位置移除 print(list2); //[JavaScript, Python, 数据, 应用, CodeSE, 100, Blue] print( '================================================================================='); var list3 = ['香蕉', '苹果', '西瓜', '菠萝', '哈密瓜', '柚子']; list3.fillRange(1, 3, '葡萄'); //修改替换操作:start end 值 print(list3); //[香蕉, 葡萄, 葡萄, 菠萝, 哈密瓜, 柚子] list3.insert(3, '白菜'); //插入 一个 print(list3); //[香蕉, 葡萄, 葡萄, 白菜, 菠萝, 哈密瓜, 柚子] list3.insertAll(4, ['AA', 'MM']); //插入多个 print(list3); //[香蕉, 葡萄, 葡萄, 白菜, AA, MM, 菠萝, 哈密瓜, 柚子] print( '================================================================================='); var list4 = ['菠萝', '哈密瓜', '柚子']; var str = list4.join('--'); print(str); //菠萝--哈密瓜--柚子 print(str is String); //true var text = '菠萝--哈密瓜--柚子'; var arr = text.split('--'); print(arr); //[菠萝, 哈密瓜, 柚子] }
6、Set集合
set是无序的不可重复的集合,不能通过索引获取。一般功能是:数组去重
。
void main(List<String> args) {
var s1 = new Set(); s1.add('菠萝'); s1.add('苹果'); s1.add('苹果'); print(s1); //{菠萝, 苹果} print(s1.toList()); //[菠萝, 苹果] List l = ['香蕉', '葡萄', '葡萄', '白菜', '菠萝', '哈密瓜', '柚子']; var s = new Set(); s.addAll(l); print(s); //{香蕉, 葡萄, 白菜, 菠萝, 哈密瓜, 柚子} print(s.toList()); //[香蕉, 葡萄, 白菜, 菠萝, 哈密瓜, 柚子] }
7、map
映射(Maps)是无序的键值对
7.1 常用属性:
属性 | 描述 |
---|---|
keys | 获取所有的key值 |
values | 获取所有的value值 |
isEmpty | 是否为空 |
isNotEmpty | 是否不为空 |
7.2 常用方法:
方法 | 描述 |
---|---|
remove(key) | 删除指定key的数据 |
addAll({…}) | 合并映射 给映射内增加属性 |
containsKey | 查看映射内的属性 返回true/false |
containsValue | 查看映射内的值 返回true/false |
forEach | |
map | |
where | |
any | |
every |
import 'dart:collection'; void main(List<String> args) {
//声明方式 Map student = {
'name': 'codeSE', 'age': 20, 'isBoby': true, }; print(student); //{name: codeSE, age: 20, isBoby: true} var teacher = new Map(); teacher['name'] = 'Geen'; teacher['work'] = 'PC'; print(teacher); //{name: Geen, work: PC} //常用方法 print(student.keys.toList()); //[name, age, isBoby] print(student.values.toList()); //[name, age, isBoby] print(student.isEmpty); //false print(student.isNotEmpty); //true student.addAll({
"grilFrinend": "ZXY", "hoby": ['唱歌', '跳舞'], "love me": true }); student.remove("age"); print( student); //{name: codeSE, isBoby: true, grilFrinend: ZXY, hoby: [唱歌, 跳舞], love me: true} print(student.containsValue("codeSE")); //true print(student.containsKey('name')); //true teacher.forEach((key, value) {
print(key + "---" + value); //name---Geen work---PC }); //循环三种方式 List color = ['red', 'blue', 'yellow']; for (var i = 0; i < color.length; i++) {
print(color[i]); } for (var item in color) {
print(item); } color.forEach((item) {
print("${
item}"); }); var nums = [45, 12, 33]; var newNums = []; // List nums = [45, 12, 33]; // List newNums = new List(); for (var i = 0; i < nums.length; i++) {
newNums.add(nums[i] * 2); } print(newNums); //[90, 24, 66] List list1 = [1, 3, 4]; var newList1 = list1.map((value) {
return value * 2; }); print(newList1.toList()); //[2, 6, 8] List list2 = [1, 3, 4, 5, 7, 8, 9]; var newList2 = list2.where((value) {
//任何满足条件 return value > 5; }); print(newList2.toList()); //[7, 8, 9] List list3 = [1, 3, 4, 5, 7, 8, 9]; var s = list3.any((value) {
//只要有满足条件的就返回true return value > 5; }); print(s); //true List list4 = [1, 3, 4, 5, 7, 8, 9]; var f = list4.every((value) {
//每一个都满足条件返回true 否则返回false return value > 5; }); print(f); //false }
8、函数(方法)
内置方法/函数: print(); 自定义方法: 自定义方法的基本格式 返回类型 方法名称(参数1,参数2,...){ 方法体 return 返回值; }
8.1 函数,方法定义、调用
void printInfo() {
print("自定义的函数/方法"); } int getPrice() {
int price = 199; return price; } String getName() {
return 'Jim Green'; } List getColor() {
return ['red', 'blue', 'yellow']; } void main(List<String> args) {
print('这是系统内置的函数/方法'); printInfo(); var n = getPrice(); //自定义的函数/方法 print(n); //199 print(getName()); //Jim Green print(getColor()); //[red, blue, yellow] //函数作用域 void m() {
n() {
print('内部'); print(getName()); } n(); } // n();//错误 m(); }
8.2 可选参数、默认传参,函数参数
void main(List<String> args) {
int sum(int n) {
var m = 0; for (var i = 0; i <= n; i++) {
m += i; } return m; } var s = sum(100); print("总数为$s"); //5050 String userInfo(String name, int age, double kg) {
return '姓名:${
name},年龄:$age,体重:$kg'; } print(userInfo("小猪", 24, 52.2)); //姓名:小猪,年龄:24,体重:52.2 //默认参数用{}或者[]来声明,注意放在后面 String personInfo(String name, [double kg = 55.0, int age = 22]) {
return '姓名:${
name},年龄:$age,体重:$kg'; } print(personInfo("大明", 58)); //姓名:大明,年龄:22,体重:58.0 //函数当做参数 fun1() {
print('lxc is a boby'); } fun2(fun) {
fun(); } fun2(fun1); //lxc is a boby }
8.3 箭头函数、函数相互调用
void main(List<String> args) {
List list1 = ['语文', '数学', '体育', '政治']; list1.forEach((item) {
print(item); }); //箭头函数=> list1.forEach((item) => print(item)); //注意:箭头函数内只能写一条语句,且语句后面无(;) //例1:修改数组的值,大于5的*2 List list2 = [2, 5, 6, 10, 600]; var newList2 = list2.map((item) {
if (item > 5) {
return item * 2; } return item; }); //箭头函数=> var newList3 = list2.map((e) => e > 5 ? e * 2 : e ,); print(newList2.toList()); //[2, 5, 12, 20, 1200] print(newList3.toList()); //[2, 5, 12, 20, 1200] //例2:定义一个方法判断一个数大于10并且是否偶数 bool isValidity(int n) {
if (n > 10 && n % 2 == 0) {
return true; } return false; } //例3:打印上述满足条件的所有数 printNums(int n) {
for (var i = 0; i < n; i++) {
if (isValidity(i)) {
print('满足条件有:$i'); } } } //调用 printNums(50); }
8.4 匿名方法、自执行方法(js类似的立即执行函数)、递归方法
void main(List<String> args) {
//匿名方法 var getPrice = () {
print('普通函数匿名方法'); }; var setAge = (int n) => print('箭头函数匿名方法 $n'); getPrice(); setAge(20); //自执行方法 (int a) {
print(a / 2); //15.0 print('自执行方法'); }(30); //方法递归(注意:使用递归必须有终止条件,否则......) var sum = 1; fn(int n) {
sum *= n; if (n == 1) {
return; } else {
fn(n - 1); } } fn(10); print(sum); // }
8.4 闭包
闭包:
1、全局变量特点: 全局变量常驻内存、全局变量污染全局 2、局部变量的特点: 不常驻内存会被垃圾机制回收、不会污染全局 想实现的功能:(闭包可以解决) 1.常驻内存 2.不污染全局 闭包: 函数嵌套函数, 内部函数会调用外部函数的变量或参数, 变量或参数不会被系统回收(不会释放内存) 闭包的写法: 函数嵌套函数,并return 里面的函数,这样就形成了闭包
/全局变量 */ var n = 10; void main() {
print(n); //10 f1() {
n++; print(n); } f1(); //11 f1(); //12 f1(); //13 /局部变量 */ printNum() {
var m = 100; m++; print(m); } printNum(); //101 printNum(); //101 printNum(); //101 //闭包 fn() {
var a = 123; //不污染全局 常驻内存 return () {
a++; print(a); }; } var k = fn(); k(); //124 k(); //125 k(); //126 }
二、dart面向对象
1、面向对象的基本思想
面向对象编程(OOP)的三个基本特征是:封装、继承、多态
封装:封装是对象和类概念的主要特性。封装,把客观事物封装成抽象的类,并且把自己的部分属性和方法提供给其他对象调用, 而一部分属性和方法则隐藏。 继承:面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。 多态:允许将子类类型的指针赋值给父类类型的指针, 同一个函数调用会有不同的执行效果 。
Dart所有的东西都是对象,所有的对象都继承自Object类。
Dart是一门使用类和单继承的面向对象语言,所有的对象都是类的实例,并且所有的类都是Object的子类
一个类通常由属性和方法组成。
//基本用法: class Person {
String name = 'codeSE'; int age = 80; void getPerson() {
// print("$name---$age"); print("${
this.name}---${
this.age}"); } void setPerson(int n) {
this.age = n; } } void main(List<String> args) {
var p = new Person(); print(p.name); //codeSE p.getPerson(); //codeSE---100 p.setPerson(100); p.getPerson(); //codeSE---100 }
2、构造函数
class Person {
//dart中需要初始化不可为null的实例字段,如果不初始化的话需要在属性前面加上late late String name; late int age; //默认构造函数,构造函数与类名一致 Person(String name, int age) {
this.name = name; this.age = age; print('我是构造函数,实例化自动执行一次'); } void getPerson() {
print("${
this.name}---${
this.age}"); } void setPerson(int n) {
this.age = n; } } class Student {
//dart中需要初始化不可为null的实例字段,如果不初始化的话需要在属性前面加上late late String name; late int age; //构造函数简写 Student(this.name, this.age) {
} void getPerson() {
print("${
this.name}---${
this.age}"); } } void main(List<String> args) {
Person p1 = new Person("codeSE", 123); //我是构造函数,实例化自动执行一次 print(p1.name); //codeSE p1.getPerson(); //codeSE---123 // Student s = new Student("iphoneSE", 66); s.getPerson(); //iphoneSE---66 }
2.1 自定义类的命名构造函数
/* dart里面构造函数可以写多个 */ class Person {
late String name; late int age; //默认构造函数的简写 Person(this.name, this.age); Person.now() {
print('我是命名构造函数'); } Person.setInfo(String name, int age) {
this.name = name; this.age = age; } void printInfo() {
print("${
this.name}----${
this.age}"); } } void main() {
var d = new DateTime.now(); //实例化DateTime调用它的命名构造函数 print(d); // Person p = new Person('张三', 20); //默认实例化类的时候调用的是 默认构造函数 //Person p = new Person.now(); //命名构造函数 Person p = new Person.setInfo('李四', 30); p.printInfo(); }
3、类抽离到一个文件
lib文件夹下创建.dart类的文件:
//student.dart // 注意:最新版本的dart中需要初始化不可为null的实例字段,如果不初始化的话需要在属性前面加上late class Student {
late String name; late int age; //默认构造函数的简写 Student(this.name, this.age); Student.now() {
print('我是命名构造函数'); } Student.setInfo(String name, int age) {
this.name = name; this.age = age; } void printInfo() {
print("${
this.name}----${
this.age}"); } }
index.dart 引入
import 'lib/Student.dart'; void main() {
Student s = new Student.setInfo('Dart yes', 30); s.printInfo(); }
4、私有属性
Dart和其他面向对象语言不一样,Data中没有 public private protected这些访问修饰符合
但是我们可以使用_把一个属性或者方法定义成私有。
class Animal{
late String _name; //私有属性 late int age; //默认构造函数的简写 Animal(this._name,this.age); void printInfo(){
print("${
this._name}----${
this.age}"); } String getName(){
return this._name;//间接获取私有属性 } void _run(){
print('这是一个私有方法'); } execRun(){
this._run(); //间接获取私有方法(类里面方法的相互调用) } } /// /* Dart和其他面向对象语言不一样,Data中没有 public private protected这些访问修饰符合 但是我们可以使用_把一个属性或者方法定义成私有。 */ import 'lib/Animal.dart'; void main(){
Animal a=new Animal('小狗', 3); print(a.getName()); a.execRun(); //间接的调用私有方法 }
5、get和set
class Rect1 {
int height; double width; Rect1(this.height, this.width); get area1 {
return this.height * this.width; } } class Rect2 {
late int height; late int width; Rect2(this.height, this.width); get area2 {
return this.height * this.width; } set areaHeight(value) {
this.height = value; } } void main() {
Rect1 r1 = new Rect1(10, 2.5); print("面积1:${
r1.area1}"); //注意调用直接通过访问属性的方式访问area Rect2 r2 = new Rect2(10, 4); // print("面积2:${r2.area2()}"); r2.areaHeight = 6; print(r2.area2); //24 }
6、初始化列表
// Dart中我们也可以在构造函数体运行之前初始化实例变量 class Rect{
int height; int width; Rect():height=2,width=10{
//此处初始化数据,不必传入参数 print("${
this.height}---${
this.width}"); } getArea(){
return this.height*this.width; } } void main(){
Rect r=new Rect(); print(r.getArea()); }
7、类的静态成员、静态方法
Dart中的静态成员:
1、使用static 关键字来实现类级别的变量和函数
2、静态方法不能访问非静态成员,非静态方法可以访问静态成员
3、静态方法和静态成员不用实例化便可以直接使用
// class Person {
// static String name = 'codeSE'; // static void show() {
// print(name); // } // } // main(){
// print(Person.name); // Person.show(); // } class Person {
static String name = 'codeSE'; int age = 123; static void show() {
print(name); } void printInfo() {
/*非静态方法可以访问静态成员以及非静态成员*/ // print(name); //访问静态属性 // print(this.age); //访问非静态属性 show(); //调用静态方法 } static void printUserInfo() {
//静态方法 print(name); //静态属性 show(); //静态方法 //print(this.age); //静态方法没法访问非静态的属性 // this.printInfo(); //静态方法没法访问非静态的方法 // printInfo(); } } main() {
// print(Person.name); // Person.show(); // Person p=new Person(); // p.printInfo(); Person.printUserInfo(); }
8、类中的对象操作
/* Dart中的对象操作符: ? 条件运算符 (了解) https://dart.dev/tools/diagnostic-messages#invalid_null_aware_operator as 类型转换 is 类型判断 .. 级联操作 (连缀) (记住) */ class Person {
String name; num age; Person(this.name, this.age); void printInfo() {
print("${
this.name}---${
this.age}"); } } main() {
// Person p; // p?.printInfo(); //已被最新的dart废弃 了解 // Person p=new Person('codeSE', 25); // p?.printInfo(); //已被最新的dart废弃 了解 Person p1 = new Person('codeSE', 25); if (p1 is Person) {
p1.name = "四大皆空"; } p1.printInfo(); //四大皆空---25 print(p1 is Object); //true var p2; p2 = ''; p2 = new Person('codeSE1', 25); p2.printInfo(); //codeSE1---25 (p2 as Person).printInfo(); //codeSE1---25 Person p3 = new Person('codeSE1', 25); p3.printInfo(); //codeSE1---25 p3.name = 'codeSE222'; p3.age = 40; p3.printInfo(); //codeSE222---40 Person p4 = new Person('codeSE1', 25); p4.printInfo(); //codeSE1---25 //【连级操作】 p4 ..name = "四大皆空" ..age = 30 ..printInfo(); //四大皆空---30 }
9、类的继承
Dart中的类的继承:
1、子类使用extends关键词来继承父类
2、子类会继承父类里面可见的属性和方法 但是不会继承构造函数
3、子类能复写父类的方法 getter和setter
简单继承:
class Person {
String name = 'codeSE'; num age = 25; void printInfo() {
print("${
this.name}---${
this.age}"); } } class Man extends Person {
} main() {
Man m = new Man(); print(m.name); m.printInfo();//调用父类的属性和方法 }
super
class Person {
late String name; late int age; Person(this.name, this.age); void getPerson() {
print("${
this.name}---${
this.age}"); } } class Student extends Person {
late String sex; //执行子类的构造函数时,将属性传给父类 Student(String name, int age, this.sex) : super(name, age); void run() {
print('一个$sex人在奔跑'); } } void main(List<String> args) {
Student s = new Student('codeSe', 22, '男'); // print(s.sex); s.getPerson(); //codeSe---22 s.run(); //一个男人在奔跑 }
实例化自命名构造函数:
class Person {
late String name; late int age; Person.xxx(this.name, this.age); void getPerson() {
print("${
this.name}---${
this.age}"); } } class Student extends Person {
late String sex; Student(String name, int age, this.sex) : super.xxx(name, age); void run() {
print('一个$sex人在奔跑'); } } void main(List<String> args) {
Student s = new Student('codeSe', 22, '男'); // print(s.sex); s.getPerson(); //codeSe---22 s.run(); //一个男人在奔跑 }
类的覆写写:
class Person {
late String name; late int age; Person(this.name, this.age); void getPerson() {
print("${
this.name}---${
this.age}"); } void work() {
print('$name在北京工作'); } } class Student extends Person {
late String sex; Student(String name, int age, this.sex) : super(name, age); void printInfo() {
super.getPerson(); //调用父类的方法 } //复写父类的方法 //可以写也可以不写 建议在覆写父类方法的时候加上 @override void work() {
print('$name,$sex,在上海工作'); } } void main(List<String> args) {
Student s = new Student('codeSe', 22, '男'); s.getPerson(); s.work(); }
10、抽象类和接口
Dart中抽象类: Dart抽象类主要用于定义标准,子类可以继承抽象类,也可以实现抽象类接口。
1、抽象类通过abstract 关键字来定义 2、Dart中的抽象方法不能用abstract声明,Dart中没有方法体的方法我们称为抽象方法。 3、如果子类继承抽象类必须得实现里面的抽象方法 4、如果把抽象类当做接口实现的话必须得实现抽象类里面定义的所有属性和方法。 5、抽象类不能被实例化,只有继承它的子类可以
extends抽象类 和 implements的区别:
1、如果要复用抽象类里面的方法,并且要用抽象方法约束自类的话我们就用extends继承抽象类 2、如果只是把抽象类当做标准的话我们就用implements实现抽象类
抽象类:
abstract class Person {
personInfo(); //抽象方法,定义规范,具体在子类实现(必须) job(); //抽象方法,定义规范,具体在子类实现(必须) void price() {
print(' 都是人'); } } class Teacher extends Person {
job() {
// TODO: implement job print(" 老师职业是教师"); } personInfo() {
// TODO: implement personInfo print(" 男,45"); } } class Student extends Person {
job() {
// TODO: implement job print(" 学生职业是学生"); } personInfo() {
// TODO: implement personInfo print(" 女,18"); } } void main(List<String> args) {
Teacher t = new Teacher(); Student s = new Student(); t.job(); t.personInfo(); t.price(); s.job(); s.personInfo(); s.price(); }
11、多态
1、子类的实例赋值给父类的引用。 2、多态就是父类定义一个方法不去实现,让继承他的子类去实现,每个子类有不同的表现。
/* Datr中的多态: 允许将子类类型的指针赋值给父类类型的指针, 同一个函数调用会有不同的执行效果 。 子类的实例赋值给父类的引用。 多态就是父类定义一个方法不去实现,让继承他的子类去实现,每个子类有不同的表现。 */ abstract class Animal {
eat(); //抽象方法 } class Dog extends Animal {
eat() {
print('小狗在吃骨头'); } run() {
print('run'); } } class Cat extends Animal {
eat() {
print('小猫在吃老鼠'); } run() {
print('run'); } } main() {
// Dog d=new Dog(); // d.eat(); // d.run(); // Cat c=new Cat(); // c.eat(); Animal d = new Dog(); //父类的引用指向子类 d.eat(); Animal c = new Cat(); //父类的引用指向子类 c.eat(); }
12、 接口
和Java一样,dart也有接口,但是和Java还是有区别的。
1、首先,dart的接口没有interface关键字定义接口,而是普通类或抽象类都可以作为接口被实现。
2、 同样使用implements关键字进行实现。
3、但是dart的接口有点奇怪,如果实现的类是普通类,会将普通类和抽象中的属性的方法全部需要覆写一遍。
4、 而因为抽象类可以定义抽象方法,普通类不可以,所以一般如果要实现像Java接口那样的方式,一般会使用抽象类。
5、建议使用抽象类定义接口。
/* 定义一个DB库 支持 mysql mssql mongodb mysql mssql mongodb三个类里面都有同样的方法 */ abstract class Db {
//当做接口 接口:就是约定 、规范 late String url; //数据库的链接地址 add(String data); save(); delete(); } class Mysql implements Db {
String url; Mysql(this.url); add(data) {
// TODO: implement add print('这是mysql的add方法' + data); } delete() {
// TODO: implement delete return null; } save() {
// TODO: implement save return null; } remove() {
} } class MsSql implements Db {
late String url; add(String data) {
print('这是mssql的add方法' + data); } delete() {
// TODO: implement delete print('删除成功'); return null; } save() {
// TODO: implement save return null; } } main() {
Mysql mysql = new Mysql('xxxxxx'); mysql.add('');//这是mysql的add方法heollo MsSql mssql = new MsSql(); mssql.delete();//删除成功 }
implements实现多个接口:
/* Dart中一个类实现多个接口: */ abstract class A{
late String name; printA(); } abstract class B{
printB(); } class C implements A,B{
late String name; printA() {
print('printA'); } printB() {
// TODO: implement printB return null; } } void main(){
C c=new C(); c.printA(); }
13、混入
mixins的中文意思是混入,就是在类中混入其他功能。
在Dart中可以使用mixins实现类似多继承的功能
因为mixins使用的条件,随着Dart版本一直在变,这里讲的是Dart2.x中使用mixins的条件:
1、作为mixins的类只能继承自Object,不能继承其他类
2、作为mixins的类不能有构造函数
3、一个类可以mixins多个mixins类
4、mixins绝不是继承,也不是接口,而是一种全新的特性
class A {
String info="this is A"; void printA(){
print("A"); } } class B {
void printB(){
print("B"); } } class C with A,B{
} void main(){
var c=new C(); c.printA(); c.printB(); print(c.info); }
class Person {
String name; num age; Person(this.name, this.age); printInfo() {
print('${
this.name}----${
this.age}'); } void run() {
print("Person Run"); } } class A {
String info = "this is A"; void printA() {
print("A"); } void run() {
print("A Run"); } } class B {
void printB() {
print("B"); } void run() {
print("B Run"); } } class C extends Person with B, A {
C(String name, num age) : super(name, age); } void main() {
var c = new C('张三', 20); c.printInfo(); //张三----20 c.printB(); //B print(c.info); //this is A c.run(); //A Run }
/* mixins的实例类型是什么? 很简单,mixins的类型就是其超类的子类型。 */ class A {
String info="this is A"; void printA(){
print("A"); } } class B {
void printB(){
print("B"); } } class C with A,B{
} void main(){
var c=new C(); print(c is C); //true print(c is A); //true print(c is B); //true var a = new A(); print(a is Object); //true } }
三、dart泛型
1、泛型方法
通俗理解:泛型就是解决 类 接口 方法的复用性、以及对不特定数据类型的支持(类型校验)
//同时支持返回 string类型 和int类型 (代码冗余) // String getData1(String value){
// return value; // } // int getData2(int value){
// return value; // } //同时返回 string类型 和number类型 不指定类型可以解决这个问题 // getData(value){
// return value; // } //不指定类型放弃了类型检查。我们现在想实现的是传入什么 返回什么。比如:传入number 类型必须返回number类型 传入 string类型必须返回string类型 T getData<T>(T value) {
return value; } // getData<T>(T value){
// return value; // } void main() {
// print(getData(21)); // print(getData('xxx')); getData<String>('你好'); print(getData<String>('你好')); print(getData<int>(12)); }
2、泛型类
//集合List 泛型类的用法 //案例:把下面类转换成泛型类,要求MyList里面可以增加int类型的数据,也可以增加String类型的数据。但是每次调用增加的类型要统一 /* class MyList { List list = <int>[]; void add(int value) { this.list.add(value); } List getList() { return list; } } MyList l = new MyList(); l.add(1); l.add(12); l.add(5); print(l.getList()); */ class MyList<T> {
List list = <T>[]; void add(T value) {
this.list.add(value); } List getList() {
return list; } } main() {
MyList l1 = new MyList(); l1.add("codeSE"); l1.add(120); l1.add(true); print(l1.getList()); //[codeSE, 120, true] MyList l2 = new MyList<String>(); l2.add("codeSE_01"); // l2.add(11); //错误的写法 print(l2.getList()); //[codeSE_01] MyList l3 = new MyList<int>(); l3.add(11); l3.add(12); // l3.add("aaaa");//错误的写法 print(l3.getList()); //[11, 12] // List list = List.filled(2, ""); //错误,没有实例化 // list[0] = "张三"; // list[1] = "李四"; // print(list); List list0 = new List.filled(2, ""); list0[0] = "张三0"; list0[1] = "李四0"; print(list0); //[张三0, 李四0] List list1 = new List<String>.filled(2, ""); list1[0] = "张三1"; list1[1] = "李四1"; print(list1); //[张三1, 李四1] List list2 = new List<int>.filled(2, 0); list2[0] = 12; list2[1] = 13; print(list2); //[12,13] }
3、泛型接口
Dart中的泛型接口:
实现数据缓存的功能:有文件缓存、和内存缓存。内存缓存和文件缓存按照接口约束实现。
1、定义一个泛型接口 约束实现它的子类必须有getByKey(key) 和 setByKey(key,value) 2、要求setByKey的时候的value的类型和实例化子类的时候指定的类型一致
// abstract class ObjectCache {
// getByKey(String key); // void setByKey(String key, Object value); // } // abstract class StringCache {
// getByKey(String key); // void setByKey(String key, String value); // } // abstract class Cache<T> {
// getByKey(String key); // void setByKey(String key, T value); // } abstract class Cache<T> {
getByKey(String key); void setByKey(String key, T value); } class FlieCache<T> implements Cache<T> {
getByKey(String key) {
return null; } void setByKey(String key, T value) {
print("我是文件缓存 把key=${
key} value=${
value}的数据写入到了文件中"); } } class MemoryCache<T> implements Cache<T> {
getByKey(String key) {
return null; } void setByKey(String key, T value) {
print("我是内存缓存 把key=${
key} value=${
value} -写入到了内存中"); } } void main() {
// FlieCache f = new FlieCache<String>(); // f.setByKey('fileName', '文件信息-'); FlieCache f = new FlieCache<List>(); f.setByKey('list', [ 'codeSE', 'zh', 123 ]); //我是文件缓存 把key=list value=[codeSE, zh, 123]的数据写入到了文件中 // MemoryCache m=new MemoryCache<String>(); // m.setByKey('index', '首页数据'); MemoryCache m = new MemoryCache<Map>(); m.setByKey('index', {
"name": "张三", "age": 20 }); //我是内存缓存 把key=index value={name: 张三, age: 20} -写入到了内存中 }
四、内置库和第三方库
模块化开发很重要,所以这就需要使用到库的概念。
在Dart中,库的使用时通过import关键字引入的。
library指令可以创建一个库,每个Dart文件都是一个库,即使没有使用library指令来指定。
Dart中的库主要有三种:
1、我们自定义的库 import 'lib/xxx.dart'; 2、系统内置库 import 'dart:math'; import 'dart:io'; import 'dart:convert'; 3、Pub包管理系统中的库 https://pub.dev/packages https://pub.flutter-io.cn/packages https://pub.dartlang.org/flutter/ 1、需要在自己想项目根目录新建一个pubspec.yaml 2、在pubspec.yaml文件 然后配置名称 、描述、依赖等信息 3、然后运行 pub get 获取包下载到本地 4、项目中引入库 import 'package:http/http.dart' as http; 看文档使用
1、导入本地自定义库和系统内置库
import 'lib/Animal.dart'; main(){
var a=new Animal('小黑狗', 20); print(a.getName()); }
// import 'dart:io'; import "dart:math"; main() {
print(min(12, 23)); //12 print(max(12, 25)); //25 }
2、async和await
async和await
只有async方法才能使用await关键字调用方法 如果调用别的async方法必须使用await关键字
async是让方法变成异步。
await是等待异步方法执行完成。
void main() async{
var result = await testAsync(); print(result); } //异步方法 testAsync() async{
return 'Hello async'; }
导入系统内置httpClient
库,实现异步请求数据:
import 'dart:io'; import 'dart:convert'; void main() async {
var result = await getDataFromZhihuAPI(); print(result); //请求回来的数据 } //api知乎的一个接口: http://news-at.zhihu.com/api/3/stories/latest getDataFromZhihuAPI() async {
//1、创建HttpClient对象 var httpClient = new HttpClient(); //2、创建Uri对象 var uri = new Uri.http('news-at.zhihu.com', '/api/3/stories/latest'); //3、发起请求,等待请求 var request = await httpClient.getUrl(uri); //4、关闭请求,等待响应 var response = await request.close(); //5、解码响应的内容 return await response.transform(utf8.decoder).join(); }
3、包的部分导入、冲突解决和延迟加载
/* 1、冲突解决 当引入两个库中有相同名称标识符的时候,如果是java通常我们通过写上完整的包名路径来指定使用的具体标识符,甚至不用import都可以,但是Dart里面是必须import的。当冲突的时候,可以使用as关键字来指定库的前缀。如下例子所示: import 'package:lib1/lib1.dart'; import 'package:lib2/lib2.dart' as lib2; Element element1 = new Element(); // Uses Element from lib1. lib2.Element element2 = new lib2.Element(); // Uses Element from lib2. */ import 'lib/Person1.dart'; import 'lib/Person2.dart' as lib; main(List<String> args) {
Person p1=new Person('张三', 20); p1.printInfo(); lib.Person p2=new lib.Person('李四', 20); p2.printInfo(); }
/* 部分导入 如果只需要导入库的一部分,有两种模式: 模式一:只导入需要的部分,使用show关键字,如下例子所示: import 'package:lib1/lib1.dart' show foo; 模式二:隐藏不需要的部分,使用hide关键字,如下例子所示: import 'package:lib2/lib2.dart' hide foo; */ // import 'lib/myMath.dart' show getAge; import 'lib/myMath.dart' hide getName; void main() {
// getName(); getAge(); }
/* 延迟加载 也称为懒加载,可以在需要的时候再进行加载。 懒加载的最大好处是可以减少APP的启动时间。 懒加载使用deferred as关键字来指定,如下例子所示: import 'package:deferred/hello.dart' deferred as hello; 当需要使用的时候,需要使用loadLibrary()方法来加载: greet() async { await hello.loadLibrary(); hello.printGreeting(); } */
4、补充
/* Dart 常量: final 和 const修饰符 const 声明的常量是在编译时确定的,永远不会改变 final 声明的常量允许声明后再赋值,赋值后不可改变,final 声明的变量是在运行时确定的; final不仅有const的编译时常量的特性,最重要的它是运行时常量,并且final是惰性初始化,即在运行时第一次使用前才初始化 */ void main() {
//const常量 //const PI=3.14; // PI=3.14159; //const定义的常量没法改变 //print(PI); // final 常量 // final PI=3.14; // print(PI); //final和const区别:final 可以开始不赋值 只能赋一次 ; 而final不仅有const的编译时常量的特性,最重要的它是运行时常量,并且final是惰性初始化,即在运行时第一次使用前才初始化 final a; a = 13; // a=14; print(a); final d = new DateTime.now(); print(d); //2022-11-18 11:36:52. }
/* dart:core 库中identical 函数的用法介绍如下。 用法: bool identical( Object? a, Object? b ) 检查两个引用是否指向同一个对象。 var o = new Object(); var isIdentical = identical(o, new Object()); // false, different objects. print(isIdentical); isIdentical = identical(o, o); // true, same object print(isIdentical); isIdentical = identical(const Object(), const Object()); // true, const canonicalizes print(isIdentical); isIdentical = identical([1], [1]); // false print(isIdentical); isIdentical = identical(const [1], const [1]); // true print(isIdentical); isIdentical = identical(const [1], const [2]); // false print(isIdentical); isIdentical = identical(2, 1 + 1); // true, integers canonicalizes print(isIdentical); */ void main(){
// var o1 = new Object(); // var o2 = new Object(); // print(identical(o1,o2)); //false 不共享存储空间 // print(identical(o1,o1)); //true 共享存储空间 // var o1 = Object(); // var o2 = Object(); // print(identical(o1,o2)); //false // print(identical(o1,o1)); //true //表示实例化常量构造函数 //o1 和 o2共享了存储空间 // var o1 = const Object(); // var o2 = const Object(); // print(identical(o1,o2)); //true 共享存储空间 // print(identical(o1,o1)); //true 共享存储空间 // print(identical([2],[2])); //false // var a=[2]; // var b=[2]; // print(identical(a,b)); //false 不共享存储空间 // print(identical(const [2],const [2])); //true const a=[2]; const b=[2]; print(identical(a,b)); //true 共享存储空间 const c=[2]; const d=[3]; print(identical(c,d)); //false 不共享存储空间 } // 发现:const关键词在多个地方创建相同的对象的时候,内存中只保留了一个对象 // 共享存储空间条件:1、常量 2、值相等
普通函数与常量构造函数:
class Container{
int width; int height; Container({
required this.width,required this.height}); } void main(){
var c1=new Container(width: 100,height: 100); var c2=new Container(width: 100,height: 100); print( identical(c1, c2)); //false c1和c2在内存中存储了2份 }
/* 常量构造函数总结如下几点: 1、常量构造函数需以const关键字修饰 2、const构造函数必须用于成员变量都是final的类 3、如果实例化时不加const修饰符,即使调用的是常量构造函数,实例化的对象也不是常量实例 4、实例化常量构造函数的时候,多个地方创建这个对象,如果传入的值相同,只会保留一个对象。 5、Flutter中const 修饰不仅仅是节省组件构建时的内存开销,Flutter 在需要重新构建组件的时候,由于这个组件是不应该改变的,重新构建没有任何意义,因此 Flutter 不会重建构建 const 组件 */ //常量构造函数 class Container{
final int width; final int height; const Container({
required this.width,required this.height}); } void main(){
var c1=Container(width: 100,height: 100); var c2=Container(width: 100,height: 100); print(identical(c1, c2)); //false var c3=const Container(width: 100,height: 100); var c4=const Container(width: 100,height: 100); print(identical(c3, c4)); //true var c5=const Container(width: 100,height: 110); var c6=const Container(width: 120,height: 100); print(identical(c5, c6)); //false } // 实例化常量构造函数的时候,多个地方创建这个对象,如果传入的值相同,只会保留一个对象。
到此这篇dart入门基础_dart语言菜鸟教程的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!
版权声明:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权、违法违规、事实不符,请将相关资料发送至xkadmin@xkablog.com进行投诉反馈,一经查实,立即处理!
转载请注明出处,原文链接:https://www.xkablog.com/rfx/2173.html