Dart语言基础(五)之面向对象基础特性

简介

Dart 是一种面向对象语言,包含类和基于 Mixin 的继承两部分。每个对象是一个类的实例,并且 Object 是所有类的父类。基于 Mixin 的继承指的是每个类(除了 Object )都只有一个父类,类体还可以在多个类继承中被重用。

内容

主要以一下几个方面来讲对象:

  • 类与对象、声明、创建及基本特性
  • 构造方法及初始化列表
  • 静态成员及对象操作符的使用

类与对象

  • 使用关键字class声明一个类
  • 使用关键字new创建一个对象,new可省略
  • 所有对象都继承于Object类
void main() {
            
    //创界对象
    // var person = new Person();
    //new 可以省略
    var person = Person();
}
        
//使用class声明一个类
class Person {

}

属性与方法

  • 属性默认会生成getter和setter方法
  • 使用final声明的属性只有getter方法(只读)
  • 属性和方法通过.(点语法)访问
  • 方法不能被重载
void main() {
        
    var person = Person();
    //setter 方法赋值
    person.name = '张三';
    person.age = 24;  
    // person.address = 'aaa';//报错,address是只读属性
    person.work();
            
    //getter方法取值
    print(person.name);
    print(person.age);
    print(person.address);
}

class Person {
            
    String name;
    int age;
    final String address = '北京市';
    void work() {

        print('Name:$name, age:$age,he is working');
    }
    //报错该方法已经存在,说明不能重载
    // void work(int num) { }
}

类及成员的可见性

  • Dart中的可见性以library(库)为单位
  • 默认情况下,每个Dart文件就是一个库
  • 使用_表示库的私有性
  • 使用import导入库
//创建一个类为Person的dart文件
    //class _Person //下划线表示私有类
class Person {
        
    String _name; //私有成员变量
    int _age;
    final String _address = '北京市';
    //下划线就是表示私有方法
    void _work() {

    print('Name:$name, age:$age,he is working');
  }
}
        
    //这样就可以用Person
import 'Person.dart';
        
void main() {
            
    //报错,_Person是私有类不能访问
    //var person = _Person();
    var person = Person();
            
    //私有变量访问不到
    person._name = '张三';//报错            person._age = 24; //报错
    print(person._address); //报错、       
    //私有方法访问不到
    person._work(); //报错 
}

计算属性

  • 计算属性的值是通过计算而来,本身不存储值
  • 计算属性赋值,其实是通过计算转换到其他实例变量

先写通过area方法,通过width、height来计算矩形面积。

void main() {

    var rect = Rectangle ();
    rect.width = 10;
    rect.height = 20;
    print(rect.area());//200 
}

class  Rectangle {
    num width,height;
    //通过area方法获取面积
    //num area() {
    //  return width * height;
    //}
    //前面讲过只有一行表达式可以简化
    num area()=>width * height;
}

用计算属性来获取面积,通过计算属性来获取width、height值

void main() {

    var rect = Rectangle ();
    rect.width = 10;
    rect.height = 20;
    //通过width、height计算出来的
    print(rect.area); //200
    
    //计算属性area赋值
    rect.area = 300;
    //width 通过计算属性获取的
    print(rect.width); //30.0
}

class  Rectangle {
    num width,height;
    //计算属性自定义area的getter、setter方法
    num get area {
        return  width * height;
    }
    //set方法通过计算属性来获取成员变量width
    set area(value) {

        width = value/10;
    }
}

构造方法

  • 如果没有自定义构造方法,则会有个默认的构造方法
void main() {
            
    var person = Person();
    person.name = '张三';
    person.age = 20;
    print(person.name); //张三
    print(person.age); //20
}

class Person {
            
    String name;
    int age;
    //这个就是Person默认的构造方法,默认的可以不写
    //这个是默认的方法,只不过默认没有显示出来
    Person() {
        
    }
}

  • 如果存在自定义构造方法,则默认构造方法无效
void main(List<String> args) {
            
    //var person = Person()无效了
    var person = Person('张三',   20);        
        print(person.name); //张三
        print(person.age); //20
    }

class Person {
  
