第六节: Dart 面向对象/构造函数/静态属性

Dart 语法学习目录

第一节: Dart 语法了解,认识变量,常量,数据类型
第二节: Dart 操作符(运算符)
第三节: Dart 中流程控制,条件判断以及循环语句
第四节: Dart 中常用集合 List/Map
第五节: Dart 函数使用,声明函数/匿名函数/可选参数/作用域/闭包
第六节: Dart 面向对象/构造函数/静态属性
第七节: Dart中抽象类abstract/接口implements/混入Mixin
第八节: Dart中泛型/泛型定义与使用
第九节: Dart 中的库/自定义库/内置库/第三方库


1. 面向对象介绍

1.1 面向对象编程的认识

面向对象编程(OOP)的三个特征是: 封装,继承,多态

  1. 封装: 封装是对象和类概念的主要特征, 封装,把客观事物封装成抽象的类,并且把自己的部分属性和方法提供给其他对象
  2. 继承:面向对象编程语言的主要功能就是继承, 继承是指一种能力
  3. 多态: 允许将子类的指针赋值给父类类型的指针,同一个函数调用会有不同的执行效果


1.2 Dart的面向对象认识
  1. Dart 是一个面向对象编程语言,同时支持基于 mixin 的继承机制。
  2. 每个对象都是一个类的实例,所有的类都继承于 Object.
  3. 基于 Mixin 的继承 意味着每个类(Object 除外) 都只有一个超类,一个类的代码可以在其他多个类继承中重复使用。即一个类可以继承自多个父类。
  4. 使用关键字calss 声明一个类
  5. 使用关键字new 创建一个对象,new 可以省略。
  6. 对象的由函数和数据(即方法和实例变量)组成。
  7. 方法的调用要通过对象来完成: 调用的方法可以访问其对象的其他函数和数据。


2. 创建使用类

2.1 定义类

通过class定义类,并在类中定义属性和方法

示例:

class Person{
    // 定义属性
    String name = "小明";
    int age = 10;

    // 定义方法
    void getInfo(){
        print("姓名:${this.name}--年纪:${this.age}")
    }
}


2.2 使用类
void main(){
    // 注意:student是Person的实例,所以变量的类型就是 Person类
    Person student = new Person();
    print(student);  // Instance of 'Person' Person类的实例
    
    // 调用实例的方法
    student.getInfo(); // 姓名:小明--年纪:10

}



3. 默认构造函数

我们会发现,上一个例子中我们定义的方法,必须在我们手动调用的时候才会执行, 其实我们也可以定义类似于JS ES6 类中的constructor 函数这样的在实例化时默认调用的构造函数

默认构造函数的名称必须与类名相同


  1. 如果未显式定义默认构造函数,会默认一个空的构造函数。

  2. 默认构造函数没有参数,并且会调用超类的没有参数的构造函数。


3.1 未显示定义默认构造函数
class Person{
    String name = "小明";
    int age = 18;


    void getInfo(){
        print("name:$name---age: $age");
    }
}


3.2 定义默认构造函数

在通过这类实例化对象时会默认调用这个构造函数

// 定义类
class Person{
    // 定义属性
    String name = "小明";
    int age = 10;

    // 默认调用的构造函数
    Person(){
        print("我是在类实例化时默认调用的函数");
    }

    // 定义方法
    void getInfo(){
        print("姓名:${this.name}--年纪:${this.age}");
    }
}

// 入口函数
void main(){
    
    // 实例化类
    Person student = new Person();
    /*
        打印结果:
            我是在类实例化时默认调用的函数
    */

}

上面的例子虽然在实例化时执行了默认的构造函数, 但是我们会发现实例化对象的属性是没有变化的,

所以我们可以在默认构造函数中根据实例化时传递的参数动态改变实例对象的属性


3.3 默认构造函数传参
  1. 类在实例化是传入的动态参数会在默认构造函数执行时形参所接收.

  2. 其中this 关键字指当前的实例。

示例:

// 定义类
class Person{
    // 定义属性
    String name;
    int age;

    // 默认调用的构造函数
    Person(String name,int age){
        this.name = name;
        this.age = age;
    }

