一、为什么要学习Dart和Flutter?
闲来无事,在公司趁着项目闲来学习混编的技术,作为5年iOS开发经验的小菜鸡,自然是比不上所谓的大神。不过抱着虚心学习总会提高的心态,还是学习一下时下最流行的混编框架Flutter,在学习Flutter之前,还是得先把基础打好,这不就先来学习一下Dart的基础知识
1.Dart如何安装
学习dart就得从头学习,虽然语法上跟传统的java和javascript语法习惯差不多,但是Dart中还是会有自己得语法特点,所以我们就从头开始学习:
搭建dart环境
这已章节我就简单带过吧,因为网上大神的教程,如果你是mac系统,那么就跟我一起来,如果是win系统的朋友,那就更简单了官网下载最新的Dart运行环境的安装包,跟着步骤一步一步安装即可,在这里我就不赘述了,我们来看Mac端的安装步骤。
let's go
- 安装 Dart Sdk
brew tap dart-lang/dart
brew install dart
- 升级 Dart
brew upgrade dart
- 查看安装信息
brew info dart
- 安装上面成功安装后,查看dart的安装信息如下:
yumodeMacBook-Pro:flutter yumo$ brew info dart
dart-lang/dart/dart: stable 2.3.1, devel 2.3.2-dev.0.0
The Dart SDK
https://www.dartlang.org/
/usr/local/Cellar/dart/2.3.1 (367 files, 409.7MB) *
Built from source on 2019-05-26 at 08:59:06
From: https://github.com/dart-lang/homebrew-dart/blob/master/dart.rb
==> Options
--devel
Install development version 2.3.2-dev.0.0
==> Caveats
Please note the path to the Dart SDK:
/usr/local/opt/dart/libexec
这样简单的命令行代码之后就实现了dart的安装,简单吧?
2.如何输出第一个Dart指令
这里编辑器我们推荐用vscode,因为现在vscode的插件化实在是太好用了,我们现在来简单是输出一个Hello world来看看吧
import 'package:dart/dart.dart' as dart;
main(List<String> arguments) {
print('Hello world: ${dart.calculate()}!');
}
然后看看控制台,是不是输出了:
[Running] dart "/Users/test/dart_flutter/dart_test/index.dart"
Hello world
[Done] exited with code=0 in 1.697 seconds
这样第一个Dart代码就生成了,是不是很简单?那么今天得分享就是带大家部署一下Dart的运行环境,下一次我们一起就来深入的学习Dart的语法和代码逻辑结构了~
二、变量与常量
1.变量
变量和常量是一个老生常谈的内容,在我们了解所有语言之前都会讨论变量和常量的东西,这是一个语言的基础,Dart是一个强大的脚本类语言,可以不预先定义变量类型,自动会类型推倒,Dart中定义变量可以通过var关键字可以通过类型来申明变量,如:
var str='this is var';
String str='this is var';
int str=123;
// 注意: var 后就不要写类型 , 写了类型 不要var 两者都写 var a int = 5; 报错
2.常量(final 和 const修饰符)
const值不变,一开始就得赋值,final可以开始不赋值,最后只能赋一次 ; 而final不仅有const的编译时常量的特性,最重要的它是运行时常量,并且final是惰性初始化,即在运行时第一次使用前才初始化,永远不改量的量,请使用final或const修饰它,而不是使用var或其他变量类型。
final name = 'Bob'; // Without a type annotation
final String nickname = 'Bobby';
const bar = 1000000; // Unit of pressure (dynes/cm2)
const double atm = 1.01325 * bar; // Standard atmosphere
3.Dart中的命名规则
- 变量名称必须由数字、字母、下划线和美元符($)组成。
- 注意:标识符开头不能是数字
- 标识符不能是保留字和关键字。
- 变量的名字是区分大小写的如: age和Age是不同的变量。在实际的运用中,也建议,不要用一个单词大小写区分两个变量。
- 标识符(变量名称)一定要见名思意 :变量名称建议用名词,方法名称建议用动词
三、数据类型
Dart中支持以下数据类型:
` Numbers(数值)`:
int
double
`Strings(字符串)`
String
`Booleans(布尔)`
bool
`List(数组)`
在Dart中,数组是列表对象,所以大多数人只是称它们为列表
`Maps(字典)`
通常来说,Map 是一个键值对相关的对象。 键和值可以是任何类型的对象。每个 键 只出现一次, 而一个值则可以出现多次
1.字符串类型数据
通常字符串类型数据我们可以这么定义:
var str1='this is str1';
var str2="this is str2";
String str3="""
this is str1
this is str1
this is str1
""";
所谓的用单引号''和双引号""Dart都能解析出字符串,第三种方法比较特殊,用三个双引号去引用字符串,这里的意义在与我们可以完全输出三个引号之间的内容,从而不改变字符串的格式,例如某些字符串需要换行之类的。这样就减轻了我们对字符串的输出控制。
字符串的拼接:这是一个比较重要的模式,在程序开发时我们都需要用到字符串的拼接功能,在Dart中,字符串拼接我们是用+去链接两个字符串,例如:
String str1='你好';
String str2='Dart';
print("$str1 $str2");
print(str1 + str2);
print(str1 +" "+ str2);
2.数值类型数据
数值类型的数据比较好理解,就是int和double类型的数据,在这里就不过多阐述了,相信大家有点语言基础的朋友都能理解,举个例子:
//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);
3.布尔类型数据
bool类型的数据无非就是true或者false,跟数值类型一样,很好理解:
//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');
// }
var a=123;
var b=123;
if(a==b){
print('a=b');
}else{
print('a!=b');
}
4.List(数组)类型数据
数组类型的数据就是将一众合数据组成一个数组,选择这个数组中某一个数据可以由数组的index值来选择。我们在定义数组类型的数据时,可以这样定义:
var l1=['aaa','bbbb','cccc'];
print(l1);
print(l1.length);
print(l1[1]);
也可以这样定义:
var l2=new List();
l2.add('张三');
l2.add('李四');
l2.add('王五');
print(l2);
print(l2[2]);
这样都是没有问题的。但是如果我们需要让数组类型的数据中都由一种数据组成我们可以这样设置数组
var l3=new List<String>();
l3.add('张三');
// l3.add(123);//报错,因为数据类型不匹配
print(l3);
从上述例子可以看出,在规定了数组类型数据为string类型时,我们添加了一个int类型进数组就不被承认,Dart会抛出异常。所以我们就不能像之前的例子中随意添加数据了。
5.Map类型数据
Map数据类型我们其实可以看成是一种对象类型数据,这个在我们开发实践中很常见,所有的json数据都基本上会采用Map,我们可以把Map理解成一种Key/Value类的数据形式,首先从定义的角度我们可以这样去定义Map:
var person={
"name":"张三",
"age":20,
"work":["程序员","送外卖"]
};
print(person);
print(person["name"]);
print(person["age"]);
print(person["work"]);
也可以这样定义:
var p=new Map();
p["name"]="李四";
p["age"]=22;
p["work"]=["程序员","送外卖"];
print(p);
print(p["age"]);
- 注意事项
1.map的key类型不一致也不会报错。
2.添加元素的时候,会按照你添加元素的顺序逐个加入到map里面,哪怕你的key,比如分别是 1,2,4,看起来有间隔,事实上添加到map的时候是{1:value,2:value,4:value} 这种形式。
3.map里面的key不能相同。但是value可以相同,value可以为空字符串或者为null。
四、基本运算符
| 描述 | 操作符 |
|---|---|
| 一元后置操作符 | expr++ expr-- () [] . ?. |
| 一元前置操作符 | expr !expr ~expr ++expr --expr |
| 乘除 | * / % ~/ |
| 加减 | + - |
| 位移 | << >> |
| 按位与 | & |
| 按位或 | | |
| 按位异或 | ^ |
| 逻辑与 | && |
| 逻辑或 | || |
| 关系和类型判断 | >= > <= < as is is! |
| 等 | == != |
| 如果为空 | ?? |
| 条件表达式 | expr1 ? expr2 : expr3 |
| 赋值 | = *= /= ~/= %= += -= <<= >>= &= ^= = ??= |
<p>其实我们要是有点开发经验的朋友,对于上述的表格最常用的其实也就那么几个,例如基本运算符"+,-,*,%,/",要么就是逻辑运算符"||,&&",判断也有用过,首先来介绍:</p>
<h3 id="toc_0">1.基础运算符</h3>
<p><code>+</code>,<code>-</code>,<code>*</code>,<code>%</code>,<code>/</code></p>
<p>我们先写一个小demo</p>
<pre><code class="language-dart">int a=13;
int b=5;
print(a+b); //加
print(a-b); //减
print(a*b); //乘
print(a/b); //除
print(a%b); //其余
print(a~/b); //取整
var c=a*b;
print('--------');
print(c);
</code></pre>
<p>写完这个demo之后运行,我们可以发现控制台打印:</p>
<pre><code class="language-dart">[Running] dart "/Users/test/Downloads/Dart课件-2/demo04/02 算术运算符.dart"
18
8
65
2.6
3
2
65
[Done] exited with code=0 in 1.781 seconds
</code></pre>
<p>其实这个运算符在常量中跟我们通常的数学运算符是一样的。这个应该上过小学的都知道吧~我就不再赘述了。</p>
<h3 id="toc_1">2.关系运算符</h3>
<p><code>==</code>,<code>!=</code>,<code>></code>,<code><</code>,<code>>=</code>,<code><=</code></p>
<p>这些跟我们数学运算符也差不多的,小学也学过,Demo撸上</p>
<pre><code class="language-dart">void main(){
// == != > < >= <=
int a=5;
int b=3;
print(a==b); //判断是否相等
print(a!=b); //判断是否不等
print(a>b); //判断是否大于
print(a<b); //判断是否小于
print(a>=b); //判断是否大于等于
print(a<=b); //判断是否小于等于
if(a>b){
print('a大于b');
}else{
print('a小于b');
}
}
</code></pre>
<p>控制台输出</p>
<pre><code class="language-dart">[Running] dart "/Users/test/Downloads/Dart课件-2/demo04/tempCodeRunnerFile.dart"
false
true
true
false
true
false
a大于b
[Done] exited with code=0 in 1.878 seconds
</code></pre>
<h3 id="toc_2">3.逻辑运算符</h3>
<p><code>!</code>,<code>||</code>,<code>&&</code></p>
<p>在很多开发语言中,我们用<code>!</code>来表示取反,也就是相反值,用<code>||</code>来表示或运算,就是在<code>||</code>两边的值如果有某一个为真那么统一为真,都为真则为真,都为假则为假,而<code>&&</code>就不同了,在<code>&&</code>两边的若其中有一个为假则整个表达式为假。都为真则为真,Demo:</p>
<pre><code class="language-dart">void main(){
/* ! 取反 /
bool flag=false;
print(!flag); //取反
/ &&并且:全部为true的话值为true 否则值为false /
bool a=true;
bool b=true;
print(a && b);
/ ||或者:全为false的话值为false 否则值为true */
bool c=false;
bool d=false;
print(c || d);
//如果一个人的年龄是20 并且 sex是女的话我们打印这个人
int age1=20;
String sex1="女";
if(age1==20 && sex1=="女"){
print("sex1");
}else{
print("不打印");
}
//如果一个人的年龄是20 或者 sex是女的话我们打印这个人
int age=23;
String sex="女";
if(age==20 || sex=="女"){
print("sex");
}else{
print("不打印");
}
}
</code></pre>
<p>控制台输出</p>
<pre><code class="language-dart">[Running] dart "/Users/test/Downloads/Dart课件-2/demo04/04 逻辑运算符.dart"
true
true
false
20 --- 女
23 --- 女
[Done] exited with code=0 in 1.491 seconds
</code></pre>
<h3 id="toc_3">4.赋值运算符</h3>
<p>赋值运算符分为<code>基础赋值运算符</code>和<code>复合赋值运算符</code></p>
<blockquote>
<p>基础赋值运算符 <code>=</code> <code>??=</code>
复合赋值运算符 <code>+=</code> <code>-=</code> <code>*=</code> <code>/=</code> <code>%=</code> <code>~/=</code></p>
</blockquote>
<p>其中<code>??=</code>代表是如果前面的为空,则把等号后面的给赋值给前面的参数</p>
<p>例如:</p>
<pre><code class="language-dart"> int b=6;
b??=23;
print(b);
int b;
b??=23;
print(b);
</code></pre>
<p>控制台打印</p>
<pre><code class="language-dart">[Running] dart "/Users/test/Downloads/Dart课件-2/demo04/05 赋值运算符.dart"
6
[Done] exited with code=0 in 1.38 seconds
</code></pre>
<p>复合运算符我们就简单举一个例子,其余的都跟这个一个模式</p>
<pre><code class="language-dart"> var a=12;
a=a+10;//表示a=a+10
print(a);
</code></pre>
5.++和--
++和--是自增自减运算符,表示变量自动增加或者减少
var a=10;
var b=a--;
print(a); //9
print(b); //10
6.+=,-=
+=和-=是表示变量加上等号后面的值或者自己减少等号后面的值,demo
var c = 10;
c+=10,
print(c);//20
五、条件表达式
1.if...else
这个就不多说了,好像所有的语言都有if...else,在if中书写条件语句,else则是非if中条件的执行block,这个我就不多阐述,有点编程基础的都应该晓得,demo:
bool flag=true;
if(flag){
print('true');
}else{
print('false');
}
2.三目运算符
三目运算符其实原理跟if..else如出一辙,只是判断简化了不少,不用写那么多代码而已:demo,
bool flag=false;
String c=flag?'我是true':'我是false';
print(c);
3.??运算符
这个运算符用起来比较少,意思是说假如果有a和b两个变量,要把a的值给b,如果a有值则直接将a的值给b,如果a没有值就用??后面的值去取代a的值赋给b,举个例子:
var a;
var b= a ?? 10;
print(b);
控制台打印
[Running] dart "/Users/test/Downloads/Dart课件-2/demo04/06 条件表达式.dart"
10
[Done] exited with code=0 in 1.019 seconds
不难看出其实??的作用就是给一个默认值而已,也很容易理解
4.do..while
do..while中do表示条件控制,while里表示循环内容
语法格式:
while(表达式/循环条件){
}
do{
语句/循环体
}while(表达式/循环条件);
!> 注意
1、最后的分号不要忘记
2、循环条件中使用的变量需要经过初始化
3、循环体中,应有结束循环的条件,否则会造成死循环。
有一道很经典的运算,就是求1到100的和,我们就可以用do..while来解
int i=1;
var sum=0;
while(i<=100){
sum+=i;
i++;
}
print(sum);
5.for循环
这个跟if一样都是很多开发语言通用的语法,for里面控制循环条件,只要符合条件的就执行代码块中的内容
for (int i = 1; i<=100; i++) {
print(i);
}
//第一步,声明变量int i = 1;
//第二步,判断i <=100
//第三步,print(i);
//第四步,i++
//第五步 从第二步再来,直到判断为false
举个例子:
//打印0-50所有的偶数
for(int i=0;i<=50;i++){
if(i%2==0){
print(i);
}
}
六、List,Set,Map
这一个章节我们来讨论开发中用的最多的集合类型,List,Set和Map在很多开发语言中也比较常见了,在Dart中也集成了这些语言的特性。
1.List
List就是数组类型,数组表示众多数据集合在一个数组类型中,换言之我们用一个[]来表示这些数据:
List fruitList=['香蕉','苹果','西瓜'];
//又或者
List numList=[1,2,3];
那么如果在开发中我们要对数组进行操作怎么办?不要紧,在Dart中内置了很多对List操作的方法,例如:
i> 常用属性:
| 名称 | 含义 |
|---|---|
| length | 长度 |
| reversed | 翻转 |
| isEmpty | 是否为空 |
| isNotEmpty | 是否不为空 |
i> 常用方法:
| 方法名 | 含义 |
|---|---|
| remove | 删除 |
| removeAt | 删除,传入索引值 |
| add | 增加 |
| addAll | 拼接数组 |
| indexOf | 查找,传入具体值 |
| fillRange | 修改 |
| Insert(index,value) | 指定位置插入 |
| insertAll(index,list) | 指定位置插入List |
| toList() | 其他类型转换成List |
| Join() | List转换成字符串 |
| Split() | 字符串转化成List |
| forEach | 遍历所有数组 |
| map | 映射数组 |
关于List的属性我们可以直接调用Dart自带的方法:
List myList=['香蕉','苹果','西瓜'];
print(myList.length);
print(myList.isEmpty);
print(myList.isNotEmpty);
print(myList.reversed); //对列表倒序排序
var newMyList=myList.reversed.toList();
print(newMyList);
控制台打印为:
[Running] dart "/Users/test/Downloads/Dart课件-3/demo06/01list.dart"
3
false
true
(西瓜, 苹果, 香蕉)
[西瓜, 苹果, 香蕉]
[Done] exited with code=0 in 1.992 seconds
- List添加一个元素
List myList=['香蕉','苹果','西瓜'];
myList.add('桃子'); //增加数据 增加一个
- List添加一个数组
myList.addAll(['桃子','葡萄']); //拼接数组
- List查找一个数据
myList.indexOf('苹x果')//indexOf查找数据 查找不到返回-1 查找到返回索引值
- List移除一个数据
myList.remove('西瓜');
- List移除一个特定下标的数据
myList.removeAt(1)
- List修改元素
myList.fillRange(0, 3,'aaa'); //修改
//fillrange代表的意思是从第几位开始修改之后的几位
- List插入元素
myList.insert(1,'aaa')
- List插入数组
myList.insertAll(1, ['aaa','bbb'])
- List转换为字符串
List myList=['香蕉','苹果','西瓜'];
var str=myList.join('-'); //list转换成字符串
print(str);
控制台打印:
[Running] dart "/Users/test/Downloads/Dart课件-3/demo06/01list.dart"
香蕉-苹果-西瓜
[Done] exited with code=0 in 2.078 seconds
- 将字符串转为数组
var str='香蕉-苹果-西瓜';
var list=str.split('-');
print(list);
控制台打印:
[Running] dart "/Users/test/Downloads/Dart课件-3/demo06/01list.dart"
[香蕉, 苹果, 西瓜]
[Done] exited with code=0 in 1.555 seconds
!> 数组转换字符串和字符串转换为数组我们用的是
join和split 关键字,这个要牢记
2.Set
Set就是集合类型,在Set中我们保证每个元素都是不相同不重复的,在许多语言中,Set可以用来区别数据唯一性的选择类型,Dart语言也不例外。同样有着Set集合类型。例如
var s=new Set();
s.add('香蕉');
s.add('苹果');
s.add('苹果');
print(s); //{香蕉, 苹果}
print(s.toList()); //set转为List
我们也可以用Set来对List进行去重操作,例如:
List myList=['香蕉','苹果','西瓜','香蕉','苹果','香蕉','苹果'];
var s=new Set();
s.addAll(myList);
print(s);
print(s.toList());
3.Map
Map在我们iOS里叫Dictionary,也就是字典类型,用key和value解释数据,也就是在Map中每一个值都有一个名字,而不是像数组中用index去区分,也就是俗话说的对象类型,例如:
Map person={
"name":"张三",
"age":20
};
- 我们如果需要获取Map中key对应的值则可以这么写:
person["name"];//张三
- 如果我们要将所有的key或者value都转为list则可以
person.keys.toList();
person.values.toList();
- 判断一个Map是否为空或者是否不为空
person.isEmpty
person.isNotEmpty
七、函数方法
1.方法的基本写法
一般在Dart中函数我们通常可以这么去书写,这个写法想必众多语言也是用着同样的写法:
print();
上述就是一个简单的打印方法,我们用函数/方法名作为前置标识,后面用一个()去表示执行这个函数方法。那么我们还可以自定义去扩展这个方法,例如:
i> 返回类型 方法名称(参数1,参数2,...){
方法体
return 返回值;
}
上述的是文字版的描述,如果换成代码我们举个简单的例子可以写一个简单的打印参数的方法:
void printInfo(name,age){
print("$name,$age");
}
//调用
printInfo("leon",17)//caoxiaozhu,17
2.方法的作用域
如果在一个方法中调用了另外一个方法,我们通常在外部就不能直接调用那个方法内部的方法,这句话听起来比较绕口,简单来说,嵌套方法我们只能调用最外层的方法:
void xxx(){
aaa(){
print(getList());
print('aaa');
}
aaa();
}
上述就是一个简单的嵌套方法,那我们在外部调用时,我们不能直接调用xxx方法中的aaa,而只能调用xxx,我们在调用aaa时,Dart就会报错,说明我们不能跨域操作。
3.方法传参
在方法传参中跟我们上述介绍的自定义方法有点如出一辙,我们可以指定参数类型,也可以不指定参数类型。具体说起来我们可以将外部的数据传入方法体中进行进一步的操作。
//1、定义一个方法 求1到这个数的所有数的和
int sumNum(int n){
var sum=0;
for(var i=1;i<=n;i++)
{
sum+=i;
}
return sum;
}
//调用
sumNum(100)//求1到100的和
4.可选参数
我们除了上述必须传参的函数之外我们还可以创建可选参数传入,也就是说上述必传参函数我们必须要传入定义的参数,但是可选传参函数我们则可以选择性的传入需要的参数,就算不传参系统也不会报错,在可选传参函数中我们用[]来表示可选参数的传入。
String printUserInfo(String username,[int age]){ //行参
if(age!=null){
return "姓名:$username---年龄:$age";
}
return "姓名:$username---年龄保密";
}
//age为可选参数
外部调用
print(printUserInfo('张三',21))//张三 21
print(printUserInfo('张三'))//张三
5.参数默认值
在开发中我们还有一种需求就是有一个参数我们可以不必每次都传入新的数据,而是有些情况下参数相对的都一样,那么我们则可以用默认值来代替某一次参数的传入。我们直接在参数后面接一个=并且附上你需要的默认值。
String printUserInfo(String username,[String sex='男',int age]){ //行参
if(age!=null){
return "姓名:$username---性别:$sex--年龄:$age";
}
return "姓名:$username---性别:$sex--年龄密";
}
//调用
print(printUserInfo('张三'))//姓名:张三---性别:男--年龄保密
print(printUserInfo('小李','女'))//姓名:小李---性别:女--年龄保密
print(printUserInfo('小李','女',30))//姓名:小李---性别:女--年龄:30
6.箭头函数
Dart中的箭头函数必须满足在function代码块中只执行一句语句,才能使用箭头函数,如果超过一句话语则不能使用箭头函数,如:
//需求:使用forEach打印下面List里面的数据
List list=['苹果','香蕉','西瓜'];
list.forEach((value){
print(value);
});
list.forEach((value)=>print(value));
//箭头函数只打印一句话
list.forEach((value)=>{
print(value)
});
在上述打印方法中我们还可以将箭头函数缩略写成
list.forEach((value)=>print(value));
7.匿名函数
匿名函数区别于普通函数的最大的地方就是匿名函数没有函数名作为支撑。可以直接将函数附给一个变量,通过变量来执行代码块。不过在实际开发中用的比较少,如果按照规范来的话不太推荐大家用匿名函数。
var printNum=(){
print(123);
};
printNum();
8.自执行方法
顾名思义,自动执行的方法,但是不知道什么时候会用 @_@。。
((int n){
print(n);
print('我是自执行方法');
})(12);
9.闭包
首先我们来说一个场景:
- 想实现的功能:
1、常驻内存
2、不污染全局 - 目前的功能:
1、全局变量特点: 全局变量常驻内存、全局变量污染全局
2、局部变量的特点: 不常驻内存会被垃圾机制回收、不会污染全局 - 闭包:
1、闭包的特点:常驻内存、不污染全局。
2、概念:函数嵌套函数, 内部函数会调用外部函数的变量或参数, 变量或参数不会被系统回收(不会释放内存)
3、写法:函数嵌套函数,并return 里面的函数,这样就形成了闭包。
举个例子:
//全局变量
var a=123;
void main(){
print(a);
fn(){
a++;
print(a);
}
fn();
fn();
fn();
}
//在main的外层有个全局变量a,我们如果调用fn会使a的值一直增加
因为a常驻内存,所以很多地方都可以调用a这个变量,导致了污染全局。保证不了数据的安全。所以我们想办法让a变为内部变量,防止外部的调用,这样闭包就有了意义。
fn(){
var a=123; /*不会污染全局 常驻内存*/
return(){
a++;
print(a);
};
}
var b=fn();
b();
b();
b();
在实际开发中,我们可以将变量写在一个方法体内,保证了变量的的作用域仅限于方法体内,外部并不能调用a。从而使得数据变得相对安全。不会导致任何地方都能用a的值。
八、面向对象
面向对象编程(OOP)的三个基本特征是:封装、继承、多态
- 封装:封装是对象和类概念的主要特性。封装,把客观事物封装成抽象的类,并且把自己的部分属性和方法提供给其他对象调用, 而一部分属性和方法则隐藏。
- 继承:面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
- 多态:允许将子类类型的指针赋值给父类类型的指针, 同一个函数调用会有不同的执行效果 。
要注意的是:
!> Dart所有的东西都是对象,所有的对象都继承自Object类。
1.类
创建一个类我们可以用class关键字去创建,比如说我们要创建一个Person类可以这样写:
class Person{
String name="张三";
int age=23;
void getInfo(){
// print("$name----$age");
print("${this.name}----${this.age}");
}
void setInfo(int age){
this.age=age;
}
2.构造方法
class Person{
String name;
int age;
//默认构造函数的简写
Person(this.name,this.age);
void printInfo(){
print("${this.name}----${this.age}");
}
}
构造方法就是在函数内部重写这个类名后面并且带上类中需要的参数,这样外部在new这个类的时候就必须向这个类传入对应的参数,从而达到外部传值的作用。
3.命名构造函数
这个用的比较少,我这里只展示一下如何使用
class Person{
String name;
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}");
}
}
外部调用
Person p1=new Person.now();
4.get/set方法
这个就比较常见了,其他语言也有g/s方法,顾名思义了,set就是设置的意思,get就是获取的意思,set设置属性的值,get拿取属性的值。
//定义一个正方形
class Rect{
num height;
num width;
Rect(this.height,this.width);
get area{
return this.height*this.width;
}
set areaHeight(value){
this.height=value;
}
}
在Main中调用
void main(){
Rect r=new Rect(10,4);
// print("面积:${r.area()}");
r.areaHeight=6;
//获取面积
print(r.area);
}
5.类中的静态成员,静态方法
在类中,实用static关键字来实现类级别的变量和函数,静态方法可以访问静态成员。
class Person {
static String name = '张三';
static void show() {
print(name);
}
}
main(){
print(Person.name);
Person.show();
}
不过需要注意的是
class Person {
static String name = '张三';
int age=20;
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();
}
6.继承
简单继承,用extern关键字
class Person {
String name='张三';
num age=20;
void printInfo() {
print("${this.name}---${this.age}");
}
}
class Web extends Person{
}
main(){
Web w=new Web();
print(w.name);
w.printInfo();
}
Dart中的继承我们要注意:
- 1、子类使用extends关键词来继承父类
- 2、子类会继承父类里面可见的属性和方法 但是不会继承构造函数
- 3、子类能复写父类的方法 getter和setter
7.super关键字
super关键字我们用来调用父类的属性或者方法:
class Person {
String name;
num age;
Person(this.name,this.age);
void printInfo() {
print("${this.name}---${this.age}");
}
}
class Web extends Person{
String sex;
Web(String name, num age,String sex) : super(name, age){
this.sex=sex;
}
run(){
print("${this.name}---${this.age}--${this.sex}");
}
}
main(){
Person p=new Person('李四',20);
p.printInfo();
Person p1=new Person('张三',20);
p1.printInfo();
Web w=new Web('张三', 12,"男");
w.printInfo();
w.run();
}
控制台打印
[Running] dart "/Users/test/Downloads/Dart课件-3/demo10/tempCodeRunnerFile.dart"
李四---20
张三---20
张三---12
张三---12--男
[Done] exited with code=0 in 1.647 seconds
8.实例化命名构造函数传参
这个相对用的比较少,给个例子
class Person {
String name;
num age;
Person(this.name,this.age);
//自定义构造函数
Person.xxx(this.name,this.age);
void printInfo() {
print("${this.name}---${this.age}");
}
}
class Web extends Person{
String sex;
//super自定义构造函数
Web(String name, num age,String sex) : super.xxx(name, age){
this.sex=sex;
}
run(){
print("${this.name}---${this.age}--${this.sex}");
}
}
9.子类重写父类方法
class Person {
String name;
num age;
Person(this.name,this.age);
void printInfo() {
print("${this.name}---${this.age}");
}
work(){
print("${this.name}在工作...");
}
}
class Web extends Person{
Web(String name, num age) : super(name, age);
run(){
print('run');
}
//覆写父类的方法
@override //可以写也可以不写 建议在覆写父类方法的时候加上 @override
void printInfo(){
print("姓名:${this.name}---年龄:${this.age}");
}
@override
work(){
print("${this.name}的工作是写代码");
}
}
main(){
Web w=new Web('李四',20);
w.printInfo();
w.work();
}
其实继承是面向对象中比较重要的特征之一,在这里希望把继承的方法都记住。在开发中会受益颇深!
10.抽象类
Dart中的抽象类:Dart抽象类主要用于定义标准,子类可以继承抽象类,也可以实现抽象类接口。
- 抽象类通过abstract 关键字来定义
- Dart中的抽象方法不能用abstract声明,Dart中没有方法体的方法我们称为抽象方法。
- 如果子类继承抽象类必须得实现里面的抽象方法
- 如果把抽象类当做接口实现的话必须得实现抽象类里面定义的所有属性和方法。
- 抽象类不能被实例化,只有继承它的子类可以
extends抽象类 和 implements的区别:
- 如果要复用抽象类里面的方法,并且要用抽象方法约束自类的话我们就用extends继承抽象类
- 如果只是把抽象类当做标准的话我们就用implements实现抽象类
//案例:定义一个Animal 类要求它的子类必须包含eat方法
abstract class Animal{
eat(); //抽象方法
run(); //抽象方法
printInfo(){
print('我是一个抽象类里面的普通方法');
}
}
class Dog extends Animal{
@override
eat() {
print('小狗在吃骨头');
}
@override
run() {
// TODO: implement run
print('小狗在跑');
}
}
class Cat extends Animal{
@override
eat() {
// TODO: implement eat
print('小猫在吃老鼠');
}
@override
run() {
// TODO: implement run
print('小猫在跑');
}
}
main(){
Dog d=new Dog();
d.eat();
d.printInfo();
Cat c=new Cat();
c.eat();
c.printInfo();
// Animal a=new Animal(); //抽象类没法直接被实例化
}
11.多态
Datr中的多态:
- 允许将子类类型的指针赋值给父类类型的指针, 同一个函数调用会有不同的执行效果 。
- 子类的实例赋值给父类的引用。
- 多态就是父类定义一个方法不去实现,让继承他的子类去实现,每个子类有不同的表现。
abstract class Animal{
eat(); //抽象方法
}
class Dog extends Animal{
@override
eat() {
print('小狗在吃骨头');
}
run(){
print('run');
}
}
class Cat extends Animal{
@override
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还是有区别的。
- dart的接口没有interface关键字定义接口,而是普通类或抽象类都可以作为接口被实现。
- 同样使用implements关键字进行实现。
- dart的接口有点奇怪,如果实现的类是普通类,会将普通类和抽象中的属性的方法全部需要覆写一遍。
- 因为抽象类可以定义抽象方法,普通类不可以,所以一般如果要实现像Java接口那样的方式,一般会使用抽象类。
- 建议使用抽象类定义接口。
abstract class Db{ //当做接口 接口:就是约定 、规范
String uri; //数据库的链接地址
add(String data);
save();
delete();
}
class Mysql implements Db{
@override
String uri;
Mysql(this.uri);
@override
add(data) {
// TODO: implement add
print('这是mysql的add方法'+data);
}
@override
delete() {
// TODO: implement delete
return null;
}
@override
save() {
// TODO: implement save
return null;
}
remove(){
}
}
main() {
Mysql mysql=new Mysql('xxxxxx');
mysql.add('1243214');
}
13.实现多个接口
如果在Dart中需要实现多个接口我们通常需要implement多个abstract类
//Dart中一个类实现多个接口:
abstract class A{
String name;
printA();
}
abstract class B{
printB();
}
class C implements A,B{
@override
String name;
@override
printA() {
print('printA');
}
@override
printB() {
// TODO: implement printB
return null;
}
}
void main(){
C c=new C();
c.printA();
}
14.mixins
mixins的中文意思是混入,就是在类中混入其他功能。在Dart中可以使用mixins实现类似多继承的功能。
因为mixins使用的条件,随着Dart版本一直在变,这里讲的是Dart2.x中使用mixins的条件:
- 作为mixins的类只能继承自Object,不能继承其他类
- 作为mixins的类不能有构造函数
- 一个类可以mixins多个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();
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();
// c.printB();
// print(c.info);
c.run();
}
九、泛型
1.泛型方法
泛型,说通俗点:泛型就是解决 类 接口 方法的复用性、以及对不特定数据类型的支持(类型校验)
举个例子:
//只能返回string类型的数据
String getData(String value){
return value;
}
//只能返回int类型的数据
int getData1(int 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<int>(12));
}
2.泛型类
泛型类和泛型方法如出一辙就是将规定的类的类型名传入从而达到控制类的类型的目的
class PrintClass<T>{
List list=new List<T>();
void add(T value){
this.list.add(value);
}
void printInfo(){
for(var i=0;i<this.list.length;i++){
print(this.list[i]);
}
}
}
main() {
PrintClass p=new PrintClass<int>();
p.add(12);
p.add(23);
p.printInfo();
}
3.泛型接口
Dart中的泛型接口:实现数据缓存的功能:有文件缓存、和内存缓存。内存缓存和文件缓存按照接口约束实现。
- 定义一个泛型接口 约束实现它的子类必须有getByKey(key) 和 setByKey(key,value)
- 要求setByKey的时候的value的类型和实例化子类的时候指定的类型一致
abstract class Cache<T>{
getByKey(String key);
void setByKey(String key, T value);
}
class FlieCache<T> implements Cache<T>{
@override
getByKey(String key) {
return null;
}
@override
void setByKey(String key, T value) {
print("我是文件缓存 把key=${key} value=${value}的数据写入到了文件中");
}
}
class MemoryCache<T> implements Cache<T>{
@override
getByKey(String key) {
return null;
}
@override
void setByKey(String key, T value) {
print("我是内存缓存 把key=${key} value=${value} -写入到了内存中");
}
}
void main(){
MemoryCache m=new MemoryCache<Map>();
m.setByKey('index', {"name":"张三","age":20});
}
十、Dart中的其他
1.Dart中的库
前面介绍Dart基础知识的时候基本上都是在一个文件里面编写Dart代码的,但实际开发中不可能这么写,模块化很重要,所以这就需要使用到库的概念。
在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; 看文档使用
2.math库
具体的math库中是一些数学方法和数学计算,在这里我就不再罗列,只举两个例子,具体可以去查看Dart的官方文档,上面会有很详细的解说,具体我我后面有时间再用一篇文章来罗列所有的math方法
import "dart:math";
main(){
print(min(12,23));
print(max(12,25));
}
3.async和await
这两个关键字的使用只需要记住两点:
- 只有async方法才能使用await关键字调用方法
- 如果调用别的async方法必须使用await关键字
!> async是让方法变成异步。
!> await是等待异步方法执行完成。
void main() async{
var result = await testAsync();
print(result);
}
//异步方法
testAsync() async{
return 'Hello async';
}
4.延迟加载/懒加载
可以在需要的时候再进行加载。懒加载的最大好处是可以减少APP的启动时间。
懒加载使用deferred as关键字来指定,如下例子所示:
import 'package:deferred/hello.dart' deferred as hello;
//当需要使用的时候,需要使用loadLibrary()方法来加载:
greet() async {
await hello.loadLibrary();
hello.printGreeting();
}
十一、结尾
好了,这里就是所有关于Dart语言的学习内容了,其实只要把我上面所有的例子都看懂,相信理解Dart语言也很方便了,如果没有看懂可以多看几次顺便敲敲代码练习一下就行,说实话,Dart还是很好理解的,下面我就开始着手写Flutter的相关学习资料了,大家可以继续关注我的博客!如果可以,打赏一下也是不错的。