类与对象
简介
类是通过 class 声明的代码段,包含属性和方法。
- 属性:用来描述类的变量
- 方法:类中的函数称为类的方法
对象是类的实例化结果
var obj = new MyClass ( )
类是抽象,对象是实例
class person { String name = ""; void get_info() { print("this is $name"); } } void main() { person p = new person(); p.name = "zhangsan"; p.get_info(); }
构造函数
- 起初始化类变量的作用,有多种构造函数
默认构造函数:与类名相同,没有返回值,在实例化对象时默认被调用
class Point { num x = 0, y = 0; Point(x, y) { this.x = x; this.y = y * x; print("default constructor function is aclled.x:${this.x},y:${this.y}"); } Point(this.x, this.y); //简化的写法 } void main() { Point p = new Point(10, 20); }
命名构造函数:在类中使用命名构造器 (类名.函数名)实现多个构造函数,可以提供额外的初始化方式
class Point { num x = 0, y = 0; Point.special_point() { this.x = 100; this.y = 100; } } void main() { Point p = new Point.special_point(); //调用命名构造函数 print(p.x); }
常量构造函数:如果类生成的对象不会改变,可以通过常量构造函数使这些对象成为编译时常量
使用常量的对象可以提高flutter渲染性能
class const_Point { //变量使用final定义、 final x, y; //常量构造函数使用const定义,并且不能有函数体 const const_Point(this.x, this.y); } void main() { //必须使用const关键字才能实例化常量对象 var p1 = const const_Point(1, 2); }
工厂构造函数:通过 factory声明,工厂函数不会自动生成实例,而是通过代码来决定返回的实例
工厂构造函数不能进行实例化操作,也不能使用this关键字
程序每次实例化消耗的资源都很大,工厂构造函数只会实例化一次,其实与单例模式差不多
class Persion { String name; static Persion? instance; factory Persion([String name = "zhangsan"]) { if (instance == null) { //第一次实例化 instance = new Persion.newself(name); } return instance!; } Persion.newself(this.name); } void main(List<String> args) { //p1,p2是相同的 var p1 = new Persion("lisi"); print(p1.name); var p2 = new Persion("zhangsan"); print(p2.name); }
访问修饰
没有 public、protected、private等访问修饰符关键字,默认都是public;
如果属性或方法以 ’_‘(下划线)开头,则表示私有(即 private);
只有把类单独抽离出去,私有属性和方法才生效,例如 一个文件为 persion.dart,并且import
class persion { String name = "zhangsan"; num _money = 0; //声明私有属性 persion(this.name); } void main(List<String> args) { var p1 = new persion("lisi"); print(p1._money); //仍然可以访问私有,因为ipersion类和main函数在用一个作用域 } // 如此就可以生效,可以对外提供公共方法访问私有属性 import 'lib/persion.dart'; void main(List<String> args) { var p1 = new persion("lisi"); print(p1._money); //编译报错,无法访问 }
Getter与Setter
Getter (获取器)是通过 get 关键字修饰的方法
- 函数没有小括号,访问时也没有小括号(像访问属性一样访问方法)
Setter (修改器)是通过 set 关键字修饰的方法
- 访问时,像设置属性一样给函数传参
其实就相当于更明确的提供了一组get和set的接口
import 'dart:ffi';
class circle {
final double pi = 3.14;
num r = 0;circle(this.r);
void setr(var r) {
this.r = r;
}
num area() {
return this.pi * this.r * this.r;
}
//Getter
num get area1 {return this.pi * this.r * this.r;
}
//Setter
void set setr1(var r) {this.r = r;
}
}void main(List<String> args) {
var c1 = new circle(10);
print(c1.area()); //使用内部get方法
print(c1.area1); //使用getter属性c1.setr(1); //使用内部set方法
c1.setr1 = 100; //使用serter
print(c1.area1);
}
初始化列表
作用:在构造函数中设置属性的默认值
时机:在构造函数体执行之前执行
语法:使用逗号分隔初始化表达式
场景: 常用于设置 final 常量的值
其实可以使用 [ ],可选参数来实现相同的效果
class rect { int height, weight; rect() : height = 10, weight = 20 { print("${this.height} -- ${this.weight}"); } } void main(List<String> args) { var r1 = new rect(); }
Static
static关键字用来指定静态成员
- 通过 static 修饰的属性是静态属性
- 通过 static 修饰的方法是静态方法
静态成员可以通过类名称直接访问(不需要实例化)
- 实例化是比较消耗资源的,声明静态成员,可以提高程序性能
- 静态方法不能访问非静态成员, 非静态方法可以访问静态成员
- 静态方法中不能使用 this 关键字
- 不能使用 this 关键字,访问静态属性
实例化对象不能直接访问静态成员
元数据
元数据以 @开头,可以给代码标记一些额外的信息,相当于注释,对代码实际运行没有影响
元数据可以用来库,类,构造器,函数,字段,参数或变量声明的前面
@ override (重写)
- 某方法添加该注解后,表示重写了父类中的同名方法
@required (必填)
- 可以通过 @ required 来注解 Dart 中的命名参数,用来指示它是必填参数
@ deprecated (弃用)
- 若某类或某方法加上该注解之后,表示此方法或类不再建议使用
继承
根据类的先后顺序,可以将类分成父类和子类
子类通过 extends 关键字 继承 父类,并且只能单继承
- 继承后,子类可以使用父类中,可见的内容 (属性或方法)
子类中,可以通过 @override 元数据来标记“覆写"方法
- “覆写”方法:子类中与父类中同名的方法,会调用子类的
子类中,可以通过 super 关键字来引用父类中,可见的内容,属性为public的
- 属性
- 方法 (普通构造函数,命名构造函数,以及其他方法)
抽象类
抽象类是用 abstract关键字修饰的类
抽象类的作用是充当普通类的模板,约定一些必要的属性和方法,充当模板以及接口
抽象方法是指没有方法体的方法
- 抽象类中一般都有抽象方法,也可以没有抽象方法
- 普通类中,不能有抽象方法
抽象类不能被实例化(不能被 new)
抽象类可以被普通类继承 (extends)
- 如果普通类继承抽象类,必须实现抽象类中所有的抽象方法
抽象类还可以充当接口被实现 (implements)
- 如果把抽象类当做接口实现的话,普通类必须得实现抽象类里面定义的所有属性和方法。
abstract class phone {
var cpu;
var screen;void processer(); //抽象方法没有函数体实现
void camear();
}class xiaomi extends phone {
//普通类继承,必须按约束实现所有抽象方法
@override
void processer() {print("this is xiaomi xiaolong 888 cpu");
}
@override
void camear() {print("this is xiaomi 1e camera");
}
}
接口
接口在 Dart 中就是一个类 (只是用法不同)
- 接口可以是任意类,但一般使用抽象类做接口
一个类可以实现 (implements)多个接口,多个接口用逗号分隔
- class MyClass implements Interface1, Interface2 { ... }
接口可以看成一个个小零件,类实现接口就相当于组装零件
- 普通类实现接口后,必须重写接口中所有的属性和方法
// 硬件接口
abstract class hdware {
String ipf = "";
String chip = "";void hd_comp(String ipf, String chip);
}// 软件接口
abstract class software {
String ver = "";
String size = "";void soft_comp(String ver, String size);
}// 通过普通类实现接口,实现所有抽象属性和方法,相当于把所有的组件拼装起来
class phone implements hdware, software {
@override
String ipf = "";
String chip = "";
String ver = "";
String size = "";phone(this.chip, this.ipf, this.ver, this.size);
@override
void hd_comp(String ipf, String chip) {print("this hd comp is ipf $ipf, chip $chip");
}
@override
void soft_comp(String ver, String size) {print("this soft comp is ver $ver, size $size");
}
}
混入
混入(Mixin)是一段公共代码。混入有两种声明方式:
将类当作混入 mixin class MixinA { ..}
- mixin class既可以当作普通类使用,也可以当作mixin使用
使用 mixin 关键字声明 mixin Mixin B { ..}
混入(Mixin)可以提高代码复用的效率,解决了只能单继承的问题,普通类可以通过 with 来使用混入
class MyClass with MixinA, Mixin B { .. }
使用多个混入时,后引入的混入会覆盖之前混入中的重复的内容
- MixinA 和 MixinB 中都有 hello() 方法, MyClass 会使用 Mixin B 中的
//混入的声明方法1
mixin class mixa {
String name = "mixA,class";void printA() {
print("A");
}
}//混入的声明方法2
mixin mixb {
String name = "mixB,mixin";void printB() {
print("B");
}
}//使用混入
class myclass with mixa, mixb {}class child extends mixa {}
void main(List<String> args) {
var c = myclass();
print(c.name);//同时使用A和B的代码内容
c.printA();
c.printB();var chi = new child();
chi.printA();
}
泛型
在函数、类、接口中指定宽泛数据类型的语法,作用在传入参数未知且多变时,对应的返回相应类型的数据,减少代码重复
//泛型函数 T getdata<T>(T val1,T val2......) { return val; } getdata<int>(10); //则T变为int getdata<String>("hello"); //则T变为String
//泛型类 class generic<T> { Set myset = new Set<T>(); void add(T value) { this.myset.add(value); } void info() { print(myset); } } void main(List<String> args) { var g = new generic<int>(); g.add(2); g.add("3"); //报错,g的数据类型只能是int g.info(); }
//泛型接口 //缓存接口 abstract class cache<T> { getbykey(String key); void setbykey(String key, T val); } //文件缓存实现 class filecache<T> implements cache<T> { @override getbykey(String key) { print("this is file cache"); return null; } @override setbykey(String key, T val) { print("this is key $key,val $val"); } } //内存缓存实现 class memcache<T> implements cache<T> { @override getbykey(String key) { print("this is file cache"); return null; } @override setbykey(String key, T val) { print("this is key $key,val $val"); } } void main(List<String> args) { var fc = new filecache<int>(); //缓存int类型 var fc2 = new filecache<Set>(); //缓存set类型 fc.setbykey("1", 50); fc2.setbykey("key1", new Set()); }
泛型类型为T时,可以传入任何类型,我们可以限制其只能传入某些特定类型
//泛型限制 class basic {} class myclass<T extends basic> { //限制T为basic以及其子类 info() { print("this is $T"); } } class ext1 extends basic {} class otherclass {} void main(List<String> args) { var f1 = new myclass<basic>(); f1.info(); //this is basic var f2 = new myclass<ext1>(); f2.info(); //this is ext1 var f3 = new myclass<int>(); //编译报错,因为限制了T只能是basic以及其子类,不能传入其他类,int不是basic的子类 var f4 = new myclass<otherclass>(); }
枚举
枚举定义一组有意义的常量
通过values获取所有枚举值列表
通过index获取枚举值对应的序号
enum chip { armv7, armv8, aarch64, x86 } void main(List<String> args) { print(chip.values); print(chip.aarch64.index); //将其转换为列表 List<chip> chiplist = chip.values; . //通过list的下标访问 print(chiplist[2]); //通过列表的foreach访问,foreach是匿名函数 chiplist.forEach((val) { print("this is list val $val"); }); }