Dart 也是一个面向对象的语言。
Dart里面所有的东西都是对象,所有的东西都继承于Object。

基本写法

什么是对象

对象就是属性和方法的集合。
List Set Map

  1. var list1 = List();
  2. list1.add(); //方法
  3. list1.length; //属性

Dart 里面类怎么声明
  1. // 声明不要放在main函数里面
  2. // 首字母必须大写
  3. class Person {
  4. String name = '张三';
  5. int age = 18;
  6. showName() {
  7. print('我的名字叫${this.name}');
  8. }
  9. }
  10. void main() {
  11. var p1 = Person(); //new 可以省略
  12. p1.showName(); //我的名字叫张三
  13. }

构造函数的使用

Dart中的类与Java中的相似,不同的是,Dart中没有privatepublic这些成员访问修饰符。如果是类私有的成员,不希望外面访问,只需要在成员变量之前加上一个下划线_变为私有即可。

另外注意,Dart中没有构造方法的重载,不能写两个同名的构造方法。

  1. class Person {
  2. String name;
  3. int age;
  4. // 类似js的 constructor ,构造函数名字和类型要一致。只能声明一个
  5. Person(name, age) {
  6. this.name = name;
  7. this.age = age;
  8. }
  9. show() {
  10. print('${this.name} -- ${this.age}');
  11. }
  12. }
  13. void main() {
  14. var p1 = Person('张三', 18);
  15. var p2 = Person('李四', 20);
  16. p1.show(); //张三 -- 18
  17. p2.show(); //李四 -- 20
  18. }

简写

  1. class Person {
  2. String name;
  3. int age;
  4. String province;
  5. String city;
  6. Person(
  7. this.name, {
  8. this.age,
  9. this.sex = 'male',
  10. });
  11. show() {
  12. print('${this.name} -- ${this.age} -- ${this.sex}');
  13. }
  14. }
  15. void main() {
  16. var p = Person('Jack', sex: 'female');
  17. p.show(); //Jack -- null -- female
  18. }

常量构造方法

如果想提供一个状态永远不变的对像,在Dart中,我们可以创建一个编译时常量对象,节省开销。

  1. class ConstPoint {
  2. final num x;
  3. final num y;
  4. // 使用const修构造方法
  5. const ConstPoint(this.x, this.y);
  6. // 编译时常量对象,需使用const来创建对象
  7. static final ConstPoint origin = const ConstPoint(0, 0);
  8. }
  9. void main() {
  10. print(ConstPoint.origin.x); //0
  11. print(ConstPoint.origin.y); //0
  12. }

命名构造函数(命名构造方法)

Dart类中两个同名构造方法不能重载,但是Dart语言为类新增了一种称为命名构造方法的东西。
例如 DateTime.now() .

使用命名构造方法可以为一个类实现多个构造方法,也可以更清晰的表明意图。

  1. class Person {
  2. String name;
  3. int age;
  4. String sex;
  5. String province;
  6. String city;
  7. Person(this.name, {this.age, this.sex = 'male'});
  8. // 命名构造函数(自定义构造函数),可以声明多个
  9. // Person.fromData(Map data) {
  10. // this.province = data['province'];
  11. // this.city = data['city'];
  12. // }
  13. Person.getPosition({
  14. this.province = '北京',
  15. this.city,
  16. });
  17. }
  18. void main() {
  19. // var p = new Person.getPosition({'province': '河南', 'city': '漯河'});
  20. // print('${p.province} -- ${p.city}'); //河南 -- 漯河
  21. var p = Person.getPosition(city: '丰台');
  22. print('${p.province} -- ${p.city}'); //北京 -- 丰台
  23. }

构造方法重定向

有时候一个构造方法会调动类中的其他构造方法来实例化,这时候可以使用构造方法重定向。

  1. class Alignment {
  2. final double x;
  3. final double y;
  4. const Alignment(this.x, this.y)
  5. : assert(x != null),
  6. assert(y != null);
  7. static const Alignment topLeft = Alignment(-1.0, -1.0);
  8. // 命名构造方法 重定向 到同名构造方法,中间使用一个冒号
  9. Alignment.bottomTargetX(x) : this(x, -1);
  10. show() {
  11. print('$x -- $y');
  12. }
  13. }
  14. void main() {
  15. Alignment(0.0, 0.0).show(); //0.0 -- 0.0
  16. Alignment.topLeft.show(); //-1.0 -- -1.0
  17. Alignment.bottomTargetX(-0.5).show(); //-0.5 -- -1.0
  18. }

工厂构造方法

