类概述
- 普通类
- 变量
- 实例变量(创建对象后,使用 对象.变量名 调用)
- 静态变量(用static修饰,使用 类名.变量名 调用)
- 函数
- 实例函数(创建对象后,使用 对象.函数名 调用)
- 静态函数(用static修饰,使用 类名.函数名 调用)
- 构造函数
- 默认构造函数
- 自定义构造函数
- 静态构造函数(使用const修饰的构造函数)
- 重定向构造函数
- 工厂构造函数
- 变量
- 抽象类
- 变量
- 实例变量(其子类创建对象后,使用 对象.变量名 调用)
- 静态变量(用static修饰,使用 类名.变量名 调用)
- 函数
- 实例函数(其子类创建对象后,使用 对象.函数名 调用)
- 静态函数(用static修饰,使用 类名.函数名 调用)
- 抽象函数(其子类实现该函数,创建对象后,使用对象.函数名 调用)
- 不能实例化(工厂构造函数除外)。
- 变量
Object
-
Object
是所有类的父类。 -
Object
没有父类。 - 一个类只能有一个父类。
- 如果一个类没有显示的用
extends
去继承一个类,那么默认其继承的是Object
。
- Dart 是一种面向对象的语言,并且支持基于mixin的继承方式。
- Dart 语言中所有的对象都是某一个类的实例,所有的类有同一个基类--Object。
- 基于mixin的继承方式具体是指:一个类可以继承自多个父类。
- 使用
.
来调用实例的变量或者方法。 - 使用
?.
来确认前操作数不为空, 常用来替代. , 避免左边操作数为null引发异常。 - 级联操作符 .. , 可以连续调用对象的一些列属性或函数。
- 使用new语句来构造一个类,构造函数的名字可能是
ClassName
,也可以是ClassName.identifier
, 例如: - 创建类的实例,使用new 或const,new对应的是普通的构造函数,const对应的是用const形式的构造函数。
- 使用
runtimeType
属性,在运行中获取对象的类型。该方法将返回Type 类型的变量。 - 在类定义中,所有没有初始化的变量都会被初始化为null。
- 构造函数不能继承(Constructors aren’t inherited)
- 构造函数不能被继承,父类中的命名构造函数不能被子类继承。如果想要子类也拥有一个父类一样名字的构造函数,必须在子类是实现这个构造函数。
构造函数(Constructors)
- 没有返回值(factory构造方法有返回值)
- 构造函数名与类名相同
- 默认构造函数,如果类中没有显示声明构造函数,那么会默认有个构造函数,默认构造函数是与类同名且无参数无返回值的函数。
class Class01{ //变量 int a; String b; //未声明构造函数 //所以这个类默认有个构造函数 //Class01(){} }
- 自定义构造函数
class Class02{ int a; String b; //自定义的一个构造函数,有两个参数 Class02(int a,String c){ this.a = a;//名字冲突时,可使用 this b = b; } } void main(){ var c02 = new Class02(3,"abc"); print(c02.a); } ''' //如果构造函数中的参数都是给实例变量赋值的,那么上面这种情况还可以写成下面这种方式,简化了: class Class02{ int a; String b; //自定义的一个构造函数,有两个参数 Class02(this.a,this.b); } void main(){ var c02 = new Class02(3,"abc"); print(c02.a); } ''';
- 命名构造函数,一种可以为类声明多个构造函数的方式。注意这里没有重载的概念,不能声明只是参数类型或数量不同的构造函数,使用命名构造函数实现。
class Class03{ int a; String b; Class03(int a,String b){ this.a = a; this.b = b; } Class03.fun1(int a){ this.a = a; } Class03.fun2(String b){ this.b = b; } } void main(){ var class03 = new Class03(3, "ccc"); var class04 = new Class03.fun1(4); var class05 = new Class03.fun2("ddd"); }
- 静态构造函数
- 类的对象不会改变
- 类的变量不会改变,也就是常量了
- 使用final修饰变量
- 使用const 修饰构造方法
- 创建实例时,使用const 而不是new
class Class04{ final int a; final String b; const Class04(this.a,this.b); void fun01(){ print("aa"); } } void main(){ var class06 = const Class04(4, "ccc"); class06.fun01(); print(class06.a); }
- 重定向构造函数,在类的构造函数中,有时我们只是需要调用到另一个构造函数。
class Class05{ int a; String b; Class05(int a,String b){ this.a = a; this.b = b; } Class05.fun1(int a){ this.a = a; } Class05.fun2(String b):this.fun1(33);//重定向到fun1 Class05.fun3(String b):this(33,"ddddd");//重定向到构造函数 }
- 工厂构造函数
- 工厂构造函数不能用
this
。 - 使用
factory
修饰构造函数 - 构造函数内有返回值,类型是当前类或其子类,此返回值可以是用命名构 造函数创建的,也可以是缓存中的。
- 使用
new
创建实例
class Class06{ int a; static Class06 instance ; //这里创建了一个单例 //这里的构造函数用factory修饰,使用new时, //不再是创建一个全新的实例了,而是通过函数体内return获取到实例 factory Class06(int a){ if(instance==null){ instance = new Class06.fun1(a); } return instance; } Class06.fun1(this.a);//注意这里是实例化当前对象的构造方法 } void main(){ var class07 = new Class06(3);//使用new print(class07.a); }
- 工厂构造函数不能用
抽象类
- 使用abstract修饰类。
- 可定义实例方法。
- 可定义抽象方法,抽象方法没有函数体。
- 抽象类不能实例化(工厂构造函数除外)。
- 子类继承抽象类后,必须实现所有抽象方法,除非子类也是抽象类。
- 只有抽象类能定义抽象方法。
//定义抽象类 abstract class Class09{ void fun01();//定义抽象方法 } //继承抽象类 class Class10 extends Class09{ @override void fun01() {//实现抽象方法 print("aaa"); } } void main(){ var c10 = new Class10(); c10.fun01(); }
- 抽象方法
''' Instance , getter 和 setter 方法可以是抽象的,也就是定义一个接口, 但是把实现交给其他的类。要创建一个抽象方法,使用分号(;)代替方法体; '''; abstract class Doer { // ...定义实例变量和方法... void doSomething(); // 定义一个抽象方法。 } class EffectiveDoer extends Doer { void doSomething() { // ...提供一个实现,所以这里的方法不是抽象的... } }
setters 和 Getters
- 是一种提供对方法属性读和写的特殊方法。每个实例变量都有一个隐式的 getter 方法,合适的话可能还会有 setter 方法。你可以通过实现 getters 和 setters 来创建附加属性,也就是直接使用 get 和 set 关键词:
借助于 getter 和 setter ,你可以直接使用实例变量,并且在不改变客户代码的情况下把他们包装成方法。 - 不论是否显式地定义了一个 getter,类似增量(++)的操作符,都能以预期的方式工作。为了避免产生任何向着不期望的方向的影响,操作符一旦调用 getter ,就会把他的值存在临时变量里。
class Rectangle {
num left;
num top;
num width;
num height;
Rectangle(this.left, this.top, this.width, this.height);
// 定义两个计算属性: right and bottom.
num get right => left + width;
set right(num value) => left = value - width;
num get bottom => top + height;
set bottom(num value) => top = value - height;
}
main() {
var rect = new Rectangle(3, 4, 20, 15);
assert(rect.left == 3);
rect.right = 12;
assert(rect.left == -8);
}
静态变量和静态函数
- 使用static修饰的变量为静态变量。
- 使用static修饰的函数为静态函数。
- 静态变量和函数,使用类名直接调用。
- 实例变量和函数,使用类的对象调用。
- 静态变量和函数,不能访问实例变量和函数。
- 静态函数内,不能使用this。
- 普通类和抽象类都可以定义静态变量和函数
- 只有当静态变量被使用时才被初始化。
- 你可以将静态方法作为编译时常量。例如,你可以把静态方法作为一个参数传递给静态构造函数。
class Class11{
static int a = 3;//静态变量
int b = 4;//实例变量
//静态方法
static void fun01(int c){
print(c);
//print(b);//这里报错,静态方法内不能使用实例变量
}
//实例方法
void fun02(){
print(b);
}
}
void main(){
var class11 = new Class11();
//实例变量和函数
print(class11.b);
class11.fun02();
//调用静态变量和函数
Class11.fun01(44);
print(Class11.a);
}
枚举类
- 是一种用来代表一个固定数量的常量的特殊类。
- 使用enum声明枚举。
- 每个枚举值都有一个唯一值。
- 枚举不能使用new实例化 。
- 使用枚举值 枚举.枚举值。
- 你不能在子类中混合或实现一个枚举。
'''声明一个枚举类型需要使用关键字 enum :''';
enum Color {
red,
green,
blue
}
'''
在枚举中每个值都有一个 index getter 方法,它返回一个在枚举声明中从 0 开始的位置。
例如,第一个值索引值为 0 ,第二个值索引值为 1 。
''';
assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);
'''要得到枚举列表的所有值,可使用枚举的 values 常量。''';
List<Color> colors = Color.values;
assert(colors[2] == Color.blue);
'''
你可以在 switch 语句 中使用枚举。
如果 e 在 switch (e) 是显式类型的枚举,那么如果你不处理所有的枚举值将会弹出警告:
''';
Color aColor = Color.blue;
switch (aColor) {
case Color.red:
print('Red as roses!');
break;
case Color.green:
print('Green as grass!');
break;
default: // Without this, you see a WARNING.
print(aColor); // 'Color.blue'
}
继承
使用extends 关键字表示继承。
构造方法不能被继承。
使用@override重写函数。
如果继承的是抽象类,要实现所有抽象函数。
-
继承抽象类
//定义抽象类 abstract class Parent{ int a = 1; String b = "bb"; void fun1();//定义抽象方法 void fun2(int a,int c){ this.a = a; print(c); } } class Child extends Parent{ String b = "child b";//重写了父类的属性 //实现了父类的抽象函数 @override void fun1() { print(b); } //重写了父类的函数 @override void fun2(int a, int c) { print(a+c); } } void main(){ var child = new Child(); child.fun1();// child b child.fun2(3, 4); //7 }
-
继承普通类
- 子类至少定义一个构造函数调用父类的任一构造函数,使用:super。
- 子类的每个构造函数都要继承父类的任一构造函数。
- 子类可重写父类的函数。
class Fruit{
String name;
int nums;
Fruit(this.name);//定义构造函数
Fruit.num(this.name,this.nums);//定义命名构造函数
Fruit.con(num){//定义命名构造函数
nums = num*2;
}
void fun1(){
print(name);
}
void fun2(){
print(nums);
}
}
class Apple extends Fruit{
String name;
int nums;
int color;
//至少需要定义一个构造函数调用父类的任一构造函数
Apple(String name) : super(name);
Apple.con1(this.color,this.name): super.num(name,3);
Apple.con2() : super.con(3){
color = 3;
}
//重写父类的fun2函数
@override
void fun2() {
print(color);
super.fun2();//调用父类的fun2方法
}
//子类自己的方法
void fun3(){
print(nums);
}
}
//使用 extends 创建一个子类,同时 supper 将指向父类:
class Television {
void turnOn() {
_illuminateDisplay();
_activateIrSensor();
}
// ...
}
class SmartTelevision extends Television {
void turnOn() {
super.turnOn();
_bootNetworkInterface();
_initializeMemory();
_upgradeApps();
}
// ...
}
- 下面是个关于重写 Object 类的方法 noSuchMethod() 的例子,当代码企图用不存在的方法或实例变量时,这个方法会被调用。
class A {
// 如果你不重写 noSuchMethod 方法, 就用一个不存在的成员,会导致NoSuchMethodError 错误。
void noSuchMethod(Invocation mirror) {
print('You tried to use a non-existent member:' +
'${mirror.memberName}');
}
}
你可以使用 @override 注释来表明你重写了一个成员。
class A {
@override
void noSuchMethod(Invocation mirror) {
// ...
}
}
'''
如果你用 noSuchMethod() 实现每一个可能的 getter 方法,
setter 方法和类的方法,那么你可以使用 @proxy 标注来避免警告。
''
@proxy
class A {
void noSuchMethod(Invocation mirror) {
// ...
}
}
mixins
- mixins 是一种多类层次结构的类的代码重用。
- 要使用 mixins ,在 with 关键字后面跟一个或多个 mixin 的名字。用
,
分开 - 要实现 mixin ,就创建一个继承 Object 类的子类,不声明任何构造函数,不调用 super 。
class With1 {
String getName() => 'With1';//三个类都有该方法
String getAge()=> "With1 10" ;//该类独有
}
class With2 {
String getName() => 'With2';//三个类都有该方法
String getColor() => "With2 red";//该类独有
int getNum()=> 6;//该类和OtherClass都有
String getFruit()=>"With2 banana";
}
class OtherClass {
String getName() => 'OtherClass';//三个类都有该方法
int getNum() => 3; //该类和With2都有
int getDesk() => 333;//该类独有
String getPhone()=>"OtherClass huawei";//该类和子类
String getFruit()=>"OtherClass apple";
}
class Child1 extends OtherClass with With1 ,With2 {
//重写父类
@override
String getPhone() {
return "Child1 iphone";
}
@override
String getFruit() {
return "Child1 oriange";
}
}
class Child2 extends OtherClass with With2, With1 {}
void main(){
print("class Child1 extends OtherClass with With1 ,With2 {}");
Child1 c1 = Child1();
//Child1 iphone 重写了函数,调用时用的是自身的函数
print(c1.getPhone());
//Child1 oriange 重写了函数,调用时用的是自身的函数
print(c1.getFruit());
//333 调用的是OtherClass的函数 With1 With2中没有同名函数
print(c1.getDesk());
print(c1.getNum());//6 调用的是With2中的函数
print(c1.getAge());//With1 10 调用的是With1中的函数
print(c1.getColor());//With2 red 调用的是With2中的函数
//With2 调用的是With2中的函数 With2在声明顺序中更靠后
print(c1.getName());
print("-----------------------");
print("class Child2 extends OtherClass with With2, With1 {}");
Child2 c2 = Child2();
//OtherClass huawei 没有重写函数,调用时用的是OtherClass的函数
print(c2.getPhone());
//With2 banana 没有重写函数,调用时用的是With2的函数
//虽然OtherClass也有,但With2在声明顺序中更靠后
print(c2.getFruit());
//333 调用的是OtherClass的函数 With1 With2中没有同名函数
print(c2.getDesk());
print(c2.getNum());//6 调用的是With2中的函数
print(c2.getAge());//With1 10 调用的是With1中的函数
print(c2.getColor());//With2 red 调用的是With2中的函数
//With1 调用的是With1中的函数 With1在声明顺序中更靠后
print(c2.getName());
}
隐式接口
- Z 实现了类X Y ,那么Z中必须重写XY中所有的属性和函数。
- 与java不同,dart中没有专门定义接口的方式,dart中类即是接口。
- 一个类可以实现多个接口,也就是可以实现多个类,用implements。
- 一个类只能继承一个类,用extends。
class X {
int x= 19;
void funX(){
print("X-X");
}
}
class Y {
String y = "yyy";
void funY(){
print("Y-Y");
}
}
class Z implements X,Y{
@override
int x=33;
@override
String y="33333";
@override
void funX() {
print("Z-X");
}
@override
void funY() {
print("Z-Y");
}
}