类是通过 class 关键字声明的代码段,包含属性和方法
属性:用来描述类的变量
方法:类中的函数称为类的方法
对象是类的实例化结果(var obj = new MyClass())
编程方式
面向对象编程(OOP)
面向过程编程(POP)
// 声明类 class Person { // 类的属性 String name = 'Roger'; // 类的方法 void getInfo() { print(this); // Instance of 'Person' print('My name is $name'); // My name is Roger print('我的名字叫 ${this.name}'); // 我的名字叫 Roger } } void main() { // 实例化类,然后得到一个对象 Person p = new Person(); // 访问类中的属性 print(p.name); // Roger // 访问类的方法 p.getInfo(); }
构造器(构造函数)
默认构造函数
与类同名的函数,在实例化时,自动被调用
class Point { num x, y; // 默认构造函数 Point() { print('这是默认的构造函数,实例化时,会第一个被调用。'); } } void main() { Point p = new Point(); print(p.x); // 这是默认的构造函数,实例化时,会第一个被调用。 // null }
class Point { num x, y; // 默认构造函数 // Point() { // // this可以省略 // x = 0; // y = 0; // } Point(num x, num y) { // 当命名指向有歧义时,this 不能省略 // x = x; // y = y; this.x = x; this.y = y; } } void main() { Point p = new Point(3, 4); print(p.x); // 构造器中参数与类属性同名,省略this时,输出结果为 null;加上this,输出结果为 3 }
class Point { num x, y; // 默认构造函数的简写 Point(this.x, this.y); } void main() { Point p = new Point(2, 4); print(p.x); // 2 }
命名构造函数
在类中使用命名构造函数(类名.函数名)实现多个构造器,可以提供额外的清晰度
class Person { String name; int age; // 默认构造函数 Person(this.name, this.age); // 命名构造函数 -- 提供 Person 类的示例 Person.example() { name = '张三'; age = 23; } // 命名构造函数 -- 使用命名参数传值 Person.namedParameter({name, age}) { this.name = name; this.age = age; } void getInfo() { print('姓名:$name, 年龄:$age'); } } void main() { Person p1 = new Person('李四', 24); Person p2 = new Person.example(); Person p3 = new Person.namedParameter(name: '光头强', age: 45); p1.getInfo(); // 姓名:李四, 年龄:24 p2.getInfo(); // 姓名:张三, 年龄:23 p3.getInfo(); // 姓名:光头强, 年龄:45 }
常量构造函数
如果类生成的对象不会改变,可以通过常量构造函数使这些对象成为编译时常量
class ConstantClass { // 属性必须通过 final 声明 final String book; // const String author; // Only static fields can be declared as const final String author; // 常量构造函数,必须通过 const 声明 const ConstantClass(this.book, this.author); } void main() { // 声明不可变对象,必须通过 const 关键字 var c1 = const ConstantClass('西游记', '吴承恩'); var c2 = const ConstantClass('西游记', '吴承恩'); print(c1 == c2); // true // 常量构造函数,可以当作普通构造函数使用 var c3 = new ConstantClass('西游记', '吴承恩'); var c4 = new ConstantClass('西游记', '吴承恩'); print(c3 == c4); // false // 实例化时,new关键字可以省略 var c5 = new ConstantClass('西游记', '吴承恩'); var c6 = new ConstantClass('西游记', '吴承恩'); print(c5 == c6); // false }
工厂构造函数
通过 factory 声明,工厂函数不会自动生成实例,而是通过代码来决定返回的实例
class Car { int wheels; static Car instance; // 命名构造函数 Car.newCar(this.wheels); // 工厂构造函数 -- 接受一个可选参数并赋默认值4 factory Car([int wheels = 4]) { // 工厂构造函数中,不能使用 this 关键字 // print(this.wheels); // Invalid reference to 'this' expression // 第一次实例化 if (Car.instance == null) { Car.instance = new Car.newCar(wheels); } // 非第一次实例化 return Car.instance; } } void main() { // Car c1 = new Car(); // Car c2 = new Car(6); // print(c1.wheels); // 4 // print(c2.wheels); // 4 Car c1 = new Car(6); Car c2 = new Car(); print(c1.wheels); // 6 print(c2.wheels); // 6 print(c1 == c2); // true }
访问修饰
Dart 没有访问修饰符(public, protected, private),在 Dart 类中,默认的访问修饰符是公开的(即 public)
如果要使用私有属性(private),需要同时满足以下两个条件:
(1)、属性或方法以_(下划线)开头
(2)、只有把类单独抽离出去,私有属性和方法才起作用
class Person { String name; // 声明私有属性 num _age = 8; Person(this.name); num getAge() { return this._age; } }
import 'lib/Person.dart'; void main() { Person p = new Person('Rogers'); print(p.name); // Rogers // 访问私有属性 // print(p._age); // The getter '_age' isn't defined for the type 'Person'. print(p.getAge()); // 8 }
Getter与Setter
Getter(获取器)是通过 get 关键字修饰的方法
函数没有小括号,访问时也没有小括号(像访问属性一样访问方法)
Setter(修改器)是通过 set 关键字修饰的方法
访问时,像设置属性一样给函数传参
class Circle { final double PI = 3.1415926; num r; Circle(this.r); // 使用 get 声明的方法,不能有小括号 // num get area() { // Getters must be declared without a parameter list. num get area { return this.PI * this.r * this.r; } set setR(r) { this.r = r; } } void main() { Circle c = new Circle(3); // 通过 Setter 修改属性 c.setR = 10; print(c.area); // 314.15926 }
初始化列表
作用:在构造函数中设置属性的默认值
时机:在构造函数体执行之前执行
语法:使用逗号分隔初始化表达式
场景:常用于设置 final 常量的值
class Rect { double height; double width; // 方案一:使用可选参数赋默认值 // Rect([double height = 3.0, double width = 2.0]) { // this.height = height; // this.width = width; // print('height: $height, $width'); // } // 初始化列表 Rect() : height = 3.0, width = 2.0 { print('height: $height, $width'); } } void main() { Rect r = new Rect(); // height: 3.0, 2.0 }
class Point { double x, y, z; Point(this.x, this.y, this.z); // 初始化列表的特殊用法(重定向构造函数) Point.twoD(double x, double y) : this(x, y, 0); } void main() { // 实例化坐标 Point p1 = new Point(1, 2, 3); print(p1.z); // 3.0 Point p2 = new Point(1, 2, 0); print(p2.z); // 0.0 }
static
static 关键字用来指定静态成员
通过 static 修饰的属性是静态属性
通过 static 修饰的方法是静态方法
静态成员可以通过类名称直接访问(不需要实例化)
实例化是比较消耗资源的,声明静态成员,可以提高程序性能
静态方法不能访问非静态成员,非静态方法可以访问静态成员
静态方法中不能使用 this 关键字
不能使用 this 关键字访问静态属性
class Person { static String name = 'Rogers'; int age = 16; void printInfo() { // 不能通过this关键字访问静态属性 // print(this.name); // 非静态方法,可以访问静态属性 print(name); print(age); // 非静态方法,可以访问静态方法 printName(); } static void printName() { // 不能通过this关键字访问静态属性 // print(this.name); print(name); // 静态方法中不能访问非静态属性 // print(age); } } void main() { // 静态成员,可以通过类名直接访问 print(Person.name); // Rogers Person.printName(); // Rogers // 不能通过类名称,直接访问非静态方法 // Person.printInfo(); Person p = new Person(); // 不能通过实例化对象访问静态属性 // print(p.name); p.printInfo(); }
元数据
元数据以 @ 开头,可以给代码标记一些额外的信息
元数据可以用在库,类,构造器,函数,字段,参数或变量声明的前面
@override(重写)
某个方法添加该注解后,表示重写了父类中的同名方法
@required(必填)
可以通过 @required 来注解 Dart 中的命名参数,用来指示它是必填参数
@deprecated(弃用)
某个类或某个方法加上该注解后,表示这个类或方法不再建议使用
class Phone { // 表示这是旧版本中的开机方法,会在后续的迭代版本中删除 @deprecated activate() { turnOn(); } turnOn() { print('开机'); } } void main() { Phone p = new Phone(); p.activate(); }