有时候可能有一种需求,并不需要每次都创建新的类实例,而是每一种情况,只需要一个实例。

工厂构造函数是一种构造函数,与普通构造函数不同,工厂函数不会自动生成实例,而是通过代码来决定返回的实例对象.
工厂构造函数的关键字为factory,下面我们用工厂构造函数写一个只能创造一个实例的类.

  • 工厂构造函数类似于static静态成员,无法访问this指针,所以在工厂构造函数中能处理的任务较为有限.
  • 使用工厂构造函数时往往需要定义一个命名构造函数用来生产实例.

示例1
  1. class A {
  2. String name;
  3. // 为 A类 创建名为 cache 的静态成员,它将缓存A类的第一个实例.
  4. static A cache;
  5. // 使用关键字factory修饰类同名构造方法
  6. factory A([String name = 'A']) {
  7. // 在工厂构造函数A中,首先判断A.cache是否已经存在实例(即判断是否是第一次实例化),
  8. // 如果存在则返回缓存的实例,不存在则新建一个实例并缓存.
  9. if (A.cache == null) {
  10. A.cache = new A.newObject(name);
  11. }
  12. return A.cache;
  13. }
  14. // A.newObject是一个命名构造函数,用于创建实例.
  15. A.newObject(this.name);
  16. }
  17. void main() {
  18. A a = new A('hello');
  19. print(a.name); //hello
  20. A b = new A('world');
  21. print(b.name); //hello
  22. print(a == b); //true 说明变量a和b指向的是同一个实例对象.
  23. }

示例2

当我们需要创建一个新的对象或者从缓存中取一个对象时,工厂构造方法就派上了用场。

  1. class Logger {
  2. final String name;
  3. // 创建一个静态Map做为缓存
  4. static final Map<String, Logger> _cache = <String, Logger>{};
  5. // 定义一个命名构造方法,用下划线"_"修饰,将构造方法私有化
  6. Logger._internal(this.name);
  7. // 使用关键字factory修饰类同名构造方法
  8. factory Logger(String name) {
  9. if (_cache.containsKey(name)) {
  10. return _cache[name];
  11. } else {
  12. // 调用命名构造方法创建新对象
  13. final logger = Logger._internal(name);
  14. _cache[name] = logger; // 存入缓存
  15. return logger;
  16. }
  17. }
  18. }
  19. void main() {
  20. var uiLog = new Logger('UI');
  21. var eventLog = new Logger('event');
  22. }

单例模式

方法1:
  1. class Demo {
  2. //命名构造函数
  3. Demo._internal() {}
  4. //保存单例
  5. static var _instance = Demo._internal();
  6. //工厂构造函数
  7. factory Demo() => _instance;
  8. }
  9. //使用
  10. var d1 = Demo();
  11. var d2 = Demo();
  12. print(d1 == d2); //true

方法2:
  1. class Demo {
  2. //命名构造函数
  3. Demo._internal() {}
  4. //保存单例
  5. static var _instance = Demo._internal();
  6. // 获取单例
  7. // tatic getInstance = _instance;
  8. static getInstance({refresh = false}) {
  9. refresh == true ? _instance._init() : _instance._next();
  10. }
  11. _init() {
  12. //refresh为true时,做一些事情后,再返回该单例对象
  13. print('_init');
  14. return this;
  15. }
  16. _next() {
  17. //refresh为false时,做一些事情后,再返回该单例对象
  18. print('_next');
  19. return this;
  20. }
  21. }
  22. //使用
  23. var d1 = Demo.getInstance(refresh: true); //_init
  24. var d2 = Demo.getInstance(); //_next
  25. print(d1 == d2); //true

初始化列表(默认值)

  • 初始化列表位于构造方法的小括号与大括号之间,在初始化列表之前需添加一个冒号。
  • 初始化列表是由逗号分隔的一些赋值语句组成。
  • 它适合用来初始化 final修饰的变量
  • 初始化列表的调用是在构造方法之前,也就是在类完成实例化之前,因此初始化列表中是不能访问 this

正常实例化

  1. class A {
  2. //实例化方法-初始化赋值
  3. A([
  4. this.name = 'jack',
  5. this.age,
  6. this.sex = 'male',
  7. ]);
  8. final String name;
  9. final int age;
  10. final String sex;
  11. show() => print('${this.name} -- ${this.age} -- ${this.sex}');
  12. }
  13. void main() {
  14. A('jack', 18).show(); //jack -- 18 -- male
  15. }
  1. class A {
  2. A({
  3. this.name = 'jack',
  4. this.age,
  5. this.sex: 'male',
  6. });
  7. final String name;
  8. final int age;
  9. final String sex;
  10. show() => print('${this.name} -- ${this.age} -- ${this.sex}');
  11. }
  12. void main() {
  13. A(age: 18).show(); //jack -- 18 -- male
  14. }