    String name;
    int age;
    //自定义构造方法并且给类属性变量赋值
    //默认的构造方法无效
    Person(String name,int age){
    
        //this.是表示访问当前实例的变量
        this.name = name;
        this.age = age;
    }
    //用构造方法给当前类的变量赋值还有一种更简洁的方法
    //Person(this.name, this.age);
}

  • 构造方法不能重载,想实现多个构造方法,用下面的命名构造方法
class Person {

    String name;
    int age;
    Person(this.name, this.age);
    Person(String name);//报错,不能重载
}
    

命名构造方法

  • 使用命名构造方法,可以实现多个构造方法
  • 使用类名.方法的形式实现
Person.withName(String name) {
    this.name =name;
}
        
Person.withAge(int age) {
    this.age = age;
}
        
//调用时候提示就会多这个构造方法的提示
var person = Person.withName('李四');
print(person.name);
        
var person1 = Person.withAge(20);
print(person1.age);

常量构造方法

  • 如果类是不可变状态,可以把对象定义为编译时常量
  • 常量类中要使用const 声明构造方法,并且所有的变量都为final
  • 使用const声明对象,可以省略
void main() {
  
    //用const声明对象 
    const person = const Person('张三',20,'男');//第二个const可以省略
    print(person.name); //张三
    print(person.age);  //20
    print(person.gender); //男
}

class Person {
  
    //final只赋值一次,类里面所有的变量都要用final声明
    final String name;
    final int age;
    final String gender;
    //构造方法也要声明const
    const Person(this.name,this.age,this.gender);
}

工厂构造方法

工厂构造方法注意点:

  • 工厂构造方法类似于设计模式中的工厂模式
  • 在构造方法前添加factory实现一个工厂构造方法
  • 在工厂构造方法中可返回对象

有一些和工厂(factory)相关的设计模式。当你需要一些类的实例,但是想以更灵活一点的方式,而不是直接用硬编码的方式调用具体类型的构造函数的话,这时这些工厂模式开始发挥作用了。如果你已经有了一个实例也许会想返回之前已经缓存的实例,或者也许你想返回一个不同类型的对象。

Dart支持这种模式,但不要求你改变创建对象时的样子。而是让你定义一个工厂构造函数。当你调用它的时候看起来像一个普通的构造函数。但是实现可以做任何它想做的事情。例如:

class Symbol {  
    final String name;  
    //工厂构造方法无法访问this,所有这里要用static
    //维护一个缓存的Map对象
    static Map<String, Symbol> 
    _cache = <String, Symbol>{};  
    //调用该构造方法的时候,会从Map中取出对象
    factory Symbol(String name) {  
        if (_cache == null) {  
            _cache = {};  
        }  
        //如果_cache有这个对象就直接取
        if (_cache.containsKey(name)) {  
            return _cache[name];  
        } else { 
         //没有的话创建一个并且添加到_cache中 
            final symbol = new Symbol._internal(name);  
            _cache[name] = symbol;  
            return symbol;  
        }  
    }   
    Symbol._internal(this.name);  
} 

这里我们有一个表示符号的类。一个符号就像一个字符串,但是我们保证在任意时间点上一个给定的名字只会有一个符号对象。这让你能安全地比较两个对象的相等性仅仅通过测试他们是同一个对象。

这里的默认(未命名)构造函数前加上了 factory 前缀。它告诉Dart这是一个工厂构造函数。当它被调用时,它不会创建一个新对象。(工厂构造函数中没有 this 对象)。相反,期望你创建一个实例并明确地返回它。这里我们用给定的名字查找之前缓存的对象,如果找到了就重用它。

最酷的是调用者根本看不到这点。它们只需要:

var a = new Symbol('something');  
var b = new Symbol('something'); 

第二个 new 将返回之前缓存的对象。这很好,因为这意味着如果我们起初不需要工厂构造函数但之后又认识到需要时,我们将不必把所有之前使用new的地方都改为使用静态方法调用。

初始化列表

  • 初始化列表会在构造方法体执行之前执行
  • 初始化方法括号后(:初始化表达式)
  • 使用逗号分隔初始化表达式
  • 初始化列表常用于设置final变量的值(最大的用途)
class Person {
  String name;
  int age;
  final String gender;
  //初始化列表执行在构造方法前,多个初始化用逗号表示。
  Person(String name, int age, String gender):name = name,gender=gender {
    this.age =age;
  }