    // 定义方法
    void getInfo(){
        print("姓名:${this.name}--年纪:${this.age}");
    }
}

void main(){
    // 第一次实例化
    Person student = new Person("小明",8);
    student.getInfo();  // 姓名:小明--年纪:8

    // 第二次实例化
    Person student2 = new Person("小红",10);
    student2.getInfo();  //姓名:小红--年纪:10

}


3.4 默认构造函数传参简写

其实默认的构造函数,如果只是修改实例属性的话,我们还可以进行简写

class Person{
    // 定义属性
    String name;
    int age;

    // 默认调用的构造函数
    // 直接将实例化传递过来的实参直接复制给实例属性
    // 简写方式
    Person(this.name,this.age);

    // 定义方法
    void getInfo(){
        print("姓名:${this.name}--年纪:${this.age}");
    }
}

void main(){
    Person student = new Person("小明",8);
    student.getInfo();  // 姓名:小明--年纪:8

    Person student2 = new Person("小红",10);
    student2.getInfo();  //姓名:小红--年纪:10

}


3.5 实例化之前给属性赋值

在默认构造函数简写的情况下,接受实参的小括号后面添加:就可以在实例化执行默认构造函数之前给实例属性赋值;

可以在实例化之前给属性赋值,但是没什么意义

class Person{
    // 定义属性
    String name;
    int age;

    // 默认调用的构造函数
    // 实例化之前给尚属性赋值,没什么意义
    Person():name="默认",age=18{
        print("姓名:${this.name}--年纪:${this.age}");
    }

    // 定义方法
    void getInfo(){
        print("姓名:${this.name}--年纪:${this.age}");
    }
}

void main(){

    Person student = new Person();  // 姓名:默认--年纪:18

}

注意:


4. 命名构造函数,

命名构造函数,是可以在实例化时通过类调用的方法

说明:

  1. 使用命名构造函数可为一个类实现多个构造函数
  2. 也可以使用命名构造函数来更清晰的表明函数意图
  3. 构造函数不能够被继承, 这意味着父类的命名构造函数不会被子类继承

调用的方法:类名.命名构造函数

class Person{
    // 定义属性
    String name;
    int age;

    // 默认调用的构造函数
    Person(this.name,this.age);

    // 命名构造函数
    Person.paly(this.name){
        print("${this.name}在打游戏");
    }

    // 定义方法
    void getInfo(){
        print("姓名:${this.name}--年纪:${this.age}");
    }
}

void main(){

    // 通过类来调用的方法
    Person.paly("小明");  // 小明在打游戏
}

可以把命名构造函数当成静态方法来理解


5. 常量构造函数

常量构函数就是希望每次创建的对象都是同一个对象赋值给不同的变量.

说明:

  1. 如果该类生成的对象是固定不变的, 那么就可以把这些对象定义为编译时常量。
  2. 需要定义一个 const 构造函数, 并且声明所有实例变量为 final
  3. 要使用常量构造函数只需要用 const 替代 new 即可,也可以省略const

示例:

class Person{
    final name = "小明";
    final age = 18;

    const Person();
    void getInfo(){
        print("name:$name---age: $age");
    }
}

void main(){

    // 通过new 也可以创建一个对象,但不是常量构造函数
    Person student= new Person();
    student.getInfo();  // name:小明---age: 18

    Person student2= const Person();
    student3.getInfo();

    Person student3 = const Person();
    student3.getInfo(); // name:小明---age: 18

    // 可以通过identical方法来比较创建的对象是不是同一个对象
    print(identical(student, student2));   // false
    print(identical(student2, student3));  // true
} 

通过结果我们可以发现, 通过new关键字创建出来的对象和通过const创建出来的对象不是同一个

但是两个const创建出来的对象是同一个对象.


6. 可以 将类抽离为单独的文件

可以将类抽离为单独的文件, 然后通过import 引入, 这样模块化的开发方式利于多协同开发

// 抽离的类
// lib/Person.dart

class Person{
    // 定义属性
    String name;
    int age;

    // 默认调用的构造函数
    Person(this.name,this.age);

