类与对象
- 类是通过class声明的代码段,包含属性和方法。
- 属性:用来描述类的变量
- 方法:类中的函数称为类的方法
- 对象是类的
实例化结果(var obj = new MyClass()) new的过程就是实例化
通俗点讲类与对象的关系就有点像图纸和房子的区别,类是图纸,对象就是造的房子。
class Person{
//声明类的属性
String name = '张三';
//类的方法
void getInfo(){ //类的方法可以不跟类型
print('我是$name');
}
}
void main(List<String> args) {
Person p = Person();
print(p.name);
p.getInfo();
}
要注意一点,Dart中所有的内容都是对象,对象的属性在vscode中是用一个🔧来标识,而方法是通过立方体来标识的。
构造器(构造函数)
- 默认构造函数
- 与类同名的函数,在实例化,自动被调用
- 命名构造函数
- 在类中使用命名构造器(类名.函数名)实现多个构造器,可以提供额外的清晰度
- 常量构造函数
- 如果类生成的对象不会改变,可以通过常量构造函数使这些对象成为编译时常量
- 工厂构造函数
- 通过factory声明,工厂函数不会自动生成实例,而是通过代码来决定返回的实例
代码1 默认构造函数
class Point{
num x;
num y;
Point(this.x,this.y);
}
void main(List<String> args) {
var p = Point(3, 4);
print(p.y); //4
}
代码2 命名构造函数
命名构造函数可以让一个类有多个构造函数,以此增加类的清晰度
class Point{
num x,y;
//默认坐标
Point.origin(){
x = 0;
y = 0;
}
//手动设置坐标
Point.fromJson({x:0,y:0}){
this.x = x;
this.y = y;
}
}
void main(List<String> args) {
Point p1 = new Point.origin();
print(p1.x);
Point p2 = new Point.fromJson(x:6,y:6);
print(p2.x);
}
代码3 常量构造函数
class Point{
num x;
num y;
Point(this.x,this.y);
}
class ImmutablePoint{
//属性必须通过final来申明
final num x;
final num y;
//常量构造函数必须通过const来声明
const ImmutablePoint(this.x,this.y); //常量构造函数不能有函数体
}
void main(List<String> args) {
var p1 = new Point(1,2);
var p2 = new Point(1, 2);
print(p1 == p2); //false
//常量构造函数可以当做普通构造函数使用,一旦new了,它就失去了它的意义变成一个普通的构造函数了
var p3 = new ImmutablePoint(1, 2);
var p4 = new ImmutablePoint(1, 2);
print(p3 == p4); //false
// 正确的方式是通过const (声明不可变对象必须通过const关键字)
var p5 = const ImmutablePoint(1, 2);
var p6 = const ImmutablePoint(1, 2); //这个在flutter性能优化中使用的特别多 这里const不能省略,省略了就相当于new
print(p5 == p6); //true
}
代码4 工厂构造函数
class Person{
String name;
static Person instance;
//声明一个工厂构造函数
factory Person([String name = '刘备']){
// 工厂构造函数中不能使用this关键字
// print(this.name);
if(Person.instance == null){
//第一次实例化
Person.instance = new Person.newSelf(name);
}
//非第一次实例化
return Person.instance;
}
//命名构造函数
Person.newSelf(this.name);
}
void main(List<String> args) {
//实例化操作
Person p1 = new Person('关羽');
print(p1.name); //关羽
Person p2 = new Person('张飞');
print(p2.name); //关羽
}
访问修饰符
- Dart与TS不同,没有访问修饰符(public、protected、private)
- Dart类中,默认的访问修饰符是公开的(即public)
- 如果属性或方法以_(下划线开头),则表示私有的(private)
- 只有把类单独抽离出去,私有属性和方法才起作用
//Person.dart
class Person{
String name;
//声明私有属性
num _money = 100;
Person(this.name);
num getMoney(){
return this._money;
}
//声明私有方法
void _wife(){
print('我是$name的老婆');
}
}
//private.dart
import 'lib/Person.dart';
void main(){
var p = new Person('张三');
print(p.name); //张三
//访问私有属性
// print(p._money); //与main函数在同一个作用域所以能访问,这个的话跟java差不多.
print(p.getMoney());
// print(p._wife()); 报错,私有方法
}
getter与setter
这里与java是类似的,但略有所不同,具体可以看如下的代码
class Circle{
final double PI = 3.1415926;
num r;
Circle(this.r);
//使用get声明的方法不能有(),get加载类型后,名称前
num get area{
return this.PI * this.r * this.r;
}
set setR(value){
this.r = value;
}
}
void main(List<String> args) {
var c = Circle(10);
print(c.area); //声明了get以后,访问方法要像访问属性一样了,不能加()
//通过Setter修改属性
c.setR = 20;
print(c.area);
}
初始化列表
- 作用:在构造函数中设置属性的默认值
我们想在类中给所有创建的对象一个默认值,可以按照下面的代码来操作
class Rect{
int height;
int width;
Rect([int height = 2,int width = 10]){
this.height =height;
this.width = width;
}
}
void main(List<String> args) {
var r = Rect();
print(r.height);
}
Dart给我们提供了一个语法糖
class Rect{
int height;
int width;
// Rect([int height = 2,int width = 10]){
// this.height =height;
// this.width = width;
// }
Rect():height = 2, width = 10{
}
}
void main(List<String> args) {
var r = Rect();
print(r.height);
}
初始化列表的特殊用法(重定向构造函数)
class Point{
double x,y,z;
Point(this.x,this.y,this.z);
Point.twoD(double x, double y) : this(x,y,0);
}
void main(List<String> args) {
//实例化点
var p = Point(1,2,3);
print(p.z); //3.0
var p2 = Point.twoD(4,5);
print(p2.z); //0.0
}
static关键字
-
static关键字用来指定静态成员
- 通过static 修饰的属性是静态属性
- 通过static修饰的方法 是静态方法
-
静态成员可以通过类名称直接访问(不需要实例化)
- 实例化是比较消耗资源的,声明静态成员,可以提高程序性能
-
静态方法不能访问非静态成员,非静态方法可以访问静态成员
- 静态方法中不能使用this
- 不能使用this关键字,访问静态属性
class Person{
static String name = '张三';
static void printInfo(){
int age = 18; //静态方法不能访问非静态属性
// print(this.name); //不能通过this访问静态属性
print(name);
print(age); //int age = 18; //静态方法不能访问非静态属性
}
printUserInfo(){
//非静态方法,可以访问静态属性
print(name);
}
}
void main(List<String> args) {
//静态成员,可以通过类名称直接访问
print(Person.name);
Person.printInfo();
}
metadata元数据
- 元数据以@开头,可以给代码标记一些额外的信息
- 元数据可以用来重写库,类,构造器,字段,参数等
- @override(重写)
- 某方法添加注释后,表示重写了父类中的同名方法
- @required(必填)
- 可以通过@ required来注解Dart中的命名参数,用来表示它是必填参数
- @deprecated(弃用)
- 若某类或某方法加上该注解之后,表示此方法或类不再建议使用 演示deprecated
class Phone {
//这是旧版本中的开机方法,会在将来的版本中移除
@deprecated
activate(){
turnOn(); //提升用户的体验
}
turnOn(){
print('开机');
}
}
void main(List<String> args) {
var p = new Phone();
p.activate();
p.turnOn();
}
面向对象的继承
- 子类通过extends关键字来继承父类
- 继承后,子类可以使用父类中,可见的内容(属性或方法)
- 子类中,可以通过@ override 元数据来标记“覆写”方法
- 覆写方法:子类中与父类同名的方法
- 子类中,可以通过super关键字来引用父类中可见(私有的不行)的内容,如属性,方法,普通构造函数,命名构造函数。
- 子类若想访问父类的方法,需要通过get与set的形式。
class Father{
String name = '刘备';
num money = 10000;
say(){
print('我是$name');
}
}
class Son extends Father{
@override //并不会对程序有任何影响,只是做一个标记,官方建议我们写上
//重写父方法
say(){
print('我是刘禅');
}
}
void main(List<String> args) {
var f = new Father();
print(f.name);
var s = new Son();
print(s.name);
print(s.money);
s.say();
}
class Father{
String name = '刘备';
num money = 10000;
int _age = 55; //private私有变量不能被继承
String job;
Father(this.job);
say(){
print('我是$name');
}
}
class Son extends Father{
@override //并不会对程序有任何影响,只是做一个标记,官方建议我们写上
//重写父方法
say(){
print('我是刘禅,我继承了${super.money} 那么多钱'); //通过super调用父类的属性
}
Son(String job):super(job); //子类可以继承构造函数
}
void main(List<String> args) {
var son1 = Son('皇帝');
son1.say();
}
抽象类
- 抽象类是用
abstract关键字修饰的类。 - 抽象类的作用是充当普通类的模板,约定一些必要的属性和方法。
- 抽象方法是指没有方法体的方法-
- 抽象类中一般都有抽象方法,也可以没有抽象方法
- 普通类中,不能有抽象方法
- 抽象类不能被实例化(不能被new)
- 抽象类可以被普通类继承(extends)
- 如果普通类继承抽象类,必须实现抽象类中的所有抽象方法
- 抽象类还可以充当接口被实现(implements)
- 如果抽象类当做接口实现的话,普通类必须得实现抽象类里面定义的所有属性和方法。
abstract class Phone{
//
//声明抽象方法
void processor(); //标记为手机的处理器
void camera(); //标记为手机的摄像头
void info(){
print('我是抽象类中的一个普通方法');
}
}
class Xiaomi extends Phone{
// 普通类继承了抽象类,就必须实现抽象类中所有的抽象方法 (实现就类似重写的意思)
@override
void processor(){
print('晓龙888处理器');
}
@override
void camera(){
print('三星摄像头');
}
}
class Huawei extends Phone{
// 普通类继承了抽象类,就必须实现抽象类中所有的抽象方法 (实现就类似重写的意思)
@override
void processor(){
print('麒麟990');
}
@override
void camera(){
print('莱卡摄像头');
}
// void aaa(); //普通类是不能这样只给方法名不给方法体的
}
void main(List<String> args) {
//抽象类,不能被实例化
// var p1 = new Phone(); 直接报错
Xiaomi m = new Xiaomi();
m.processor();
m.camera();
m.info();
Huawei h = new Huawei();
h.camera();
}
dart中的接口
- 接口在Dart中就是一个类(只是用法不同)
- 与java不同,java中的接口需要用interface关键字声明,dart中不需要
- 接口可以是任意类,但一般使用抽象类做接口
- 一个类可以实现(implements)多个接口,多个接口用逗号分隔。
- class MyClass implements interface1,interface22{...}
- 接口可以看成一个一个小零件。类实现接口就相当于组装零件
- 普通类实现接口后,必须重写接口中所有的属性和方法。
//手机的处理器
abstract class Processor {
String cores; //内核:双核,4核
arch(String name); //芯片的制程:7mm,
}
abstract class Camera{
String resolution; //摄像头的分辨率:1KW 3KW 1E
brand(String name); //品牌:三星,莱卡,蔡司
}
//通过普通类来实现接口
class Phone implements Processor,Camera{ //必须实现接口中所有的属性和方法
@override
String cores;
@override
String resolution;
Phone(this.cores,this.resolution);
@override
arch(String name) {
print('芯片制程 $name ');
}
@override
brand(String name) {
print('相机品牌 $name');
}
}
void main(List<String> args) {
Phone p = new Phone('双核', '1kW'); //相机品牌 三星
p.brand('三星');
print(p.cores); //双核
}
mixin混入
-
混入(Mixin)是一段公共代码(我的理解就是实现多继承)。混入有两种声明方式:
- 将类当作混入class MixinA{...}
- 作为Mixin的类只能继承自Object,不能继承其他类
- 作为Mixin的类不能有构造函数
-
使用mixin关键字声明mixin MixinB{...}
-
混入(Mixin)可以提高代码的复用效率(Dart是单继承),普通类可以通过
with来使用混入- class MyClass with MinxinA,MixinB{...}
-
使用多个混入时,后引入的混入会覆盖之前混入中的重复的内容
- MixinA和MixinB中都有hello()方法,MyClass会使用MixinB中的
class Father{
}
class MixinA{
String name = 'MixinA';
void printA(){
print('A');
}
//一旦用了引入就不能用构造函数
// MixinA() 用作混入的类,不能拥有构造函数
}
class MixinB{
String name = 'MixinB';
void printB(){
print('B');
}
}
class MyClass with MixinA,MixinB{ //Mixin中的类不能继承除了Object以外别的类,不然会报错.
}
void main(List<String> args) {
var c = new MyClass();
c.printA();
c.printB();
//后引入的混入,会覆盖之前引入的混入中重复的内容
print(c.name); //MixinB 方法也是一样这里我就不演示了.
}
泛型
- 泛型是在函数、类、接口中制定
宽泛数据类型的语法。通常,在尖括号中,使用一个字母来代表类型,例如E,T,S,K和V等。使用泛型可以帮助我们减少重复的代码。
泛型函数
// String getData(String value){
// return value;
// }
//泛型函数
// T getData<T>(T value){
// return value;
// }
//只约定参数类型,不约定函数返回值的类型
getData<T>(T value){
return value;
}
void main(List<String> args) {
// print(getData("hello"));
print(getData<int>(29));
print(getData<String>('hello'));
}
泛型类
class CommonClass{
Set s = new Set<int>();
void add(int value){
this.s.add(value);
}
void info(){
print(this.s);
}
}
//泛型类
class GenericClass<T>{
Set s = new Set<T>();
void add(T value){ //泛型放在实参这
this.s.add(value);
}
void info(){
print(this.s);
}
}
void main(List<String> args) {
CommonClass c = new CommonClass();
c.add(1);
c.add(2); //如果我这波新增 '2'就会报错
c.info(); //{1, 2}
GenericClass g = new GenericClass<int>();
g.add(1);
g.add(2);//如果我这波新增 '2'就会报错
g.info();
//字面量形式的泛型
Set s2 = <int>{};
s2.add(1);
print(s2);
}
泛型接口
//泛型接口
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);
}
class FileCache<T> implements Cache<T>{ //光标移动到FileCache上,快速修复
@override
getByKey(String key) {
// TODO: implement getByKey
throw UnimplementedError();
}
@override
void setByKey(String key, T value) {
// TODO: implement setByKey
print('文件缓存: key=${key} value=${value}');
}
}
void main(List<String> args) {
//文件缓存 - 缓存字符串
// FileCache fc = new FileCache<String>();
// fc.setByKey('foo', 'bar');
//文件缓存 - 缓存 Map
FileCache fc = new FileCache<Map>();
fc.setByKey('index', {"name":"zsf","age":18});
}
泛型类型限制
class SomeBaseClass{
//...
}
class Foo<T extends SomeBaseClass>{
String toString() => "Instance of 'Foo<$T>'";
}
class extender extends SomeBaseClass{
//...
}
class AnotherClass{
//...
}
void main(List<String> args) {
var SomeBaseClassFoo = Foo<SomeBaseClass>();
print(SomeBaseClassFoo);
// var f = Foo<AnotherClass>(); //类型不对直接报错
// print(f);
var f = Foo<extender>();
print(f); //不报错
}
枚举
- 枚举是数量固定的常量值,通过enum关键字声明
- enum Color {red,green,blue}
- 枚举的values常量,可以获取所有枚举值列表
List <Color> colors = Color.values
enum Color {red,green,blue} //不加分号
void main(List<String> args) {
//通过 index 返回枚举中具体常量的值
print(Color.green.index); //1
//通过values返回常量列表
print(Color.values); //[Color.red, Color.green, Color.blue]
List <Color> colors = Color.values;
print(colors); //[Color.red, Color.green, Color.blue]
//通过下标,访问列表中的内容
print(colors[0]); //Color.red
//通过forEach,去遍历列表的内容
colors.forEach((element) {
print('value $element,index ${element.index}');
});
}