  //初始化name、age、gender,然后构造方法里面什么没实现可以这样简写
  Person.withMap(Map map):gender = map['gender'],name=map['name'],age= map['age'];

  //初始化应该在构造方法前执行,用this.传参相当在构造方法执行赋值简写了。
  Person.withInit(this.name,this.age,String gender):gender=gender;

  //和Person.withInit是一样的,只不过withInit简化了写法。
  //初始化列表是还是在构造方法前执行
  Person.withMuInit(String name, int age, String gender):gender=gender {

    this.name = name;
    this.age = age;
  }

静态成员

  • 使用static关键字来实现类级别的变量和函数
  • 静态成员不能访问非静态成员,非静态成员可以访问静态成员
  • 类中的常量需要使用static const声明
class Static_Person {
  
  //要在类里面声明常量,得用static const声明,只用const会出错
  static const int num = 0;
  //要是能被静态成员访问必须也是静态的类型
  static int curPage = 1; 
  //用static声明就是类级别,实例对象不能调用
  static void add() {
    //curPage要不是静态成员,静态成员不能访问非静态成员会报错
    curPage ++;
    print(curPage);
  }

  void sub() {

    //curPage是成员变量,sub方法是非静态的,非静态可以访问静态
    curPage--;
    print(curPage);
  }
}

调用的时候代码:

  var sPerson = Static_Person();
  //add已经是静态成员方法,不能用实例对象来访问
  // sPerson.add(); 
  Static_Person.add(); //静态方法 
  sPerson.sub(); //非静态方法

对象操作符

  • 条件成员访问:?.(表示?.前面对象为空,不往下执行),不为空才往下执行
void main(List<String> args) {
  
  Person person;
  //会报错,空对象不能调用work方法
  // person.work();  
  
  //?.表示的?.前面的对象为空的不往下执行
  //可有有效的避免空指针
  person?.work();//不会执行work方法
}

class Person {
  String name;
  int age;

  void work(){
    print('person is working');
  }
}
  • 类型转换:as(当不确定当前对象是什么类型使用,让对象确定就是这个类型)
void main() {
  
  //用var声明变量没初始化就会变成dynamic
  var person;
  person = 'aa'; //dynamic类型
  //虽然声明对象,但person还是dynamic类型
  //还是不确实此类型是就是person
  //当你调用属性或者方法没有提示
  person = new Person(); 

  //这样的话又变成字符串的dynamic类型
  person = 'aa'; 

  person = new Person(); 
  //用as明确person是Person
  //确定类型之后调用work方法就有代码提示
  (person as Person).work();

}

class Person {
  String name;
  int age;

  void work(){
    print('person is working');
  }
}
  • 是否是指定类型:is(是)、is!(不是某类型)
var person;
person = 'str';
//person要是Person类走work方法
if(person is Person) {
    person.work();
}
person = new Person;

//person要是Person类走work方法
//会执行work()方法
if(person is Person) {
    person.work();
}
  • 级联操作:..(通过级联操作访问成员)
void main() {
    
    var person = new Person();
    //通过..访问name返回的还是当前对象
    //当着接着可以返回age、work()
    person..name = '张三'
          ..age=20
          ..work();
          
   //另一种方式
   new Person()..name = '张三'
               ..age=20
               ..work();
}

class Person {
  String name;
  int age;

  void work(){
    print('person is working');
  }
}

对象call方法

  • 如果类实现了call()方法,则该类的对象可以作为方法使用
  • call()里面可以添加参数、可以有返回值
  • object.call(params..)来使用
void main() {

 var person = new Person();
  //可以直接用类的对象person()当方法
  var str = person('张三',20);
  print(str); //name:张三, age:20
}

class Person {
  String name;
  int age;

  void work(){
    print('person is working');
  }
  //可以传参数、有返回值
  String call(String name, int age) {

    return ('name:$name, age:$age');
  }
}

本章小结

本章主要讲了类声明、对象创建、属性、方法、可见性、计算属性。

方法主要讲了构造方法、命名构造方法、常量构造方法、工厂构造方法。

最后还讲了初始化类表、静态成员、操作符、call方法。

结尾

通过本篇文章,基本上了解了面向对象的基础特性

下一篇会主要讲一下Dart语言基础(六)之面向对象高级特性,到时候大家关注一下。

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

推荐阅读更多精彩内容