    // 命名构造函数
    Person.paly(this.name){
        print("${this.name}在打游戏");
    }

    // 定义方法
    void getInfo(){
        print("姓名:${this.name}--年纪:${this.age}");
    }
}

引入类

// main.dart
// 引入模块的文件
import "lib/Person.dart";

void main(){
    
  // 然后就可以使用抽离的类了
  Person.paly("小明");  // 小明在打游戏
  Person student = new Person("小明", 8);
  student.getInfo();  //姓名:小明--年纪:8
}


7. 私有属性私有方法

dart中定义私有属性或方法通过_来定义

但是要注意, 类必须抽离成单独的文件私有属性和方法才可以使用 ,否则没有效果


7.1 定义类和使用类在同一文件里

定义类和使用类在同一文件里, 私有属性没用

class Person{
    // 定义属性
    // _name 为私有属性
    String _name;
    int age;

    // 默认调用的构造函数
    Person(this._name,this.age);


    // 定义方法
    // _getInfo为私有方法
    void _getInfo(){
        print("姓名:${this._name}--年纪:${this.age}");
    }
}

void main(){

    Person student = new Person("小明", 8);
    student._getInfo();  //姓名:小明--年纪:8
}


7.2 类的创建和调用在不同的文件
// 定义类
// lib/Person.dart
class Person{
    // 定义属性
    // 定义私有属性_name
    String _name;
    int age;

    // 默认调用的构造函数
    Person(this._name,this.age);


    // 私有方法
    void _getInfo(){
        print("姓名:${this._name}--年纪:${this.age}");
    }

    // 实例方法
    // 通过实例方法getInfo调用私有方法_getInfo
    // 也就是说, 私有属性和私有方法,
    void getInfo(){
        this._getInfo();
    }
}

调用类, 私有属性方法只能通过实例方法去调用获取

void main(){

    Person student = new Person("小明", 8);
    // student._getInfo();  //  实例调用私有方法报错
    // print(student._name); // 实例调用私有属性报错
    // print(Person._name);  // 类调用私有属性也报错
    
    print(student.age);   // 8
    // 私有属性和私有方法只能在实例的方法中调用
    student.getInfo();   // 姓名:小明--年纪:8
}


8. 类中getter 和setter修饰符

getter和setter定义的函数名不能更属性重名, 因为getter,setter定义的函数是计算属性, 也就是是函数名会是属性, 函数的返回值会自动计算为属性的值;

说明:

  1. GetterSetter 是用于对象属性读和写的特殊函数。
  2. 每个实例变量都有一个隐式 Getter ,如果变量不是final,通常情况下还会有一个 Setter
  3. 使用 getset 关键字实现 Getter 和 Setter ,能够为实例创建新属性。
  4. 计算属性的值是通过计算而来,本身不存储值。
  5. 计算属性赋值,其实是通过计算转换到其他实例变量。

实例:

class Person{
    // 定义属性
    String _name;
    int age;

    // 默认调用的构造函数
    Person(this._name,this.age);


    // 获取计算属性
    get getInfo{
        return "${this._name}:${this.age}";
    }

    // 设置计算属性
    set setName(value){
        this._name = value;
    }
}

void main(){

    Person student = new Person("小明", 8);
    
    // 操作计算属性
    print(student.getInfo);  // 小明:8
    student.setName = "小红";
    print(student.getInfo);  // 小红:8
}


9. 静态属性和静态方法

dart中的静态成员

  1. 使用static 关键字来实现类级别的变量和函数
  2. 静态方法不能访问非静态成员, 非静态方法可以访问静态成员
  3. 静态方法不能在类的实例上使用, 因此无法访问 this(this值得是类的实例)
  4. 静态变量对于类级别的状态是非常有用的。
  5. 静态变量在被使用的时候才被初始化。

实例

class Person{
    // 静态属性
    static String name ="小明";
    // 非静态属性
    int age;

    // 默认调用的构造函数
    Person(name,this.age);                 


