Dart 也是一个面向对象的语言。
Dart里面所有的东西都是对象,所有的东西都继承于Object。
基本写法
什么是对象
对象就是属性和方法的集合。
List Set Map
var list1 = List();list1.add(); //方法list1.length; //属性
Dart 里面类怎么声明
// 声明不要放在main函数里面// 首字母必须大写class Person {String name = '张三';int age = 18;showName() {print('我的名字叫${this.name}');}}void main() {var p1 = Person(); //new 可以省略p1.showName(); //我的名字叫张三}
构造函数的使用
Dart中的类与Java中的相似,不同的是,Dart中没有private、public这些成员访问修饰符。如果是类私有的成员,不希望外面访问,只需要在成员变量之前加上一个下划线_变为私有即可。
另外注意,Dart中没有构造方法的重载,不能写两个同名的构造方法。
class Person {String name;int age;// 类似js的 constructor ,构造函数名字和类型要一致。只能声明一个Person(name, age) {this.name = name;this.age = age;}show() {print('${this.name} -- ${this.age}');}}void main() {var p1 = Person('张三', 18);var p2 = Person('李四', 20);p1.show(); //张三 -- 18p2.show(); //李四 -- 20}
简写
class Person {String name;int age;String province;String city;Person(this.name, {this.age,this.sex = 'male',});show() {print('${this.name} -- ${this.age} -- ${this.sex}');}}void main() {var p = Person('Jack', sex: 'female');p.show(); //Jack -- null -- female}
常量构造方法
如果想提供一个状态永远不变的对像,在Dart中,我们可以创建一个编译时常量对象,节省开销。
class ConstPoint {final num x;final num y;// 使用const修构造方法const ConstPoint(this.x, this.y);// 编译时常量对象,需使用const来创建对象static final ConstPoint origin = const ConstPoint(0, 0);}void main() {print(ConstPoint.origin.x); //0print(ConstPoint.origin.y); //0}
命名构造函数(命名构造方法)
Dart类中两个同名构造方法不能重载,但是Dart语言为类新增了一种称为命名构造方法的东西。
例如 DateTime.now() .
使用命名构造方法可以为一个类实现多个构造方法,也可以更清晰的表明意图。
class Person {String name;int age;String sex;String province;String city;Person(this.name, {this.age, this.sex = 'male'});// 命名构造函数(自定义构造函数),可以声明多个// Person.fromData(Map data) {// this.province = data['province'];// this.city = data['city'];// }Person.getPosition({this.province = '北京',this.city,});}void main() {// var p = new Person.getPosition({'province': '河南', 'city': '漯河'});// print('${p.province} -- ${p.city}'); //河南 -- 漯河var p = Person.getPosition(city: '丰台');print('${p.province} -- ${p.city}'); //北京 -- 丰台}
构造方法重定向
有时候一个构造方法会调动类中的其他构造方法来实例化,这时候可以使用构造方法重定向。
class Alignment {final double x;final double y;const Alignment(this.x, this.y): assert(x != null),assert(y != null);static const Alignment topLeft = Alignment(-1.0, -1.0);// 命名构造方法 重定向 到同名构造方法,中间使用一个冒号Alignment.bottomTargetX(x) : this(x, -1);show() {print('$x -- $y');}}void main() {Alignment(0.0, 0.0).show(); //0.0 -- 0.0Alignment.topLeft.show(); //-1.0 -- -1.0Alignment.bottomTargetX(-0.5).show(); //-0.5 -- -1.0}
工厂构造方法
有时候可能有一种需求,并不需要每次都创建新的类实例,而是每一种情况,只需要一个实例。
工厂构造函数是一种构造函数,与普通构造函数不同,工厂函数不会自动生成实例,而是通过代码来决定返回的实例对象.
工厂构造函数的关键字为factory,下面我们用工厂构造函数写一个只能创造一个实例的类.
- 工厂构造函数类似于static静态成员,无法访问this指针,所以在工厂构造函数中能处理的任务较为有限.
- 使用工厂构造函数时往往需要定义一个命名构造函数用来生产实例.
示例1
class A {String name;// 为 A类 创建名为 cache 的静态成员,它将缓存A类的第一个实例.static A cache;// 使用关键字factory修饰类同名构造方法factory A([String name = 'A']) {// 在工厂构造函数A中,首先判断A.cache是否已经存在实例(即判断是否是第一次实例化),// 如果存在则返回缓存的实例,不存在则新建一个实例并缓存.if (A.cache == null) {A.cache = new A.newObject(name);}return A.cache;}// A.newObject是一个命名构造函数,用于创建实例.A.newObject(this.name);}void main() {A a = new A('hello');print(a.name); //helloA b = new A('world');print(b.name); //helloprint(a == b); //true 说明变量a和b指向的是同一个实例对象.}
示例2
当我们需要创建一个新的对象或者从缓存中取一个对象时,工厂构造方法就派上了用场。
class Logger {final String name;// 创建一个静态Map做为缓存static final Map<String, Logger> _cache = <String, Logger>{};// 定义一个命名构造方法,用下划线"_"修饰,将构造方法私有化Logger._internal(this.name);// 使用关键字factory修饰类同名构造方法factory Logger(String name) {if (_cache.containsKey(name)) {return _cache[name];} else {// 调用命名构造方法创建新对象final logger = Logger._internal(name);_cache[name] = logger; // 存入缓存return logger;}}}void main() {var uiLog = new Logger('UI');var eventLog = new Logger('event');}
单例模式
方法1:
class Demo {//命名构造函数Demo._internal() {}//保存单例static var _instance = Demo._internal();//工厂构造函数factory Demo() => _instance;}//使用var d1 = Demo();var d2 = Demo();print(d1 == d2); //true
方法2:
class Demo {//命名构造函数Demo._internal() {}//保存单例static var _instance = Demo._internal();// 获取单例// tatic getInstance = _instance;static getInstance({refresh = false}) {refresh == true ? _instance._init() : _instance._next();}_init() {//refresh为true时,做一些事情后,再返回该单例对象print('_init');return this;}_next() {//refresh为false时,做一些事情后,再返回该单例对象print('_next');return this;}}//使用var d1 = Demo.getInstance(refresh: true); //_initvar d2 = Demo.getInstance(); //_nextprint(d1 == d2); //true
初始化列表(默认值)
- 初始化列表位于构造方法的小括号与大括号之间,在初始化列表之前需添加一个冒号。
- 初始化列表是由逗号分隔的一些赋值语句组成。
- 它适合用来初始化
final修饰的变量 - 初始化列表的调用是在构造方法之前,也就是在类完成实例化之前,因此初始化列表中是不能访问
this的
正常实例化
class A {//实例化方法-初始化赋值A([this.name = 'jack',this.age,this.sex = 'male',]);final String name;final int age;final String sex;show() => print('${this.name} -- ${this.age} -- ${this.sex}');}void main() {A('jack', 18).show(); //jack -- 18 -- male}
class A {A({this.name = 'jack',this.age,this.sex: 'male',});final String name;final int age;final String sex;show() => print('${this.name} -- ${this.age} -- ${this.sex}');}void main() {A(age: 18).show(); //jack -- 18 -- male}
命名构造函数实例化
class A {A.now({this.name = 'jack',this.age = 18,});final String name;final int age;show() => print('${this.name} -- ${this.age}');}void main() {A.now().show(); //jack -- 18}
class A {A.now({name = 'jack', age = 18}): this.name = name, //this可省略age = age;final String name;final int age;show() => print('${this.name} -- ${this.age}');}void main() {A.now(age: 20).show(); //jack -- 20}
运算符重载
这个特性,又很类似于C++中的运算符重载,在Java中是没用这种概念的。
class Point {int x;int y;Point(this.x, this.y);// 使用operator关键字,为该类重载"+"运算符Point operator +(Point p) {return new Point(this.x + p.x, this.y + p.y);}// 为该类重载"-"运算符Point operator -(Point p) {return new Point(this.x - p.x, this.y - p.y);}}void main() {var p1 = new Point(1, 5);var p2 = new Point(7, 10);// 重载运算符后,类可以使用“+”、“-” 运算符操作var p3 = p1 + p2;var p4 = p2 - p1;print("${p3.x}, ${p3.y}"); //8, 15print("${p4.x}, ${p4.y}"); //6, 5}
Dart中允许重载的运算符如下:
+ |
– |
* |
~/ |
/ |
% |
^ |
< |
> |
<= |
>= |
== |
[] |
[]= |
& |
~ |
<< |
>> |
| |
getter、setter
getter和setter 是用于对象属性读和写的特殊方法。
每个实例变量都有一个隐士getter,通常情况下还会有一个setter。
使用get和set关键字实现getter和setter,能够为实例创建额外的属性。
class A {A(this.name, this.age);final String name;int age;getAge() => age;addAge(int val) {age += val;}}void main() {var s1 = A('jack', 18);print(s1.getAge()); //18s1.addAge(5);print(s1.getAge()); //23}
改:
class A {A(this.name, this.age);final String name;int age;get getAge => age;// 等同于// get getAge { return age; }set addAge(int val) {age += val;}}void main() {var s1 = A('jack', 18);print(s1.getAge); //18s1.addAge = 5;print(s1.getAge); //23}
私有属性和私有方法
js-私有属性:https://www.yuque.com/zhuchaoyang/wrif6k/ewwgff#ukD3f
私有方法和私有属性,是只能在类的内部访问的方法和属性,外部不能访问。
使用 _xxx 来定义私有属性和方法。
//注意:要使用私有属性和方法,类必须分离出去。//Person.dartclass Person {String name;int _age; //私有属性Person(this.name, this._age);show() {print('${this.name} -- ${this._age}');}// 私有方法_showPrivate() {}}//1.dartimport './Person.dart';void main() {var p1 = Person('张三', 18);print(p1.name); //张三// print(p1._age); //报错p1.show(); //张三 -- 18 内部可以使用私有属性和私有方法// p1._showPrivate(); //报错}
静态的属性和方法
js-静态方法:https://www.yuque.com/zhuchaoyang/wrif6k/ewwgff#QHBZy
- 静态的属性和方法是通过
static关键字来实现的。 - 静态变量(类变量)对于类级别的状态是非常有用的,可以直接通过类来访问不需要实例化。
- 静态变量只到它们被使用的时候才初始化。
- 静态方法(类方法)不能在实例上使用,因此他们不能访问
this。 - 静态函数可以当做编译时常量使用。例如,可以将静态方法作为参数传递给常量构造函数。
- 静态方法不能访问非静态的成员,非静态方法可以访问静态的成员。
class Person {String name;static int age = 12; //静态属性Person(this.name);// 实例方法show() {print(this.name);print(age); //非静态方法可以访问静态的成员。}// 静态方法static showStatic() {// print(this.name); //报错:静态方法不能访问非静态的成员print(age);}}void main() {var p1 = Person('张三');print(Person.age); //12 直接通过类来访问不需要实例化Person.age = 10;print(Person.age); //10Person.showStatic(); //10p1.show(); //张三 10}
继承(扩展类)
js-类的继承:https://www.yuque.com/zhuchaoyang/wrif6k/iinaz1
继承父类的构造函数:
class Parent {Parent(this.name, this.age);final int age;final String name;show() {print('${this.name} -- ${this.age}');}}// class Child extends Parent {} //报错:如果只是用extends,不能继承构造函数中的东西class Child extends Parent {Child(name, age) : super(name, age); //通过初始化列表,使用super引用父级的构造函数}void main() {var p1 = Parent('张三', 18);p1.show(); //张三 -- 18var c1 = Child('李四', 20);c1.show(); //李四 -- 20}
继承并且扩展自己:
class Child extends Parent {// name age 继承自父组件;sex是子组件扩展的参数Child(name, age, this.sex) : super(name, age);final String sex;showSex() {print(this.sex);}}void main() {var c1 = Child('李四', 20, 'male');c1.show(); //李四 -- 20c1.showSex(); //male}
class Parent {Parent({this.key});final String key;show() => print(this.key);}class Child extends Parent {Child({key,this.title,}) : assert(title != null),super(key: key);final String title;aa() => print('${this.key} -- ${key} -- ${this.title} -- ${title}');}void main() {var c1 = Child(key: '我是组件key', title: '我是标题');c1.show(); //我是组件keyc1.aa(); //我是组件key -- 我是组件key -- 我是标题 -- 我是标题}
重写父级的方法 / 普通方法里调用父级的方法
class Parent {Parent(this.name, this.age);final int age;final String name;show() {print('${this.name} -- ${this.age}');}}class Child extends Parent {Child(name, age, this.sex) : super(name, age);final String sex;@override //可写可不写,目的是让人知道show是重写父类的方法show() {print('我被重写了: ${this.name} -- ${this.age} -- ${this.sex}');}showParent() {super.show(); //普通方法里调用父级的方法 super.方法名}}void main() {var c1 = Child('李四', 20, 'male');c1.show(); //我被重写了: 李四 -- 20 -- malec1.showParent(); //李四 -- 20}
抽象类
我们在继承的时候有的时候需要约束子级必须有某个方法,那这个时候就用抽象类,也就是说,其实抽象类可以理解为一个标准。
Dart语言没有提供interface关键字来定义接口,但是Dart语言中保留了抽象类,同Java,使用abstract关键字来修饰抽象类。而Dart中的抽象类,实际上就相当于Java中的接口。
// abstract 來创建抽象类abstract class Parent {String name = 'jack';showName(); //抽象方法 抽象方法没有方法体,必须有子级来实现// showAge(); //如果有多个抽象方法,子级必须都实现}class Child extends Parent {@overrideshowName() {//子级必须有这个方法print(name);}}void main() {// var p1 = Parent('李四'); //抽象类是不能被实例化的,子类继承抽象类时,必须实现全部抽象方法。var c1 = Child();c1.showName(); //jack}
多态
同一个方法调用执行不同的效果,说白了就是子类继承了父类的方法,但是不同子类执行的效果不同。
abstract class Parent {String name = '张三';showName();}class Child extends Parent {@overrideshowName() {print('我是Child的name');}}class Worker extends Parent {@overrideshowName() {print('我是Worker的name');}}//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; } }
<a name="qxj4h"></a># 多接口继承的实现```dartabstract class InterfaceA {String a;aa();}abstract class InterfaceB {String b;bb();}class Demo implements InterfaceA, InterfaceB {@overrideString a;@overrideString b;@overrideaa() {return null;}@overridebb() {return null;}}
mixins
js-mixin模式:https://www.yuque.com/zhuchaoyang/wrif6k/iinaz1#0mtvR
混入不是真正的继承
class A{}//class Demo1 extends A { //混入的类不能继承其他类class Demo1 {String name = '张三';//Demo1() {} //不能加构造函数,因为下面的Demo3无法使用super链接父级showName() {return name;}}class Demo2 {int age = 18;}// class Demo3 extends Demo1, Demo2{} //报错class Demo3 with Demo1, Demo2 {}void main() {var d3 = Demo3();print('${d3.name} -- ${d3.age}');}
先继承再混入
class A {int a;A(this.a);}class Demo1 {String name = '张三';showName() {return name;}}class Demo2 {int age = 18;// 当出现相同的方法,注意顺序// showName() {// return 2;// }}// 先继承,再混入class Demo3 extends A with Demo1, Demo2 {Demo3(a) : super(a);}void main() {var d3 = Demo3(20);print('${d3.name} -- ${d3.age} -- ${d3.a}'); //张三 -- 18 -- 20print(d3.showName()); //张三}
枚举类型
枚举类型也称为 enumerations 或 enums,是一种特殊的类,用于表示数量固定的常量值。
使用enum关键字定义一个枚举类型:
// 枚举中的每个值都有一个index方法,该返回值所在枚举类型定义中的位置(从0开始)。// Set 是没有下标的print(Color.red.index); //0print(Color.green.index); //1print(Color.blue.index); //2// 使用枚举的values常量,获取所有枚举值列表(list)List<Color> colors = Color.values;print(colors[2]); //Color.blue// 枚举不能被实例化,mixins或实现。// var c1 = Color();
枚举中的每个值都有一个index方法,该返回值所在枚举类型定义中的位置(从0开始)。
enum SingingCharacter { lafayette, jefferson }SingingCharacter _character = SingingCharacter.lafayette;void main() {print(_character); //SingingCharacter.lafayetteprint(SingingCharacter.lafayette); //SingingCharacter.lafayette}