命名构造函数实例化

  1. class A {
  2. A.now({
  3. this.name = 'jack',
  4. this.age = 18,
  5. });
  6. final String name;
  7. final int age;
  8. show() => print('${this.name} -- ${this.age}');
  9. }
  10. void main() {
  11. A.now().show(); //jack -- 18
  12. }
  1. class A {
  2. A.now({name = 'jack', age = 18})
  3. : this.name = name, //this可省略
  4. age = age;
  5. final String name;
  6. final int age;
  7. show() => print('${this.name} -- ${this.age}');
  8. }
  9. void main() {
  10. A.now(age: 20).show(); //jack -- 20
  11. }

运算符重载

这个特性,又很类似于C++中的运算符重载,在Java中是没用这种概念的。

  1. class Point {
  2. int x;
  3. int y;
  4. Point(this.x, this.y);
  5. // 使用operator关键字,为该类重载"+"运算符
  6. Point operator +(Point p) {
  7. return new Point(this.x + p.x, this.y + p.y);
  8. }
  9. // 为该类重载"-"运算符
  10. Point operator -(Point p) {
  11. return new Point(this.x - p.x, this.y - p.y);
  12. }
  13. }
  14. void main() {
  15. var p1 = new Point(1, 5);
  16. var p2 = new Point(7, 10);
  17. // 重载运算符后,类可以使用“+”、“-” 运算符操作
  18. var p3 = p1 + p2;
  19. var p4 = p2 - p1;
  20. print("${p3.x}, ${p3.y}"); //8, 15
  21. print("${p4.x}, ${p4.y}"); //6, 5
  22. }

Dart中允许重载的运算符如下:

+ * ~/ / % ^
< > <= >= == [] []=
& ~ << >> |

getter、setter

getter和setter 是用于对象属性读和写的特殊方法。
每个实例变量都有一个隐士getter,通常情况下还会有一个setter。
使用get和set关键字实现getter和setter,能够为实例创建额外的属性。

  1. class A {
  2. A(this.name, this.age);
  3. final String name;
  4. int age;
  5. getAge() => age;
  6. addAge(int val) {
  7. age += val;
  8. }
  9. }
  10. void main() {
  11. var s1 = A('jack', 18);
  12. print(s1.getAge()); //18
  13. s1.addAge(5);
  14. print(s1.getAge()); //23
  15. }

改:

  1. class A {
  2. A(this.name, this.age);
  3. final String name;
  4. int age;
  5. get getAge => age;
  6. // 等同于
  7. // get getAge { return age; }
  8. set addAge(int val) {
  9. age += val;
  10. }
  11. }
  12. void main() {
  13. var s1 = A('jack', 18);
  14. print(s1.getAge); //18
  15. s1.addAge = 5;
  16. print(s1.getAge); //23
  17. }

私有属性和私有方法

js-私有属性:https://www.yuque.com/zhuchaoyang/wrif6k/ewwgff#ukD3f
私有方法和私有属性,是只能在类的内部访问的方法和属性,外部不能访问。
使用 _xxx 来定义私有属性和方法。

  1. //注意:要使用私有属性和方法,类必须分离出去。
  2. //Person.dart
  3. class Person {
  4. String name;
  5. int _age; //私有属性
  6. Person(this.name, this._age);
  7. show() {
  8. print('${this.name} -- ${this._age}');
  9. }
  10. // 私有方法
  11. _showPrivate() {}
  12. }
  13. //1.dart
  14. import './Person.dart';
  15. void main() {
  16. var p1 = Person('张三', 18);
  17. print(p1.name); //张三
  18. // print(p1._age); //报错
  19. p1.show(); //张三 -- 18 内部可以使用私有属性和私有方法
  20. // p1._showPrivate(); //报错
  21. }

静态的属性和方法