    // 静态方法
    static void getInfo(){
        print(name);


        // 静态方法不能调用非静态属性或方法
        // 静态方法中不能使用this
        // print("${name}:${this.age}"); // 报错
        // this.setName();    // 报错
    }

    // 非静态方法
    void setName(){
        // 非静态方法可以调用静态属性或方法,调用的使用不需要通过this调用
        print("${name}:${this.age}");
        getInfo();
    }
}

void main(){

    Person student = new Person("小明", 8);
    // 使用类调用静态方法,
    Person.getInfo();    // 小明

    // 实例调用非静态方法, 非静态方法可以调用静态方法
    student.setName();
    /*
   * 小明:8
   * 小明
   * */
}


10. 对象的操作符

  1. ? 条件运算符(了解)
  2. as 类型转换
  3. is 类型判断
  4. .. 级联操作(链式调用)


10.1 条件运算符,

判断是以对象是存在,存在就调用方法, 不存在就不调用方法

class Person{
    // 属性
    String name ="小明";
    int age = 18;

    void getInfo(){
        print("${name}:${this.age}");
    }
}

void main(){
    // 这么调用没有任何问题
    // Person student = new Person();
    // student.getInfo();
    
    // 如果没有实例化呢
    // Person student ;
    // 没有实例化就调用方法会报错
    // student.getInfo();
    
    // 所以我们要先判断在调用
    Person student ;
    // 如果student 有这个方法就调用没有,也不会报错
    student?.getInfo();
}


10.2 类型转换

类型转换使用关键字 as

class Person{
    // 属性
    String name ="小明";
    int age = 18;


    void getInfo(){
        print("${name}:${this.age}");
    }


}

void main(){
    var student;
    student = '';
    student = new Person() ;
    // 现在这么调用没有什么问题, 以前会报错
    // 就是student 不能识别自己是字符串类型还是Person类型
    // student.getInfo();
    
    // 所以以前需要类型转换
    (student as Person).getInfo()
}


10.3 类型判断

类型判断使用is

class Person{
    // 属性
    String name ="小明";
    int age = 18;

    void getInfo(){
        print("${name}:${this.age}");
    }
}

void main(){
    var student;
    student = '';
    student = new Person() ;
    // 判断是不是Person类型
    print(student is Person); // true
}

其他类型的判断

void main(){
  var str = "";
  print(str is String);   // true
    
  int num = 123;
  print(num is int);   // true
    
  bool boolean = true;
  print(boolean is bool);   // true
    
  List list = ['a','b'];
  print(list is List);   // true
    
  Map map = {"name":"aa"};
  print(map is Map);   // true
    
}


10.4 联级调用

就是可以通过..联级一次给实例添加多个属性

class Person{
    // 属性
    String name ="小明";
    int age = 18;
    String sex = "男";


    void getInfo(){
        print("${this.name}:${this.age}:${this.sex}");
    }


}

void main(){
    Person student = new Person() ;
    student.getInfo();  // 小明:18:男

    // 修改属性值
    // student.name = "小红";
    // student.age = 14;
    // student.sex = "女";
    // student.getInfo();   // 小红:14:女

    // 修改多个属性值可以使用联级调用
    // 结果完全一样
    student..name = "小红"
        ..age = 14
        ..sex = "女";
    student.getInfo();   // 小红:14:女
}


11. Dart中类的继承

  1. 子类使用extents关键词来继承父类

  2. 子类会继承父类里面可见的属性和方法, 但不会继承构造函数

  3. 子类能覆写父类的方法


11.1 继承父类

子类使用extents关键字继承父类

// 父类
class Person{
    // 属性
    String name ="小明";
    int age = 18;

    // 方法
    void getInfo(){
        print("${this.name}:${this.age}");
    }
}

// 子类
// 子类通过extends 继承父类
class Student extends Person{

}

void main(){

    //  通过子类实例化对象
    Student student  = new Student();
    // 继承父类的属性
    print(student.name); // 小明
    // 继承了父类的方法
    student.getInfo();  // 小明:18

}


11.2 使用关键字super来给父类的构造函数传参

实例化子类给父类传值

class Person{
    // 属性
    String name ;
    int age ;

    // 构造函数
    Person(this.name, this.age);

