版权声明:本文为作者原创书籍。转载请注明作者和出处,未经授权,严禁私自转载,侵权必究!!!
情感语录:很多人都觉得时间是最好的治疗方案,但其实治好的全是皮外伤!
很高兴再次遇到你! 上期已经介绍完Dart中的基础语法, 本期将继续深入介绍Dart, 相信在看这期内容的同学已经把上期的内容大部分掌握了。回顾上期内容:戳这里☞ Flutter大战前夜之Dart语言(上)
Dart面向对象编程
面向对象编程(OOP)的三个基本特征是:封装、继承、多态。
封装:封装是对象和类概念的主要特性。封装,把客观事物封装成抽象的类,并且把自己的部分属性和方法提供给其他对象调用, 而一部分属性和方法则隐藏。
继承:面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
多态:允许将子类类型的指针赋值给父类类型的指针, 同一个函数调用会有不同的执行效果 。
[温馨小贴士:]
1、 Dart所有的东西都是对象,所有的对象都继承自Object类。
2、 Dart是一门使用类和单继承 的面向对象语言,所有的对象都是类的实例,并且所有的类都是Object的子类
3、一个类通常由属性和方法组成。
什么是类?
1、类的实质是一种数据类型,类似于int、double等基本类型,不同的是它是一种复杂的数据类型。因为它的本质是类型,而不是数据,所以不存在于内存中,不能被直接操作,只有被实例化为对象时, 才会变得可操作。
2、类是对现实生活中一类具有共同特征的事物的抽象。
类的定义和实例化:
1、类的定义:class + 类名
(类名首字母大写)
2、类的实例化:通过关键字 new 类名() 或者 类名()
创建实例化对象,new
并非必写
//创建一个Persion 类
class Person{
//类的属性
String name="再红";
String dis="你好";
//类的方法
String getInfo(){
return "${this.name}-${this.dis}";
}
void setInfo(String dis){
this.dis=dis;
}
}
void main(){
//实例化Persion
var persion = Person();
//调用类中的方法
var info= persion.getInfo();
print(info); // 输出:再红-你好
}
什么是构造函数?
1、构造函数,是一种特殊的方法。主要用来在创建对象时初始化对象,即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。
2、构造函数的命名必须和类名完全相同。
3、构造函数的功能主要用于在类的对象创建时定义初始化的状态。
4、Dart语言中的构造函数分为默认构造函数和命名构造函数。
Dart和Java中构造函数的区别:
Dart:
1.Dart的默认构造函数: 类名(参数列表) 如:Persion(String name)
2.Dart的命名构造函数: 类名.构造函数名(参数列表) 如:Persion.useInfo(String name)
Java:
1.Java中构造函数: 类名(参数列表)
2.Java中的构造函数是根据 参数列表进行区分,多个构造函数的参数列表一定不同
总结:在Dart中默认构造函数有且只有一个,命名构造函数可以有任意个,但构造函数名不能相同。在Java中可以有多个构造函数,构造函数名一定相同,但构造函数的参数列表一定不同,否则报错。
实例代码:
//创建一个Persion 类
class Person{
//类的属性
String name="再红";
String dis="你好";
//Person(this.name,this.dis); // 构造函数简写,等同于下面的构造函数
Person(String name,String dis){
this.name = name;
this.dis = dis;
}
//定义一个无参命名构造函数
Person.UserInfo(){
}
Person.UserInfo1(String name){
this.name = name;
}
//类的方法
String getInfo(){
return "${this.name}-${this.dis}";
}
void setInfo(String dis){
this.dis=dis;
}
}
void main(){
//实例化Persion 并初始化成员变量
var persion = Person("小郑","好帅");
//调用类中的方法
var info= persion.getInfo();
print(info); // 输出:小郑-好帅
//通过命名构造函数实例化对象
var persion1 = Person.UserInfo();
var info1 = persion1.getInfo();
print(info1); // 输出:再红-你好
//通过命名构造函数实例化对象
var persion2 = Person.UserInfo1("瑶瑶");
var info2 = persion2.getInfo();
print(info2); // 输出:瑶瑶-你好
}
Dart中的getter和setter
getter和setter方法的定义:
1、getter的定义方式 get+方法名
2、setter的定义方式 set+方法名(参数)
注意:
getter方法是没有( )
的。返回值类型可以不用声明。setter方法有且只能接收一个参数
class Rect{
num height;
num width;
Rect(this.height,this.width);
// getter
get area{
return this.height*this.width;
}
//setter
set areaHeight(value){
this.height=value;
}
}
void main(){
Rect r=new Rect(10,2);
r.areaHeight=6; //修改高的值
print(r.area); //输出:12
}
Dart中方法和属性的私有化
Dart和其他面向对象语言不一样,Dart 中没有 public private protected这些访问修饰符。在Dart中使用 _ (下划线)
把一个属性或者方法定义成私有。
class Persion{
//定义私有属性
String _name ="小哥";
}
void main(){
Persion persion=new Persion();
print( persion._name);
}
程序一执行Logcat上你会发现惊奇的一行字 "小哥"。纳尼??? 按照要求书写不是已经私有化了么?咋还能访问该属性呢? 你可能会说:"老铁别怕,我刀很锋利,感觉不到痛!" 我只能说客官别急,这是我们下面要讲的内容。
Dart类的抽离
类抽离好处: 既把类单独分离出去,既可以解决代码臃肿的问题,也能提高代码的复用性,你可以简单理解成模块化吧!
继续上面讲的例子,我们把Persion类抽离出去,新建一个文件夹(bean)然后把Persion类写入该文件夹下,如图:
在Test类中通过 import 关键字进行导包引入,这与Java和Kotlin中是一直的。然后我们在Test中去调用Persion中的方法和属性看能不能访问!!
import 'bean/Persion.dart';
void main() {
Persion persion = new Persion();
print(persion._name); // 尝试访问使用属性
print(persion.getName()); //尝试调用私有方法
}
如果你编译器足够智能,你会发现在打印输出这行就已经提示错误信息了,不能访问该属性和方法,强行运行很显然是会报错的 eg: Error: The getter '_name' isn't defined for the class 'Persion'.
【总结:】
1、在Dart中是支持方法和属性私有化的,私有化没有关键字,而是通过 “_ 下划线
”开头进行命名的方法或属性。
2、在一个类要引用另一个类使用关键字 import
进行类的导包。
思考:方法和属性能进行私有化,那么类能进行私有化么?
Dart中的静态成员
1、使用 static 关键字来实现类级别的变量和函数
2、静态方法不能访问非静态成员,非静态方法可以访问静态成员
对于一些公共常用的方法和属性我们常用 static进行定义,这样的好处可避免频繁的创建对象和销毁,这和Java 是如出一辙,做Java 或Android开发的这个在熟悉不过了。
class Person {
static String name = '瑶瑶';
int age=20;
static void show() {
print(name);
}
void printInfo(){ /*非静态方法可以访问静态成员以及非静态成员*/
print(name); //访问静态属性
print(this.age); //访问非静态属性
show(); //调用静态方法
}
static void printUserInfo(){//静态方法
print(name); //静态属性
show(); //静态方法
// print(this.age); //静态方法没法访问非静态的属性
// this.printInfo(); //静态方法没法访问非静态的方法
// printInfo();
}
}
main(){
print(Person.name);
Person.show();
Person p=new Person();
p.printInfo();
Person.printUserInfo();
}
Dart中的对象操作符:
1、?
条件运算符
2、as
类型转换
3、is
类型判断
4、..
级联操作
class Person {
String name;
num age;
Person(this.name,this.age);
void printInfo() {
print("${this.name}---${this.age}");
}
}
main(){
// 此时的 p 并未实例化,既空,p? 表示:如果p为空则不执行.后面的方法,
// 否则执行,喜欢kotlin开发的同学对此操作符再熟不过了!
Person p;
p?.printInfo(); //不会执行printInfo()方法
Person p1=new Person('张三', 20);
p1?.printInfo(); //张三---20
Person p2=new Person('张三', 20);
if(p2 is Person){ //true
p2.name="李四";
}
p2.printInfo(); // 李四---20
print(p2 is Object); // 还记得上面讲到的吗?所有的类都是Object 的子类
num value = 123.2;
print(value as double); // 将num 强转成double类型
Person p4=new Person('张三1', 20);
p4..name="李四"
..age=30
..printInfo();
// 效果同下
// Person p4=new Person('张三1', 20);
// p4.name='李四';
// p4.age=30;
// p4.printInfo();
}
Dart中的类的继承(面向对象的三大特性之一):
1、子类使用 extends
关键词来继承父类
2、子类会继承父类里面可见的属性和方法 但是不会继承构造函数
3、子类能复写父类的方法 getter和setter
4、子类调用父类方法或属性使用 super
关键字。
5、 @override
可以写也可以不写,建议在覆写父类方法的时候加上 @override
class Parent {
String name='红红';
num age=18;
void userInfo() {
print("${this.name}---${this.age}");
}
}
class Child extends Parent{
}
class Child1 extends Parent{
//复写父类方法
@override
void userInfo() {
name = "淇淇";
print("${this.name}---${this.age}");
}
}
class Child2 extends Parent{
//复写父类方法
@override
void userInfo() {
//调用父类方法
super.userInfo();
//调用父类属性
super.name = "瑶瑶";
super.userInfo();
}
}
main(){
Child child=new Child();
//访问父类的属性
print(child.name); //输出:红红
//调用父类的方法
child.userInfo(); //输出:红红---18
Child1 child1=new Child1();
child1.userInfo(); //输出: 淇淇---18
Child2 child2=new Child2();
child2.userInfo(); //输出: 红红---18 然后输出:瑶瑶---18
}
Dart中抽象类
Dart抽象类主要用于定义标准,子类可以继承抽象类,也可以实现抽象类接口。
1、抽象类通过abstract
关键字来定义
2、Dart中的抽象方法不能用abstract声明,Dart中没有方法体的方法我们称为抽象方法。
3、如果子类继承抽象类必须得实现里面的抽象方法
4、如果把抽象类当做接口实现的话必须得实现抽象类里面定义的所有属性和方法。
5、抽象类不能被实例化,只有继承它的子类可以
extends抽象类 和 implements的区别:
1、如果要复用抽象类里面的方法,并且要用抽象方法约束自类的话我们就用extends继承抽象类
2、如果只是把抽象类当做标准的话我们就用implements实现抽象类
abstract class Animal{
eat(); //抽象方法
run(); //抽象方法
printInfo(){
print('抽象类里面的普通方法');
}
}
class Persion extends Animal{
@override
eat() {
print('人在吃饭');
}
@override
run() {
print('人在走路');
}
}
class Cat extends Animal{
@override
eat() {
print('小猫在吃老鼠');
}
@override
run() {
print('小猫在跑');
}
}
main(){
Persion persion = new Persion();
persion.eat(); // 输出:人在吃饭
persion.run(); // 输出:人在走路
persion.printInfo(); // 调用父类的普通方法
Cat c=new Cat();
c.eat(); // 输出:小猫在吃老鼠
c.run(); // 输出:小猫在跑
//Animal a=new Animal(); //错误操作;抽象类没法直接被实例化
}
Datr中的多态:
1、允许将子类类型的指针赋值给父类类型的指针, 同一个函数调用会有不同的执行效果 。
2、子类的实例赋值给父类的引用。
3、 多态就是父类定义一个方法不去实现,让继承他的子类去实现,每个子类有不同的表现。
abstract class Animal{
eat(); //抽象方法
}
class Persion extends Animal{
@override
eat() {
print('人在吃饭');
}
}
class Cat extends Animal{
@override
eat() {
print('小猫在吃老鼠');
}
}
main(){
Animal persion = new Persion();
persion.eat(); // 输出:人在吃饭
Animal cat =new Cat();
cat.eat(); // 输出:小猫在吃老鼠
//Animal a=new Animal(); //错误操作;抽象类没法直接被实例化
}
Datr中的接口:
Dart 的接口没有像Java中的 interface 关键字定义接口,而是普通类或抽象类都可以作为接口被实现。同样使用 implements 关键字进行实现。如果实现的类是普通类,会将普通类和抽象中的属性的方法全部需要覆写一遍。而因为抽象类可以定义抽象方法,普通类不可以,所以一般要实现像Java接口那样的方式,一般会使用抽象类。
接口的优势:
一、规范性
接口就是约定 、规范,让实现者按着规范去实现自己的业务逻辑,在整个系统设计中,涉及到很多层,为了使各个层之间调用透明话,你只需要知道接口,按照这个接口做你具体做的事情,就可以融合到整个系统中了。
二、高内聚低耦合
一个好的程序一定符合高内聚低耦合的特征,那么实现低耦合,定义接口是一个很好的方法,能够让系统的功能较好地实现,而不涉及任何具体的实现细节。这样就比较安全、严密一些。
三、扩展性
在项目开发过程中,由于客户的需求经常变化,如果不采用接口,那么我们必须不停改写现有的业务代码。改写代码可能产生新的BUG,而且改写代码还会影响到调用该业务的类,可能全都需要修改,影响系统本身的稳定性。到最后,可能会出现代码凌乱,不易读懂,后接手的人无法读懂代码,系统的维护工作越来越重,最终可能导致项目失败。
实例: 定义一个DB库 支持 Mysql 、SQLserver的接口
//定义一个添加删改查的接口
abstract class DbController {
//数据库的链接地址
String uri;
add(String data);
delete();
update();
query();
}
//使用MySql去实现自己的CRUD
class Mysql implements DbController {
@override
String uri;
Mysql(this.uri);
add(String data) {
}
@override
delete() {
}
@override
query() {
}
@override
update() {
}
}
//使用SqlServer去实现自己的CRUD
class SqlServer implements DbController {
@override
String uri;
SqlServer(this.uri);
add(String data) {
}
@override
delete() {
}
@override
query() {
}
@override
update() {
}
}
main() {
Mysql mysql = new Mysql('连接地址');
mysql.add('张三');
SqlServer server = new SqlServer('连接地址');
server.query();
}
【温馨提示:】一个类实现一个接口使用 implements
关键字,实现多个接口使用 ,
进行分割开。
Dart中的泛型
泛型就是解决类、 接口、 方法的复用性、以及对不特定数据类型的支持。
一、 泛型的定义:泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法
的创建中,分别称为泛型类、泛型接口、泛型方法。
二、 泛型的好处:泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。
实例 (泛型方法):
假设别人需要你提供一个方法给开发者使用,前期开发者会告诉你他会传一个什么样类型的数据给你,比如是String 类型的年龄 如:二十岁,并让你处理后返回给他一个结果。你可能说很简单,咔咔几下就写出了如下代码:
String getAge(String value){
return "红红今年:"+value+"岁";
}
嗯,满足需求,没有任何毛病。就在你很满意的时候,开发者告诉你我给不了你中文的数字"二十",只能给你一个阿拉伯数字,让你改改。
此时你可能咔咔几下又写出了如下代码:
String getAge(String value){
return "红红今年:"+value+"岁";
}
String getAge1(int value){
return "红红今年:${value}岁";
}
完美,同时支持中文和阿拉伯数字的方法了,但是这样就出现一个问题(代码冗余) 实际开发中肯定不是这样一个简单的需求。那怎么办,要是我能接收一个任意类型的参数就好了,灵机一动你可能想到,啊哈,前面不是讲到在Dart中方法中参数类型和返回类型可以不用指定么! 然后你可能Duang写出了下面代码:
// 同时返回 string类型 和number类型
getAge(value){
return "红红今年:${value}岁";
}
但是!!!!!,不指定类型放弃了类型检查,使得程序变的极不安全。使用Object 做参数类型和返回类型,看起来很理想哈,在Dart中所有的对象都是Object的子类,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这也是一个安全隐患。
我们现在想实现的是传入什么 返回什么。比如:传入number 类型必须返回number类型 传入 Persion 类型必须返回Persion类型。使用泛型进行参数任意化
T getUserInfo<T>(T value) {
return value;
}
void main() {
print(getUserInfo(21));
print(getUserInfo('晓晓'));
print(getUserInfo<String>('你好'));
}
实例 (泛型方类):
上面讲了泛型方法即在方法中使用 "T" 声明的参数类型,这个"T"可以是任意字母,但通常是一个大写的字母表示即可。这个作用在方法是泛型方法,作用在类上就是泛型类了。
class PrintClass<T>{
List list=new List<T>();
void add(T value){
this.list.add(value);
}
void printInfo(){
for(var i=0;i<this.list.length;i++){
print(this.list[i]);
}
}
}
main() {
PrintClass p = new PrintClass<String>();
p.add('你好');
p.add('哈哈');
p.printInfo(); //分别输出: 你好; 哈哈
PrintClass p1=new PrintClass<int>();
p1.add(12);
p1.add(23);
p1.printInfo(); //分别输出: 12; 23
}
实例 (泛型接口):
实现数据缓存的功能:有文件缓存、和内存缓存。内存缓存和文件缓存按照接口约束实现。
1、定义一个泛型接口 约束实现它的子类必须有getByKey(key) 和 setByKey(key,value)
2、要求setByKey的时候的value的类型和实例化子类的时候指定的类型一致
//定义接口
abstract class Cache<T>{
getByKey(String key);
void setByKey(String key, T value);
}
class FileCache<T> implements Cache<T>{
@override
getByKey(String key) {
return null;
}
@override
void setByKey(String key, T value) {
print("我是文件缓存 把key=${key} value=${value}的数据写入到了文件中"); // 我是文件缓存 把key=文件缓存 value=这是文件缓存的数据写入到了文件中
}
}
class MemoryCache<T> implements Cache<T>{
@override
getByKey(String key) {
return null;
}
@override
void setByKey(String key, T value) {
print("我是内存缓存 把key=${key} value=${value} -写入到了内存中");//我是内存缓存 把key=内存缓存 value={name: 张三, age: 20} -写入到了内存中
}
}
void main(){
FileCache fileCache=new FileCache<String>();
fileCache.setByKey('文件缓存', '这是文件缓存');
MemoryCache m=new MemoryCache<Map>();
m.setByKey('内存缓存', {"name":"张三","age":20});
}
Dart中的异步
在Dart中异步是用async
关键字标识,async使用在方法时则在调用该方法时必须使用await
关键字,async是让方法变成异步,await是等待异步方法执行完成。
实例 (异步):
import 'dart:io';
import 'dart:convert';
void main() async{
//等待异步方法返回结果,在main方法中使用了 await,所以main 方法也需要async 标识
var result = await request();
print(result);
}
//api接口: http://news-at.zhihu.com/api/3/stories/latest
//创建一个异步方法
request() async{
//1、创建HttpClient对象
var httpClient = new HttpClient();
//2、创建Uri对象
var uri = new Uri.http('news-at.zhihu.com','/api/3/stories/latest');
//3、发起请求,等待请求
var request = await httpClient.getUrl(uri);
//4、关闭请求,等待响应
var response = await request.close();
//5、解码响应的内容
return await response.transform(utf8.decoder).join();
}
Dart中的懒加载
什么是懒加载?既在需要的时候再进行加载。 懒加载的最大好处是可以减少APP的启动时间。懒加载使用deferred as
关键字来指定。
import 'hello.dart' deferred as hello;
greet() async {
//加载hello.dart
await hello.loadLibrary();
//调用hello中的方法
hello.printf();
}
void main() async{
}
Dart中库以及导包
1、关于库的使用
Dart中的库主要有三种:
1、我们自定义的库
import 'lib/xxx.dart';
2、系统内置库
import 'dart:math';
import 'dart:io';
import 'dart:convert';
3、Pub包管理系统中的库
https://pub.dev/packages
https://pub.flutter-io.cn/packages
https://pub.dartlang.org/flutter/
1、需要在自己想项目根目录新建一个pubspec.yaml
2、在pubspec.yaml文件 然后配置名称 、描述、依赖等信息
3、然后运行 pub get 获取包下载到本地
4、项目中引入库
如下:
name: 库名
description: 库的描述
dependencies:
http: ^0.12.0+2 // 依赖信息 如这个 http 版本0.12
2、关于导包的使用
在Dart中当引入两个库中有相同名称标识符的时候,如果是java通常我们通过写上完整的包名路径来指定使用的具体标识符,甚至不用import都可以,但是Dart里面是必须import
的。当冲突的时候,可以使用as关键字来指定库的前缀。如下例子所示:
import 'package:lib1/User.dart';
import 'package:lib2/User.dart' as User2;
Element element1 = new Element(); // Uses Element from lib1.
User2.Element element2 = new User2.Element(); // Uses Element from lib2.
如果只需要导入库的一部分,有两种模式:
模式一:只导入需要的部分,使用show关键字,如下例子所示:
import 'package:lib1/User.dart' show getAge;
模式二:隐藏不需要的部分,使用hide关键字,如下例子所示:
import 'package:lib2/User.dart' hide getAge;
好了,关于Dart的基础知识到这里就基本讲完了,更多知识点请关注下期内容 一举拿下Android和IOS《Flutter专栏》,谢谢大家观看,下期再会!!!!