面向对象编程(OOP)的三个基本特征是:封装、继承、多态
封装:封装是对象和类概念的主要特性。封装,把客观事物封装成抽象的类,并且把自己的部分属性和方法提供给其他对象调用, 而一部分属性和方法则隐藏。
继承:面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
多态:允许将子类类型的指针赋值给父类类型的指针, 同一个函数调用会有不同的执行效果 。
Dart所有的东西都是对象,所有的对象都继承自Object类。
Dart是一门使用类和单继承的面向对象语言,所有的对象都是类的实例,并且所有的类都是Object的子类
一个类通常由属性和方法组成。
void main()
{
List list = new List();
list.add("香蕉");
list.add("苹果");
list.add("葡萄");
print(list); // [香蕉, 苹果, 葡萄]
Map map = new Map();
map['usrName'] = 'admin';
map['password'] = '123456';
print(map); // {usrName: admin, password: 123456}
}
步骤:
1. 类的定义用class关键字
2. 类首字母必须大写
3. 使用new关键字和构造函数来创建对象
语法:
class class_name
{
// 变量: 类中声明的任何变量,表示与对象有关的数据
// setters和getters: 允许程序初始化和检索类字段的值,默认的getter/setter与每个类相关联。但是可以通过显式定义setter/getter来覆盖默认值。
// 构造函数: 负责为类的对象分配内存
// 函数: 函数表示对象可以采取的操作,它们有时也称为方法。
}
实例:
class Point
{
num x;
num y;
}
void main()
{
Point point = new Point(); // 使用默认的构造函数
point.x = 10; // 使用点"."引用实例变量或方法
point?.y = 11; // 如果point不为空,设置它的变量y的值为11
print(point.x); // 10
print(point.y); // 11
}
使用变量说明:
1. 声明实例变量时,所有未初始化的实例变量的值为null
2. 对象的成员包括函数和数据(分别是方法和实例变量)。使用点(.)引用实例变量或方法;
3. 使用?.来确认前操作数不为空, 常用来替代. , 避免左边操作数为null引发异常
新创建的类如果没有声明构造函数,默认有构造函数,默认构造函数没有参数,调用父类的无参构造函数。子类不能继承父类的构造函数。
class Point
{
num x;
num y;
Point()
{
print("这是构造函数,这个方法在类实例化的时候触发");
}
}
void main()
{
Point point = new Point(); // 使用默认的构造函数,调用完会打印:这是构造函数,这个方法在类实例化的时候触发
point.x = 10; // 使用点"."引用实例变量或方法
point?.y = 11; // 如果point不为空,设置它的变量y的值为11
print(point.x); // 10
print(point.y); // 11
}
构造函数就是一个与类同名的函数,构造函数是一个函数,因此可以参数化。但是与函数不同,构造函数不能具有返回类型。
class Point
{
num x;
num y;
Point(num x, num y)
{
print("这是构造函数,这个方法在类实例化的时候触发");
this.x = x;
this.y = y;
}
}
void main()
{
Point point = new Point(11, 22); // 使用有参数的构造函数,调用完会打印:这是构造函数,这个方法在类实例化的时候触发
print(point.x); // 11
print(point.y); // 22
}
在构造函数里初始化成员属性是很常见的事情,因此Dart开发了新的语法糖来简化这种操作:
class Point
{
num x;
num y;
// 注意x,y的赋值会在构造函数执行之前完成.
Point(this.x, this.y);
}
void main()
{
Point point = new Point(11, 22); // 使用有参数的构造函数
print(point.x); // 11
print(point.y); // 22
}
在Dart中构造函数的名称可以是类名 ClassName 或者类名和标识符 ClassName.identifier 。 其中构造函数名称是“ClassName”的函数叫“类名构造函数”;构造函数名称是“ClassName.identifier”的函数叫“命名构造函数”。
class Point
{
num x;
num y;
// 类名构造函数
Point(num x, num y)
{
print("这是构造函数,这个方法在类实例化的时候触发");
this.x = x;
this.y = y;
}
}
void main()
{
Point point = new Point(11, 22); // 使用有参数的构造函数,调用完会打印:这是构造函数,这个方法在类实例化的时候触发
print(point.x); // 11
print(point.y); // 22
}
使用命名构造函数可以为类提供多个构造函数,以使类定义多个构造函数。
class Point
{
num x;
num y;
// 类名构造函数
Point(num x, num y)
{
print("这是类名构造函数");
this.x = x;
this.y = y;
}
// 命名构造函数
Point.now(num x, num y)
{
print("这是命名构造函数");
this.x = x;
this.y = y;
}
}
void main()
{
Point point = new Point(11, 22); // 使用有参数的类名构造函数
print(point.x); // 11
print(point.y); // 22
Point point1 = new Point.now(123, 456); // 使用有参数的命名构造函数
print(point1.x); // 123
print(point1.y); // 456
}
有时构造函数的唯一目的是重定向到同一类中的另一个构造函数。重定向构造函数的主体为空,构造函数调用出现在冒号( :)之后 。 就是在创建类时,定义一个命名构造函数,但是这个构造函式的主体不实现,直接通过另外一个构造函数实现对外界传入的参数接收并赋值给内部的变量。
class Point
{
num x;
num y;
// 类名构造函数
Point(this.x, this.y);
// 命名构造函数
Point.now(this.x, this.y);
// 重定向构造函数,origin构造函数将外界的传值指向给了now构造函数
Point.origin(num x, num y):this.now(x, y);
}
void main()
{
Point point = new Point.origin(88, 99); // 调用有参数的重定向构造函数
print(point.x); // 88
print(point.y); // 99
}
在实现一个构造函数时使用factory关键字,该构造函数并不总是创建其类的新实例。例如,工厂构造函数可能会从缓存中返回实例,也可能会返回子类型的实例。
class Logger
{
final String name;
// 变量前加下划线表示私有属性
static final Map _cache = new Map();
factory Logger(String name)
{
if(_cache.containsKey(name))
{
return _cache[name];
}
else
{
final logger = new Logger._internal(name);
_cache[name] = logger;
return logger;
}
}
Logger._internal(this.name);
}
void main()
{
var x = new Logger("X");
var alsoX = new Logger("X");
print(identical(x, alsoX)); // true
}
class Person
{
String name = "张三";
int age = 23;
void getInfo()
{
print("${this.name} --- ${this.age}");
}
void setInfo(String name, int age)
{
this.name = name;
this.age = age;
}
}
void main()
{
Person p = Person();
print(p.name); // 张三
p.getInfo(); // 张三 --- 23
p.setInfo("李四", 55);
p.getInfo(); // 李四 --- 55
}
在对象的实例方法有权限获取对象变量和this
import 'dart:math';
class Point
{
num x, y;
Point(this.x, this.y);
num distanceTo(Point other)
{
var dx = x - other.x;
var dy = y - other.y;
return sqrt(dx * dx + dy * dy);
}
}
void main()
{
Point p = new Point(3, 5);
var value = p.distanceTo(new Point(3, 3));
print(value); // 2.0
}
1. 子类继承父类使用 extends 关键字
2. 重写方法最好加上 @override 注解重写
3. 子类构造方法中,如果要初始化父类构造方法,使用 super 关键字
4. 子类中调用父类的方法使用 super.fun()
实例:
class Animal
{
String name;
int age;
Animal(this.name, this.age);
void speak() {}
void printInfo()
{
print('My name is ${name}');
}
void parentPrint()
{
print('I am parent');
}
}
class Dog extends Animal
{
String nickName;
Dog(String name, int age, [String nickName]) : super(name, age);
@override
void speak()
{
this.parentPrint();
super.parentPrint();
print('wang wang!');
}
void setNickName(String name)
{
if (name != null)
{
this.nickName = name;
}
}
get fullInfo
{
return {"name": this.name, "age": this.age, "nickName": this.nickName};
}
}
void main()
{
Dog d = new Dog('dahuang', 3, 'kitty');
d.printInfo();
d.speak();
d.setNickName('Nicjski');
print(d.fullInfo);
// 运行结果:
// My name is dahuang
// I am parent
// I am parent
// wang wang!
// {name: dahuang, age: 3, nickName: Nicjski}
}
1. Mixins 类似于多继承,是在多类继承中重用一个类代码的方式
2. 作为 Mixins 的类不能有显示声明构造方法
3. 作为 Mixin 的类只能继承自 Object
1.使用 with 关键字来实现多继承,必须要先有 extends 然后才能使用 with 实现 mixins 操作
class A
{
void a()
{
print("A.a().....");
}
}
class B
{
void b()
{
print("B.b().....");
}
}
class C
{
void c()
{
print("C.c().....");
}
}
// 这里使用 mixins 来实现多继承,使用 with 关键字
// error: class D with A, B, C{},必须要现有 extends
class D extends A with B, C
{
}
void main(List args)
{
var d = new D();
d.a(); // A.a().....
d.b(); // B.b().....
d.c(); // C.c().....
}
2.mixins 操作的顺序问题
修改上面代码:
class A
{
void a()
{
print("A.a().....");
}
}
class B
{
void a()
{
print("B.a().....");
}
void b()
{
print("B.b().....");
}
}
class C
{
void a()
{
print("C.a().....");
}
void b()
{
print("C.b().....");
}
void c()
{
print("C.c().....");
}
}
class D extends A with B, C
{
}
void main(List args)
{
var d = new D();
d.a(); // C.a().....
}
由于,继承的顺序中,C 是在最后,所以就会去调用 C 中的 a 方法。修改这个继承顺序,那么就会看最后的那个类,再去执行最后那个类的方法。
多态举个例子通俗来讲,就是将改变子类实例的类型是父类,父类能够调用子类中同名的方法,输出结果与子类相同,但是不能调用父类没有子类中拥有的方法。
实际上就是将子类的方法覆盖父类的方法。
class Animal
{
speak()
{
}
}
class Dog extends Animal
{
@override
speak()
{
print('wang!');
}
run()
{
print('dog run!');
}
}
class Cat extends Animal
{
@override
speak()
{
print('miao');
}
run()
{
print('dog run!');
}
}
void main()
{
Animal d1 = new Dog();
Dog d2 = new Dog();
d1.speak();
// d1.run(); // Error: The method 'run' isn't defined for the class 'Animal'.
d2.speak();
d2.run();
// 运行结果:
// wang!
// wang!
// dog run!
}
1. 抽象类用 abstract 关键字声明
2. 抽象类中没有方法体的方法是抽象方法
3. 子类继承抽象类必须实现抽象类中的抽象方法
4. 抽象类作为接口使用的时候必须实现所有的属性和方法
5. 抽象类不能被实例化继承抽象类的子类可以
6. 作为抽象类 继承的子类不需要对属性重写
// 关键字 abstract
abstract class Animal
{
speak(); // 没有方法体的抽象方法,子类必须实现
printInfo() // 不需要实现
{
print('not abstract method');
}
}
// 子类继承这个抽象类
class Dog extends Animal
{
@override
speak() // 继承的子类必须实现这个方法
{
print('wang!');
}
}
// 子类继承这个抽象类
class Cat extends Animal
{
@override
speak() // 继承的子类必须实现这个方法
{
print('miao!');
}
}
void main()
{
Dog d = new Dog();
Cat c = new Cat();
d.speak(); // wang!
d.printInfo(); // not abstract method
c.speak(); // miao!
c.printInfo(); // not abstract method
}
1. 接口也是abstract关键字声明的
2. 接口需要使用implements关键字来实现
// 关键字 abstract
abstract class DB
{
String host;
String port;
String user;
String pass;
query(String data);
update(String data);
insert(String data);
delete(String data);
}
// 子类使用implements来实现
class Mysql implements DB
{
@override
String host;
@override
String pass;
@override
String port;
@override
String user;
Mysql(this.host, this.user, this.pass, [this.port = '3306'])
{
print('[ok] connect to ${this.host}:${this.port}, use ${this.user}:${this.pass}');
}
@override
delete(String data)
{
print('delete ${data}');
}
@override
insert(String data)
{
print('insert ${data}');
}
@override
query(String data)
{
print('query ${data}');
}
@override
update(String data)
{
print('update ${data}');
}
}
void main()
{
Mysql my = new Mysql('127.0.0.1', 'root', '123456', '3306');
my.insert('121');
my.update('121');
my.query('121');
my.delete('121');
// 执行结果:
// [ok] connect to 127.0.0.1:3306, use root:123456
// insert 121
// update 121
// query 121
// delete 121
}
abstract class A
{
fnA();
}
abstract class B
{
fnB();
}
class C implements A, B
{
@override
fnA()
{
print('FN------A');
}
@override
fnB()
{
print('FN------B');
}
}
void main()
{
C c = new C();
c.fnA(); // FN------A
c.fnB(); // FN------B
}
用抽象类:
继承的子类都用到了父类的同一个或多个方法或者属性的情况下
用接口:
继承的子类只是把父类作为一个模版和标准的时候需要自己全部实现属性和方法的时候
枚举类型,通常称为枚举,是一种特殊类型的类,用于表示固定数量的常量值。
1. 使用enum关键词来声明一个枚举类型
2. 枚举的每一个值都有一个index属性,index从0开始计数
3. 不能子类化、混合或实现枚举
4. 不能显式实例化枚举
enum Animal
{
cat,
dog,
bird
}
main(List args)
{
print(Animal.dog.index);
// 获取所有枚举值
List animals = Animal.values;
Animal dog = Animal.dog;
switch (dog)
{
case Animal.cat:
print("animal is cat");
break;
case Animal.dog:
print("animal is dog");
break;
default: // 没有default,将会报错
print("which animal?");
}
// 执行结果:
// 1
// animal is dog
}
this关键字引用类的当前实例。这里,参数名称和类字段的名称是相同的。因此,为了避免歧义,类的字段以this关键字为前缀。
class Car
{
String engine;
Car(String engine)
{
this.engine = engine;
print("The engine is : ${engine}");
}
}
void main()
{
Car c1 = new Car('EA888'); // The engine is : EA888
}
class StaticMem
{
static int num;
static disp()
{
print("The value of num is ${StaticMem.num}");
}
}
void main()
{
StaticMem.num = 12;
StaticMem.disp(); // The value of num is 12
}
supper关键字可用于引用超类的变量,属性或方法等
class Parent {
String msg = "message variable from the parent class";
void m1(int a)
{
print("Parent of a ${a}");
}
}
class Child extends Parent
{
@override
void m1(int b)
{
print("Child of b ${b}");
super.m1(13);
print("${super.msg}");
}
}
void main()
{
Child c = new Child();
c.m1(12);
// 运行结果:
// Child of b 12
// Parent of a 13
// message variable from the parent class
}
?
、is
、as
和 ..
操作符编号 | 操作符 | 说明 |
---|---|---|
1 | ? | 操作符用于判断如果实例存在则调用方法,否则不调用 |
2 | is | 用于判断是否归属于某个类的实例或者子类 |
3 | as | 用于声明某个实例当做某个类使用,比如 子类 as 父类 |
4 | .. | 级联操作符用于串联操作 |
class Person
{
String name;
int age;
Person(this.name, this.age);
void printInfo()
{
print('name is ${this.name}, age is ${this.age}');
}
}
void main()
{
// p.printInfo(); // NoSuchMethodError: The method 'printInfo' was called on null.
// ?条件运算符
Person p1;
p1?.printInfo();
Person p2 = new Person('postbird', 20);
p2?.printInfo();
// is
if (p2 is Person)
{
p2.name = 'newPers';
}
p2.printInfo();
print(p1 is Person);
// as
var p3 = p2;
// (p3 as Person).name = 'newPtbird';
// p3.printInfo();
(p3 as Person).printInfo();
// ..级联操作符
Person p4 = new Person('sada', 20);
p4..name = 'new Name'
..age = 22
..printInfo();
// 运行结果:
// name is postbird, age is 20
// name is newPers, age is 20
// false
// name is newPers, age is 20
// name is new Name, age is 22
}
默认类中的所有属性和方法是public的。在dart中,可以在属性和方法名前添加_
使私有化。现在让我们使name属性私有化。
class Person
{
String name; // 公有变量
int _age; // 私有变量
// get方法
String getName()
{
return name;
}
int getAge()
{
return _age;
}
// set方法
void setName(String name)
{
this.name = name;
}
void setAge(int age)
{
this._age = age;
}
}
void main()
{
var person = Person();
person.name = "lise"; // 也可以 person.setName("lise");
// person.age = 12; // error
person.setAge(12);
print(person.getName()); // lise
print(person.getAge()); // 12
}