js-静态方法:https://www.yuque.com/zhuchaoyang/wrif6k/ewwgff#QHBZy

  • 静态的属性和方法是通过 static 关键字来实现的。
  • 静态变量(类变量)对于类级别的状态是非常有用的,可以直接通过类来访问不需要实例化。
  • 静态变量只到它们被使用的时候才初始化。
  • 静态方法(类方法)不能在实例上使用,因此他们不能访问 this
  • 静态函数可以当做编译时常量使用。例如,可以将静态方法作为参数传递给常量构造函数。
  • 静态方法不能访问非静态的成员,非静态方法可以访问静态的成员。
  1. class Person {
  2. String name;
  3. static int age = 12; //静态属性
  4. Person(this.name);
  5. // 实例方法
  6. show() {
  7. print(this.name);
  8. print(age); //非静态方法可以访问静态的成员。
  9. }
  10. // 静态方法
  11. static showStatic() {
  12. // print(this.name); //报错:静态方法不能访问非静态的成员
  13. print(age);
  14. }
  15. }
  16. void main() {
  17. var p1 = Person('张三');
  18. print(Person.age); //12 直接通过类来访问不需要实例化
  19. Person.age = 10;
  20. print(Person.age); //10
  21. Person.showStatic(); //10
  22. p1.show(); //张三 10
  23. }

继承(扩展类)

js-类的继承:https://www.yuque.com/zhuchaoyang/wrif6k/iinaz1

继承父类的构造函数:
  1. class Parent {
  2. Parent(this.name, this.age);
  3. final int age;
  4. final String name;
  5. show() {
  6. print('${this.name} -- ${this.age}');
  7. }
  8. }
  9. // class Child extends Parent {} //报错:如果只是用extends,不能继承构造函数中的东西
  10. class Child extends Parent {
  11. Child(name, age) : super(name, age); //通过初始化列表,使用super引用父级的构造函数
  12. }
  13. void main() {
  14. var p1 = Parent('张三', 18);
  15. p1.show(); //张三 -- 18
  16. var c1 = Child('李四', 20);
  17. c1.show(); //李四 -- 20
  18. }

继承并且扩展自己:
  1. class Child extends Parent {
  2. // name age 继承自父组件;sex是子组件扩展的参数
  3. Child(name, age, this.sex) : super(name, age);
  4. final String sex;
  5. showSex() {
  6. print(this.sex);
  7. }
  8. }
  9. void main() {
  10. var c1 = Child('李四', 20, 'male');
  11. c1.show(); //李四 -- 20
  12. c1.showSex(); //male
  13. }
  1. class Parent {
  2. Parent({this.key});
  3. final String key;
  4. show() => print(this.key);
  5. }
  6. class Child extends Parent {
  7. Child({
  8. key,
  9. this.title,
  10. }) : assert(title != null),
  11. super(key: key);
  12. final String title;
  13. aa() => print('${this.key} -- ${key} -- ${this.title} -- ${title}');
  14. }
  15. void main() {
  16. var c1 = Child(key: '我是组件key', title: '我是标题');
  17. c1.show(); //我是组件key
  18. c1.aa(); //我是组件key -- 我是组件key -- 我是标题 -- 我是标题
  19. }

重写父级的方法 / 普通方法里调用父级的方法
  1. class Parent {
  2. Parent(this.name, this.age);
  3. final int age;
  4. final String name;
  5. show() {
  6. print('${this.name} -- ${this.age}');
  7. }
  8. }
  9. class Child extends Parent {
  10. Child(name, age, this.sex) : super(name, age);
  11. final String sex;
  12. @override //可写可不写,目的是让人知道show是重写父类的方法
  13. show() {
  14. print('我被重写了: ${this.name} -- ${this.age} -- ${this.sex}');
  15. }
  16. showParent() {
  17. super.show(); //普通方法里调用父级的方法 super.方法名
  18. }
  19. }
  20. void main() {
  21. var c1 = Child('李四', 20, 'male');
  22. c1.show(); //我被重写了: 李四 -- 20 -- male
  23. c1.showParent(); //李四 -- 20
  24. }

抽象类

我们在继承的时候有的时候需要约束子级必须有某个方法,那这个时候就用抽象类,也就是说,其实抽象类可以理解为一个标准。
Dart语言没有提供interface关键字来定义接口,但是Dart语言中保留了抽象类,同Java,使用abstract关键字来修饰抽象类。而Dart中的抽象类,实际上就相当于Java中的接口。

  1. // abstract 來创建抽象类
  2. abstract class Parent {
  3. String name = 'jack';
  4. showName(); //抽象方法 抽象方法没有方法体,必须有子级来实现
  5. // showAge(); //如果有多个抽象方法,子级必须都实现
  6. }
  7. class Child extends Parent {
  8. @override
  9. showName() {
  10. //子级必须有这个方法
  11. print(name);
  12. }
  13. }
  14. void main() {
  15. // var p1 = Parent('李四'); //抽象类是不能被实例化的,子类继承抽象类时,必须实现全部抽象方法。
  16. var c1 = Child();
  17. c1.showName(); //jack
  18. }

多态

