简介
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方法。