1、面向对象编程(Object-Oriented Programming, OOP)三大特征
(1)封装
- 封装是指将对象的状态(属性)和行为(方法)封装在一起,对外部隐藏对象的内部细节,只提供公共的访问方式。
- 通过封装,可以保护对象的状态不被外部直接访问和修改,只能通过对象提供的接口进行操作,提高了安全性和可维护性。
(2)继承
- 继承是指一个类(子类)可以从另一个类(父类)中继承属性和方法的机制。
- 子类可以继承父类的公共成员,并且可以通过重写(Override)或扩展(Extend)父类的方法来实现特定的行为。
- 继承可以减少代码的重复性,提高代码的可重用性和可扩展性。
(3)多态
- 多态是指同一个方法调用可以根据对象的不同类型而具有不同的行为。
- 多态可以通过继承和接口实现,其中包括重载(Overloading)和重写(Overriding)两种形式。
- 多态可以提高代码的灵活性和扩展性,使得程序能够更好地应对不同的需求和情况。
重载(Overloading)
// 相同方法名,不同参数
fun foo() {}
fun foo(str: String?) {}
重写(Overriding)
// 子类重新定义(覆盖)了父类中已有的方法
open class Animal{
open fun makeSound(){
System.out.println("Animal makes a sound");
}
}
class Cat : Animal(){
override fun makeSound() {
System.out.println("Cat makes a sound");
}
}
2、Dart 中的类与对象
(1)类与对象
void main() {
Person person = Person("leon", 18);
print(person.toString()); //name: leon age: 18
}
//所有的类均继承自 Object
class Person {
String? name;
int? age;
Person(this.name, this.age);
//重写父类的方法
String toString() {
return "name: $name age: $age";
}
}
(2)初始化列表
void main() {
Student student1 = Student("艾伦", 19, '清华大学');
print(student1.toString()); //name: 艾伦 age: 19 school: 清华大学 city: null country: 中国 fulName: 中国.null
Student student2 = Student("三笠", 20, '复旦大学', city: '上海');
print(student2.toString()); //name: 三笠 age: 20 school: 复旦大学 city: 上海 country: 中国 fulName: 中国.上海
Student student3 = Student("莱纳", 21, '哈佛大学', city: '剑桥', country: '美国');
print(student3.toString()); //name: 莱纳 age: 21 school: 哈佛大学 city: 剑桥 country: 美国 fulName: 美国.剑桥
}
class Student extends Person {
//通过查下划线标识私有字段,作用域是当前文件
String? _school;
String? city;
String? country;
String? funName;
/**
* 初始化列表 funName = '$country.$city'
* 必填项 name. age, _school
* 选填项 city, country
* 带默认值 country
*/
Student(name, age, this._school, {this.city, this.country = "中国"})
: funName = '$country.$city', super(name, age) {
//构造函数体不是必须的
}
String toString() {
return "name: $name age: $age school: $_school city: $city country: $country fulName: $funName";
}
}
(3)命名构造方法
- 命名构造方法,在类的里面,通过 “类名.方法名” 的方式构造
- 使用命名构造方法可以实现多个构造方法
void main() {
Student s1 = Student.create1("炭治郎");
print(s1.toString()); //name: 炭治郎 age: 0 school: null city: null country: null
Student s2 = Student.create2('祢豆子', 4);
print(s2.toString()); //name: 祢豆子 age: 4 school: null city: null country: null
Student s3 = Student.create3('利威尔', 24, '巨人学院');
print(s3.toString()); //name: 利威尔 age: 24 school: 巨人学院 city: null country: null
}
class Student extends Person {
//通过查下划线标识私有字段,作用域是当前文件
String? _school;
String? city;
String? country;
String? funName;
Student(name, age) : super(name, age) {
//构造函数体不是必须的
}
/**
* 命名构造方法:类名.方法名
*/
Student.create1(name) : super(name, 0) {}
Student.create2(name, age) : super(name, age) {}
Student.create3(name, age, school) : super(name, age) {
_school = school;
}
String toString() {
return "name: $name age: $age school: $_school city: $city country: $country";
}
}
注:当有 final 变量时,命名构造方法必须初始化它
注:构造函数中也必须初始化 final 变量
void main() {
Student s1 = Student.create1("炭治郎", '鬼灭学院');
print(s1.toString()); //name: 炭治郎 age: 0 school: 鬼灭学院 city: null country: null
Student s2 = Student.create2('祢豆子', 4, '鬼灭学院');
print(s2.toString()); //name: 祢豆子 age: 4 school: 鬼灭学院 city: null country: null
Student s3 = Student.create3('利威尔', 24, '巨人学院', '玛利亚', '日本');
print(s3.toString()); //name: 利威尔 age: 24 school: 巨人学院 city: 玛利亚 country: 日本
}
class Student extends Person {
//通过查下划线标识私有字段,作用域是当前文件
final String? _school;
String? city;
String? country;
String? funName;
//构造函数中也必须初始化 final 变量
Student(name, age, this._school) : super(name, age) {
//构造函数体不是必须的
}
/**
* 命名构造方法中必须 初始化 final 变量
*/
Student.create1(name, this._school) : super(name, 0) {}
Student.create2(name, age, this._school) : super(name, age) {}
Student.create3(name, age, this._school, this.city, country) : super(name, age) {
this.country = country;
}
String toString() {
return "name: $name age: $age school: $_school city: $city country: $country";
}
}
(4)命名工厂构造方法
- 命名工厂构造方法,在类的里面,通过 " factory 类名.方法名" 方式构造
- 会返回一个实例对象
void main() {
Student s1 = Student.generate1('冯宝宝', 200);
print(s1.toString()); //name: 冯宝宝 age: 200 school: null city: null country: null
Student s2 = Student.generate2('张楚岚', 18, '野鸡大学');
print(s2.toString()); //name: 张楚岚 age: 18 school: 野鸡大学 city: null country: null
}
class Student extends Person {
//通过查下划线标识私有字段,作用域是当前文件
String? _school;
String? city;
String? country;
String? funName;
Student(name, age) : super(name, age) {
//构造函数体不是必须的
}
/**
* 命名工厂构造方法:factory 类名.方法名
* 它会返回一个实例对象
*/
factory Student.generate1(name, age) {
return Student(name, age);
}
factory Student.generate2(name, age, school) {
Student s = Student(name, age);
s._school = school;
return s;
}
String toString() {
return "name: $name age: $age school: $_school city: $city country: $country";
}
}
注:命名工厂构造方法无需初始化 final 变量
void main() {
Student s1 = Student.generate1('冯宝宝', 200);
print(s1.toString()); //name: 冯宝宝 age: 200 school: 未知 city: null country: null
Student s2 = Student.generate2('张楚岚', 18, '野鸡大学');
print(s2.toString()); //name: 张楚岚 age: 18 school: 野鸡大学 city: null country: null
}
class Student extends Person {
//通过查下划线标识私有字段,作用域是当前文件
final String? _school;
String? city;
String? country;
String? funName;
//构造函数中也必须初始化 final 变量
Student(name, age, this._school) : super(name, age) {
//构造函数体不是必须的
}
/**
* 命名工厂构造方法:factory 类名.方法名
* 它会返回一个实例对象
*/
factory Student.generate1(name, age) {
return Student(name, age, '未知');
}
factory Student.generate2(name, age, school) {
return Student(name, age, school);
}
String toString() {
return "name: $name age: $age school: $_school city: $city country: $country";
}
}
(5)工厂构造方法
- 通常是通过它实现单例
void main() {
LogUtils log1 = LogUtils();
LogUtils log2 = LogUtils();
//若是单例,则 log1 == log2
print("log1 与 log2 是否为同一个单例: ${log1 == log2}"); //log1 与 log2 是否为同一个单例: true
//调用单例内的方法
log1.log("输出日志 test"); //输出日志 test
}
/**
* 工厂构造方法 实现单例
*/
class LogUtils {
//静态私有成员
static LogUtils? _instance;
//命名构造方法,私有方法
LogUtils._getInstance();
//工厂构造方法
factory LogUtils() {
// ??=:表示若 _instance 为空,则取等号后面的值
//若 _instance 不空,则继续执行下面代码,返回 _instance
_instance ??= LogUtils._getInstance();
return _instance!;
}
log(String msg) => print(msg);
}
3、Dart 中的 get 和 set 方法
- 类中的私有属性只能在当前文件中使用,在其他文件中无法直接使用
如,在 main1.dart 中定义 People,在 main2.dart 中调用其私有属性,就会报错
main1.dart
class People{
String? name;
int? _age;
People(this.name, this._age);
}
main2.dart
- 使用 get 和 set 可以实现访问其他文件中定义的类中的私有属性
main1.dart
class People {
String? name;
int? _age;
//通过 getter 方法来让外界获取到私有字段
int? get AGE1 {
return _age;
}
// => 简化写法
int? get AGE2 => _age;
//setter 方法
set setAge(int age){
_age = age;
}
People(this.name, this._age);
}
main2.dart
import 'package:leon/main1.dart';
void main(){
People people = People("leon", 18);
print("name: ${people.name}"); //leon
print("age: ${people.AGE1}"); //18
print("age: ${people.AGE2}"); //18
people.setAge = 20;
print("age: ${people.AGE2}"); //20
}
4、Dart 中的抽象类和方法
- Dart 中是没有 interface 的,所以通过抽象类来定义接口
- 通过 extends 继承抽象类,需要实现抽象方法
- 通过 implements 实现抽象类,必须实现抽象类中所有方法
// 抽象类
abstract class Study {
//抽象类中没有方法体的就是抽象方法
void write();
void read() {
print("在阅读");
}
}
// 通过 extends 继承抽象类,需要实现抽象方法
class Student1 extends Study {
void write() {
print("学生1在写作");
}
}
//通过 implements 实现抽象类,必须实现抽象类中所有方法
class Student2 implements Study{
void read() {
print("学生2在阅读");
}
void write() {
print("学生2在写作");
}
}
void main() {
Student1 s1 = Student1();
s1.read(); //在阅读
s1.write(); //学生1在写作
Student2 s2 = Student2();
s2.read(); //学生2在阅读
s2.write(); //学生2在写作
}
5、mixins
- mixins 特征:实现 mixin,创建一个继承 Object 类的子类(不能继承其他类),不声明任何构造方法,不调用 super
- mixins 使用:在 with 关键字之后跟一个或多个 mixin 的名字(逗号隔开),并且 with 要用在 extends 关键字之后
- mixins 作用:Dart中的mixins提供了一种在类中复用代码的方式,类似于多继承的概念。通过使用mixins,你可以将一个或多个mixin类的功能合并到一个类中,而无需创建复杂的继承层次结构。这有助于避免一些与传统多继承相关的问题
基类
class Person {
String? name;
Person(this.name);
display() {
print("姓名:$name");
}
}
mixin 类
mixin StudyMixin {
//抽象方法
void write();
//抽象类中可以包含有方法体的方法
void read() {
print("在阅读");
}
}
mixin 类
mixin LearnMixin {
void learnChinese();
void learnEnglish() {
print("在学英语");
}
}
实现类
class Student extends Person with StudyMixin, LearnMixin {
Student(name) : super(name);
void learnChinese() {
print("$name 在学中文");
}
void write() {
print("$name 在写作");
}
}
执行
void main() {
Student student = Student("Leon");
student.display(); //姓名:Leon
student.write(); //Leon 在写作
student.read(); //在阅读
student.learnChinese(); //Leon 在学中文
student.learnEnglish(); //在学英语
}