同一个方法调用执行不同的效果,说白了就是子类继承了父类的方法,但是不同子类执行的效果不同。

  1. abstract class Parent {
  2. String name = '张三';
  3. showName();
  4. }
  5. class Child extends Parent {
  6. @override
  7. showName() {
  8. print('我是Child的name');
  9. }
  10. }
  11. class Worker extends Parent {
  12. @override
  13. showName() {
  14. print('我是Worker的name');
  15. }
  16. }
  17. //showName虽然都是继承自父类的方法,但是Child、Worker执行的效果不一样。

隐式类(接口的定义)

实际上在Dart中,每个类都隐式的定义了一个包含所有实例成员的接口, 并且该类实现了这个接口。

如果要创建一个A类,A要支持B类的API,但是不需要继承B的实现,那么我们可以通过A实现B的接口。

因此,如果我们想实现某个接口,但又不想继承,则可以使用这种隐式接口机制。我们需要用到关键字 implements 来实现接口。

extends抽象类和implements的区别:

  • 如果要复用抽象类里面的方法,并且要用抽象类方法约束子类,我们用extends继承抽象类
  • 如果只是把抽象类当作标准的话,我们就用implements ```dart abstract class DataForm { String name; add(); //只继承所有的抽象类方法 del();

    // show() {} //不需要继承其它方法,如果有,那么User用implements继承的时候就会报错。 }

class User implements DataForm { @override String name;

@override add() { return null; }

@override del() { return null; } }

  1. <a name="qxj4h"></a>
  2. # 多接口继承的实现
  3. ```dart
  4. abstract class InterfaceA {
  5. String a;
  6. aa();
  7. }
  8. abstract class InterfaceB {
  9. String b;
  10. bb();
  11. }
  12. class Demo implements InterfaceA, InterfaceB {
  13. @override
  14. String a;
  15. @override
  16. String b;
  17. @override
  18. aa() {
  19. return null;
  20. }
  21. @override
  22. bb() {
  23. return null;
  24. }
  25. }

mixins

js-mixin模式:https://www.yuque.com/zhuchaoyang/wrif6k/iinaz1#0mtvR

混入不是真正的继承
  1. class A{}
  2. //class Demo1 extends A { //混入的类不能继承其他类
  3. class Demo1 {
  4. String name = '张三';
  5. //Demo1() {} //不能加构造函数,因为下面的Demo3无法使用super链接父级
  6. showName() {
  7. return name;
  8. }
  9. }
  10. class Demo2 {
  11. int age = 18;
  12. }
  13. // class Demo3 extends Demo1, Demo2{} //报错
  14. class Demo3 with Demo1, Demo2 {}
  15. void main() {
  16. var d3 = Demo3();
  17. print('${d3.name} -- ${d3.age}');
  18. }

先继承再混入
  1. class A {
  2. int a;
  3. A(this.a);
  4. }
  5. class Demo1 {
  6. String name = '张三';
  7. showName() {
  8. return name;
  9. }
  10. }
  11. class Demo2 {
  12. int age = 18;
  13. // 当出现相同的方法,注意顺序
  14. // showName() {
  15. // return 2;
  16. // }
  17. }
  18. // 先继承,再混入
  19. class Demo3 extends A with Demo1, Demo2 {
  20. Demo3(a) : super(a);
  21. }
  22. void main() {
  23. var d3 = Demo3(20);
  24. print('${d3.name} -- ${d3.age} -- ${d3.a}'); //张三 -- 18 -- 20
  25. print(d3.showName()); //张三
  26. }

枚举类型

枚举类型也称为 enumerations 或 enums,是一种特殊的类,用于表示数量固定的常量值。

使用enum关键字定义一个枚举类型:

  1. // 枚举中的每个值都有一个index方法,该返回值所在枚举类型定义中的位置(从0开始)。
  2. // Set 是没有下标的
  3. print(Color.red.index); //0
  4. print(Color.green.index); //1
  5. print(Color.blue.index); //2
  6. // 使用枚举的values常量,获取所有枚举值列表(list)
  7. List<Color> colors = Color.values;
  8. print(colors[2]); //Color.blue
  9. // 枚举不能被实例化,mixins或实现。
  10. // var c1 = Color();

枚举中的每个值都有一个index方法,该返回值所在枚举类型定义中的位置(从0开始)。

  1. enum SingingCharacter { lafayette, jefferson }
  2. SingingCharacter _character = SingingCharacter.lafayette;
  3. void main() {
  4. print(_character); //SingingCharacter.lafayette
  5. print(SingingCharacter.lafayette); //SingingCharacter.lafayette
  6. }