引言
开发语言嘛,面向过程和面向对象。dart是面向对象的。
什么是面向对象???
面向对象
面向对象编程(OOP)的三个基本特征是:封装、继承、多态
- 封装:封装是对象和类概念的主要特性。封装,把客观事物封装成抽象的类,并且把自己的部分属性和方法提供给其他对象调用, 而一部分属性和方法则隐藏。
- 继承:面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
- 多态:允许将子类类型的指针赋值给父类类型的指针, 同一个函数调用会有不同的执行效果 。
Dart所有的东西都是对象,所有的对象都继承自Object类。
类
用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;
}
构造方法
构造方法就是在函数内部重写这个类名后面并且带上类中需要的参数,这样外部在new这个类的时候就必须向这个类传入对应的参数,从而达到外部传值的作用。
class Person{
String name;
int age;
//默认构造函数的简写
Person(this.name,this.age);
void printInfo(){
print("${this.name}----${this.age}");
}
}
命名构造函数
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();
tips: 跟kotlin的扩展函数有点像。
get/set方法
set设置属性的值,get拿取属性的值。
类中的静态成员,静态方法
在类中,实用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();
}
继承
简单继承,用extends 关键字
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中的继承我们要注意:
- 子类使用extends关键词来继承父类
- 子类会继承父类里面可见的属性和方法 但是不会继承构造函数
- 子类能复写父类的方法 getter和setter
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();
}
控制台打印:
李四---20
张三---20
张三---12
张三---12--男
实例化命名构造函数传参
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}");
}
}
子类重写父类方法
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();
}
继承是面向对象中比较重要的特征之一,在这里希望把继承的方法都记住。在开发中会受益颇深!
抽象类
Dart抽象类主要用于定义标准,子类可以继承抽象类,也可以实现抽象类接口。
注意:
- 抽象类通过abstract 关键字来定义
- Dart中的抽象方法不能用abstract声明,Dart中没有方法体的方法我们称为抽象方法。
- 如果子类继承抽象类必须得实现里面的抽象方法
- 如果把抽象类当做接口实现的话必须得实现抽象类里面定义的所有属性和方法。
- 抽象类不能被实例化,只有继承它的子类可以
extends抽象类 和 implements的区别:
- 如果要复用抽象类里面的方法,并且要用抽象方法约束自类的话我们就用extends继承抽象类
- 如果只是把抽象类当做标准的话我们就用implements实现抽象类
多态
- 允许将子类类型的指针赋值给父类类型的指针, 同一个函数调用会有不同的执行效果 。
- 子类的实例赋值给父类的引用。
- 多态就是父类定义一个方法不去实现,让继承他的子类去实现,每个子类有不同的表现。
接口
和Java一样,dart也有接口,但是和Java还是有区别的。
- dart的接口没有interface关键字定义接口,而是普通类或抽象类都可以作为接口被实现。
- 同样使用implements关键字进行实现。
- dart的接口有点奇怪,如果实现的类是普通类,会将普通类和抽象中的属性的方法全部需要覆写一遍。
- 因为抽象类可以定义抽象方法,普通类不可以,所以一般如果要实现像Java接口那样的方式,一般会使用抽象类。
- 建议使用抽象类定义接口。
实现多个接口
在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();
}
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();
}
函数
示例:
void printInfo(name,age){
print("$name,$age");
}
//调用
printInfo("leon",17)//caoxiaozhu,17
方法的作用域
如果在一个方法中调用了另外一个方法,我们通常在外部就不能直接调用那个方法内部的方法,这句话听起来比较绕口,简单来说,嵌套方法我们只能调用最外层的方法:
void xxx(){
aaa(){
print(getList());
print('aaa');
}
aaa();
}
上述就是一个简单的嵌套方法,那我们在外部调用时,我们不能直接调用xxx方法中的aaa,而只能调用xxx,我们在调用aaa时,Dart就会报错,说明我们不能跨域操作。
方法传参
在方法传参中跟我们上述介绍的自定义方法有点如出一辙,我们可以指定参数类型,也可以不指定参数类型。具体说起来我们可以将外部的数据传入方法体中进行进一步的操作。
//1、定义一个方法 求1到这个数的所有数的和
int sumNum(int n){
var sum=0;
for(var i=1;i<=n;i++)
{
sum+=i;
}
return sum;
}
//调用
sumNum(100)//求1到100的和
可选参数
我们除了上述必须传参的函数之外我们还可以创建可选参数传入,也就是说上述必传参函数我们必须要传入定义的参数,但是可选传参函数我们则可以选择性的传入需要的参数,就算不传参系统也不会报错,在可选传参函数中我们用[]
来表示可选参数的传入。
String printUserInfo(String username,[int age]){ //行参
if(age!=null){
return "姓名:$username---年龄:$age";
}
return "姓名:$username---年龄保密";
}
//age为可选参数
外部调用
print(printUserInfo('张三',21))//张三 21
print(printUserInfo('张三'))//张三
参数默认值
在开发中我们还有一种需求就是有一个参数我们可以不必每次都传入新的数据,而是有些情况下参数相对的都一样,那么我们则可以用默认值来代替某一次参数的传入。我们直接在参数后面接一个=
并且附上你需要的默认值。
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
箭头函数
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));
匿名函数
匿名函数区别于普通函数的最大的地方就是匿名函数没有函数名作为支撑。可以直接将函数附给一个变量,通过变量来执行代码块。不过在实际开发中用的比较少,如果按照规范来的话不太推荐大家用匿名函数。
var printNum=(){
print(123);
};
printNum();
自执行方法
顾名思义,自动执行的方法,但是不知道什么时候会用 @_@。。
((int n){
print(n);
print('我是自执行方法');
})(12);
闭包
首先我们来说一个场景:
- 想实现的功能:
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的值。
结束。