    // 方法
    void getInfo(){
        print("${this.name}:${this.age}");
    }
}

// 子类
// 如果父类的属性是需要通过构造函数传参的
// 那么子类在继承的时候就需要通过super来给父类传参
class Student extends Person{
    // 子类先后去实例化时传递过来的实参
    // 子类获取实参后再将数据通过super 传递给父类的构造函数
    Student(String name,int age):super(name,age);
}

void main(){

    //  通过子类实例化对象
    Student student  = new Student("小明", 18);
    // 继承父类的属性
    print(student.name); // 小明
    // 继承了父类的方法
    student.getInfo();  // 小明:18


}


同时我们也可以扩展自己的属性和方法

// 父类
class Person{
    // 属性
    String name ;
    int age ;

    // 构造函数
    Person(this.name, this.age);

    // 方法
    void getInfo(){
        print("${this.name}:${this.age}");
    }

}


// 子类
class Student extends Person{
    // 扩展子类自己的属性
    String sex;
    Student(String name,int age,String sex):super(name,age){
        this.sex = sex;
    }

    // 扩展子类自己的方法
    void getStudentInfo(){
        print("${this.name}:${this.age}:${this.sex}");
    }
}


void main(){

    //  通过子类实例化对象
    Student student  = new Student("小明", 18,"男");
    // 继承父类的属性
    print(student.name); // 小明
    // 继承了父类的方法
    student.getInfo();  // 小明:18

    // 调用子类自己的方法
    student.getStudentInfo(); // 小明:18:男


}


还可以给父类里的命名构造函数传参

// 父类
class Person{
    // 属性
    String name ;
    int age ;

    // 构造函数
    Person(this.name, this.age);

    // 父类的命名构造函数
    Person.sayHello(String name){
        print("${name} 说你好");
    }

    // 方法
    void getInfo(){
        print("${this.name}:${this.age}");
    }

}


// 子类
class Student extends Person{
    // 扩展子类自己的属性
    String sex;
    Student(String name,int age,String sex):super(name,age){
        this.sex = sex;
    }
    
    // 给父类的命名构造函数传参
    Student.sayHello(String name):super.sayHello(name);

    // 扩展子类自己的方法
    void getStudentInfo(){
        print("${this.name}:${this.age}:${this.sex}");
    }
}


void main(){

    //  通过子类实例化对象
    Student student  = new Student("小明", 18,"男");

    // 调用子类的命名构造函数
    Student.sayHello("哈哈");  // 哈哈 说你好
}


11.3 覆写父类方法

所谓的复写,就是子类定义一个更父类同名的方法, 当子类调用这个同名方法的时候,优先使用子类自己的方法, 看起来就像把父类的同名方法覆盖了

覆写父类的方法时建议使用关键字: @override(可写可不写)

// 父类
class Person{
    // 属性
    String name = "小明";
    int age = 18 ;

    // 方法
    void getInfo(){
        print("${this.name}:${this.age}");
    }
}


// 子类
class Student extends Person{

    // 覆写父类的方法
    @override  // 覆写的时候加上这个,不写也可以,一般写上会比较好,知道这个方法是覆写方法
    void getInfo(){
        print("覆写了父类的方法");
    } 
}

void main(){

    //  通过子类实例化对象
    Student student  = new Student();

    student.getInfo();  // 覆写了父类的方法

}

11.4 子类扩展父类的方法

子类方法里调用父类的方法

对父类的方法进行扩展

// 父类
class Person{
    // 属性
    String name = "小明";
    int age = 18 ;

    // 方法
    void getInfo(){
        print("${this.name}:${this.age}");
    }
}


// 子类
class Student extends Person{

    // 调用父类方法, 扩展父类方法的功能
    void sayHello(){
        // 在子类的方法里调用父类的方法
        super.getInfo();

        //  扩展父类方法的功能
        print("${this.name}说你们好");
    }
}


void main(){

    //  通过子类实例化对象
    Student student  = new Student();

    student.sayHello();  // 扩展父类功能的子类方法
    /*
    小明:18
    小明说你们好
  */
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335