Dart语法下

十三、Dart中的范型

13.1 范型方法

通俗理解: 范型就是解决类、接口、方法的复用性,以及对不特定数据类型的支持(类型校验)

案例1:

  • 定义一个函数只能返回String类型的数据
String getStringData(String value){
    return value;
}

案例2:

  • 同时支持返回String和int类型
String getStrData(String value){
    return value;
}
int getIntData(int value){
    return value;
}

案例3: 同时返回String类型和number类型,不指定类型可以解决这个问题

getMixData(value){
    return value;
}

案例4:

  • 不指定类型放弃了类型检查.我们现在想实现的是传入什么,返回什么.
  • 比如传入number,类型必须返回number类型,传入String类型必须返回String类型.要求类型一致.
    • 因此引入范型
      • 用T对返回值类型进行校验
      • 对返回值类型不进行校验
 //T校验返回值
  T getMixDatas<T>(T value){
      return value;
  }
  //不校验返回值
  getMixDatal<T>(T value){
      return value;
  }
    • 案例调用
main(){
    print(getMixDatas(2222));
    print(getMixDatas("11111"));
}
    • 但是这么操作还是没有实现类型一致性,既然引入了范型,那么我们在调用的时候添加上约束就行
print(getMixDatas<String>("3333"));
print(getMixDatas<int>(4444));

13.2 范型在类中的用法.

  • 集合List在范型类的用法

案例:

把下面类转换成范型类,要求MyList里面可以增加int类型的数据,也可以增加String类型的数据.但是每次调用增加的类型要统一.

class MyList{
    List list = <int>[];
    void add(int value){
        this.list.add(value);    
    }
    List getList(){
        return list;    
    }
}
main(){
    MyList l = new MyList();
    l.add(1);
    l.add(12);
    l.add(5);
    print(l.getList());//[1,12,5]
}
    • 修改后的实现部分
class MyList<T>{
    List list = <T>[];
    void add(T value){
        this.list.add(value);    
    }
    List getList(){
        return list;    
    }
}
    • 案例调用
List list1 = new List<int>.filled(2,0);//初始化list1列表,长度为2,填充值为0,也就是[0,0]
list1[0] = 11111;
list1[1] = 22222;
print(list1);//[11111, 22222]

13.3 范型接口

案例1:

    • 两个抽象类ObjectCache、StringCache
    • 对两个抽象类进行复用性精简.
abstract class ObjectCache {
    getByKey(String key);
    void setByKey(String key,Object value);
}
abstract class StringCache{
    getByKey(String key);
    void setByKey(String key,String value);
}
//将上述两个抽象类进行复用性精简
abstract class Cache<T> {
    getByKey(String key);
    void setByKey(String key,T value);
}

案例2:

实现数据缓存的功能: 有文件缓存和内存缓存.内存缓存和文件缓存按照接口约束实现.

    • 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}的数据写入到了文件中");    
    }
}
class MemoryCache<T> implements Cache<T>{
    @override 
    getByKey(String key){
        return null;    
    }
    @override
    void setByKey(String key,T value){
        print("我是内存缓存 把key=${key} value=${value} - 写入到了内存中");    
    }
}
  • 根据编写的类,对其进行调用测试
void main(){
    MemoryCache m = new MemoryCache<String>();
    m.setByKey('index','首页数据');//我是内存缓存 把key=index  value=首页数据 -写入到了内存中
    MemoryCache n = new MemoryCache<Map>();
    n.setByKey('index',{"name": "张三", "age": 20});//我是内存缓存 把key=index  value={name: 张三, age: 20} -写入到了内存中
}

13.4 总结

  • 范型就是解决类、接口、方法的复用性,以及对不特定数据类型的支持(类型校验)

十四、Dart中的库

14.1 Dart中的库

前面介绍Dart基础知识的时候基本上都是在一个文件里面编写Dart代码,但实际开发中不可能这么写,模块化很重要,所以这就需要使用到库的概念.

  • 在Dart中,库的使用是通过import关键字引入的.
  • library指令可以创建一个库,每个Dart文件都是一个库,即使没有使用library指令来指定.

Dart中的库主要有三种:

  1. 我们自定义的库
    1. import 'lib/xxx.dart';
  2. 系统内置库
    1. import 'dart:math';
    2. import 'dart:io';
    3. import 'dart:convert';
  3. Pub包管理系统中的库
    1. Page 1 | Top packagesPage 1 | Top packagesSearch results for sdk:flutter
    2. 需要在自己项目根目录下新建一个pubspec.yaml
    3. 在pubspec.yaml文件,然后配置名称,描述,依赖等信息.
    4. 然后运行pub get 获取包下载到本地
    5. 项目中引入库 import 'package:http.dart' as http;

例如: 一个pubspec.yaml文件

name: xxx
description: A new flutter module project.
dependencies:
  http: ^0.12.0+2
  date_format: ^1.0.6
environment:
  sdk: '>=2.10.0 <3.0.0'

14.2 Dart中导入自己本地库

在一个模块中导入自己本地库,也就是两个文件的调用关系,比如在lib文件夹下创建的Animal.dart模块,在main.dart模块中需要使用,那么只需要给相对路径即可.

import 'lib/Animal.dart';
main(){
  var a=new Animal('小黑狗', 20);
  print(a.getName());
}

14.3 导入系统内置库

如果我们想要使用系统库,系统库的前缀是 'dart:',在它的后面加上你要使用的哪个系统库.command+鼠标左键即可点进去一个库对其内容进行查看.

import 'dart:math';
main(){
    print(min(12,23)); //12
    print(max(12,25)); //25
}

14.4 导入系统内置库实现请求数据

一个网络数据的请求过程:

  1. 创建HttpClient对象
  2. 创建url对象
  3. 发起请求,等待请求
  4. 关闭请求,等待响应
  5. 解码响应的内容

案例: 请求知乎最近的新闻数据

  • HttpClient是系统内置库io中的模块
  • 需要用到数据解析,使用的是convert中相关的模块
import 'dart:io';
import 'dart:convert';
void main() async{
    var result = await getDataFromZhihuAPI();
    print(result);
}
getDataFromZhihuAPI() async {
    //1、创建HttpClient对象
    var httpClient = new HttpClient();
    //2、创建Url对象
    var uri = new Uri.http('news-at-zhihu.com','/api/3/stories/latest');
    //3、发起请求,等待请求
    var request = await httpClient.getUrl(url);
    //4、关闭请求,等待响应
    var response = await request.close();
    //5、解码响应的内容
    return await response.transform(utf8.decoder).join();
}
  • 执行函数,返回请求结果如下:
{"date":"20230501","stories":[{"image_hue":"0x424f37","title":"小事 · 你见过哪些父母惊艳到你的教育方式?","url":"https:\/\/daily.zhihu.com\/story\/9761023","hint":"VOL.1483","ga_prefix":"050107","images":["https:\/\/picx.zhimg.com\/v2-4b3cb2892a38b2340b12ba33c6ff381a.jpg?source=8673f162"],"type":0,"id":9761023},{"image_hue":"0x453040","title":"有哪些知道了语源后会令人感到惊讶的英语单词?","url":"https:\/\/daily.zhihu.com\/story\/9760986","hint":"一直在路上David · 4 分钟阅读","ga_prefix":"050107","images":["https:\/\/picx.zhimg.com\/v2-f66107a7770e06616a689d6e542d2136.jpg?source=8673f162"],"type":0,"id":9760986},{"image_hue":"0xb37d7d","title":"吕秀才和郭芙蓉在一起是不是郭巨侠祝无双所认为的一时冲动?","url":"https:\/\/daily.zhihu.com\/story\/9760990","hint":"童大掌柜呢 · 8 分钟阅读","ga_prefix":"050107","images":["https:\/\/pic1.zhimg.com\/v2-889394ae433b0996ea24f6a6129d1ae8.jpg?source=8673f162"],"type":0,"id":9760990},{"image_hue":"0x547bae","title":"燕子为何会选择在人类屋檐下搭窝?","url":"https:\/\/daily.zhihu.com\/story\/9761000","hint":"知乎用户 · 2 分钟阅读","ga_prefix":"050107","images":["https:\/\/pic1.zhimg.com\/v2-ba775f62ab095a62d3c7ab512f68fba5.jpg?source=8673f162"],"type":0,"id":9761000},{"image_hue":"0x040404","title":"世界上有哪些两地相隔不远但互通必须绕一大圈的例子?","url":"https:\/\/daily.zhihu.com\/story\/9761008","hint":"莱茵行宫伯爵 · 2 分钟阅读","ga_prefix":"050107","images":["https:\/\/pic1.zhimg.com\/v2-368438800a90e5f1bdd081f3e9e1c351.jpg?source=8673f162"],"type":0,"id":9761008},{"image_hue":"0x7c643f","title":"面粉为什么能洗出面筋?粉是怎么转化成「筋」的呢?","url":"https:\/\/daily.zhihu.com\/story\/9761018","hint":"钱程 · 1 分钟阅读","ga_prefix":"050107","images":["https:\/\/picx.zhimg.com\/v2-d798a6d979126c6ce16f573daced0ee1.jpg?source=8673f162"],"type":0,"id":9761018}],"top_stories":[{"image_hue":"0x424f37","hint":"作者 \/ 文小白","url":"https:\/\/daily.zhihu.com\/story\/9761023","image":"https:\/\/picx.zhimg.com\/v2-d412fe4c9e66a9c2aaed7ad75fa6b975.jpg?source=8673f162","title":"小事 · 你见过哪些父母惊艳到你的教育方式?","ga_prefix":"050107","type":0,"id":9761023},{"image_hue":"0xafafaf","hint":"作者 \/ 真实故事计划","url":"https:\/\/daily.zhihu.com\/story\/9760980","image":"https:\/\/picx.zhimg.com\/v2-f01dedf1b9345156c61204fa3724b9ed.jpg?source=8673f162","title":"小事 · 你见过的最阴暗的事是什么?","ga_prefix":"043007","type":0,"id":9760980},{"image_hue":"0x5e6c24","hint":"作者 \/ 百花杀","url":"https:\/\/daily.zhihu.com\/story\/9760967","image":"https:\/\/pic1.zhimg.com\/v2-3263de81e79d67126d92b379ab6a5cb8.jpg?source=8673f162","title":"小事 · 你遇过的最温暖的瞬间是什么?","ga_prefix":"042907","type":0,"id":9760967},{"image_hue":"0xb39168","hint":"作者 \/ 斜绿天蛾","url":"https:\/\/daily.zhihu.com\/story\/9760874","image":"https:\/\/pic1.zhimg.com\/v2-a4bc6aa6a1c1ba806e8306d27adc8adc.jpg?source=8673f162","title":"昆虫演化真的符合进化论吗?","ga_prefix":"042807","type":0,"id":9760874},{"image_hue":"0x946d68","hint":"作者 \/ 于朝阳","url":"https:\/\/daily.zhihu.com\/story\/9760851","image":"https:\/\/pic1.zhimg.com\/v2-c95731f8815aadd68e2e7cb5c518ff4c.jpg?source=8673f162","title":"秦朝(秦国)为何迅速灭亡?","ga_prefix":"042

14.4 关于async和await

这两个关键字的使用只需要记住两点:

  • 只有async方法才能使用await关键字调用方法
  • 如果调用别的async方法必须使用await关键字

async是让方法变成异步.

await是等待异步方法执行完成.

void main() async {
    var result await testAsync();
    print(result);
}
testAsync() async {
    return "Hello async";
}

14.5 Dart导入pub包管理系统中的库

14.5.1 pub包管理系统简介:

1、从下面网址找到要用的库

2、创建一个pubspec.yaml文件,内容如下

    name: xxx
    description: A new flutter module project.
    dependencies:  
        http: ^0.12.0+2
        date_format: ^1.0.6

3、配置dependencies

4、运行pub get 获取远程库

5、看文档引入库使用

14.5.2 案例: 网络请求及响应

  • 首先导入需要用到的模块、数据解析用到 'dart:convert'
  • http请求使用了 package:http/http.dart
  • 时间格式化采用 date_format.dart
import 'dart:convert' as convert;
import 'package:http/http.dart' as http;
import 'package:date_format/date_format.dart';
  • 实现部分
main() async {
   var url = "http://www.phonegap100.com/appapi.php?a=getPortalList&catid=20&page=1";
   var response = await http.get(url);
   if response.statusCode == 200 {
       var jsonResponse = convert.jsonDecode(response.body);
       print(jsonResponse);   
   }  else {
      print("Request failed with status: ${response.statusCode}.");    
   }
   print(formatDate(DateTime(2023,5,1), [yyyy,'**',mm,"**",dd])); //2023**05**01
}

14.6 Dart 库的重命名&Dart冲突解决

1、冲突解决

  • 当引入两个库中有相同名称标识符的时候
  • 如果是Java通常我们通过写上完整的包名称路径来指定使用的具体标识符,甚至不用import都可以,但是Dart里面是必须import的.
  • 当冲突的时候,可以使用 as关键字来制定库的前缀.
  • 例如:
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
Element element1 = new Element();          // Uses Element from lib1.
lib2.Element element2 = new lib2.Element();// Uses Element from lib2.

案例:

import 'lib/Person1.dart';
import 'lib/Person2.dart' as lib;
main(List<String> args) {
  Person p1=new Person('张三', 20);
  p1.printInfo(); //Person1:张三----20
  lib.Person p2=new lib.Person('李四', 20);
  p2.printInfo(); //Person2:李四----20
}

14.7 部分导入

部分导入:

  • 如果只需要导入库的一部分,有两种模式
    • 一、只导入需要的部分,使用show关键字,如下所示
import 'package:lib1/lib1.dart
    • 二、隐藏不需要的部分,使用 hide 关键字, 如下所示
import 'package:lib2/lib2.dart' hide foo;

案例:

  • 实现这么一个模块,名叫myMath.dart
void getName(){
  print('张三');
}
void getAge(){
  print(20);
}
void getSex(){
  print('男');
}
  • 在使用时
    • 1、如果只想用 getAge方法,那么
import 'lib/myMath.dart' show getAge;
void main(){
   getAge();
}
    • 2、如果想要隐藏 getName方法,那么调用就会报错
import 'lib/myMath.dart' hide getName;
void main(){
//  getName();//调用报错
  getAge();
}

14.8 延迟加载

  • 也称为懒加载,可以在需要的时候再进行加载
  • 懒加载的最大好处是可以减少App的启动时间
  • 懒加载使用deferred as关键字来指定,如
import 'package:deferred/hello.dart' deferred as hello;
  • 当需要使用的时候,需要使用loadLibrary()方法来加载;
greet() async {
    await hello.loadLibrary();
    hello.printGreeting();
}

14.9 总结

1、Dart中的库主要有三种:

  1. 我们自定义的库
    1. import 'lib/xxx.dart';
  2. 系统内置库
    1. import 'dart:math';
    2. import 'dart:io';
    3. import 'dart:convert';
  3. Pub包管理系统中的库
    1. Page 1 | Top packagesPage 1 | Top packagesSearch results for sdk:flutter
    2. 需要在自己项目根目录下新建一个pubspec.yaml
    3. 在pubspec.yaml文件,然后配置名称,描述,依赖等信息.
    4. 然后运行pub get 获取包下载到本地
    5. 项目中引入库 import 'package:http.dart' as http;

2、在一个模块中导入自己本地库,也就是两个文件的调用关系,比如在lib文件夹下创建的Animal.dart模块,在main.dart模块中需要使用,那么只需要给相对路径即可.

3、如果我们想要使用系统库,系统库的前缀是 'dart:',在它的后面加上你要使用的哪个系统库.

4、async和await这两个关键字的使用只需要记住两点:

    • 只有async方法才能使用await关键字调用方法
    • 如果调用别的async方法必须使用await关键字
  • async是让方法变成异步.
  • await是等待异步方法执行完成.

5、Dart导入pub包管理系统中的库

1、从下面网址找到要用的库

2、创建一个pubspec.yaml文件

3、配置dependencies

4、运行pub get 获取远程库

5、看文档引入库使用

6、冲突解决

  • 当引入两个库中有相同名称标识符的时候
  • 如果是Java通常我们通过写上完整的包名称路径来指定使用的具体标识符,甚至不用import都可以,但是Dart里面是必须import的.
  • 当冲突的时候,可以使用 as关键字来制定库的前缀.

7、如果只需要导入库的一部分,有两种模式

    • 一、只导入需要的部分,使用show关键字
    • 二、隐藏不需要的部分,使用 hide 关键字

8、延迟加载

  • 也称为懒加载,可以在需要的时候再进行加载
  • 懒加载的最大好处是可以减少App的启动时间
  • 懒加载使用deferred as关键字来指定

十五、Dart 2.13后的新特性 Null safety、late关键字、空类型声明符?、非空断言!、required关键字

15.1 Null safety & 类型断言

Null safety翻译成中文的意思是空安全。

null safety 可以帮助开发者避免一些日常开发中很难被发现的错误,并且额外的好处是可以改善性能。

Flutter2.2.0(2021年5月19日发布) 之后的版本都要求使用null safety。

  • ? 可空类型(相当于Swfit中的可选值Optional)
  • ! 类型断言(相当于Swift中的强制解包)

案例:

String? getData(apiUrl){
  if(apiUrl !=null ){
    return "this is server data";
  }
  return null;
}
  • 案例调用
  //1、null safety
  int a=123;
  print(a);//123
  String username="张三";
  print(username);//张三
  List<String> l1=["张三","李四","王五"];
  print(l1);//[张三, 李四, 王五]
  // int b=123;  //非空的int类型
  // b=null;  //A value of type 'Null' can't be assigned to a variable of type 'int'
  // String usernameA="张三";  //非空的String类型
  // usernameA=null;   //A value of type 'Null' can't be assigned to a variable of type 'String'.
  String? usernameB="张三";   // String?  表示username是一个可空类型
  usernameB=null;
  print(usernameB);//null
  int? c=123;  //  int? 表示a是一个可空类型
  c=null; 
  print(c);//null
  List<String> l2=["张三","李四","王五"];
  // l2=null;  //A value of type 'Null' can't be assigned to a variable of type 'List<String>'.
  List<String>? l3=["张三","李四","王五"];
  l3=null;  
  print(l3);//null
  //调用方法
  print(getData("http://www.itying.com"));//this is server data
  print(getData(null)); //null
//2、! 类型断言
  String? str="this is str";
  str=null;
  print(str!.length);  //null
  // 类型断言: 如果str不等于null 会打印str的长度,如果等于null会抛出异常
   printLength("str");//
   printLength(null);

15.2 late关键字

  • late关键字主要用于延迟初始化

案例:

  • 声明构造方法时,要求初始化成员不能为空,于是得加上late懒加载修饰
class Person {
  late String name;
  late int age;
  void setName(String name, int age) {
    this.name = name;
    this.age = age;
  }
  String getName() {
    return "${this.name}---${this.age}";
  }
}
void main(args) {
  Person p = new Person();
  p.setName("张三", 20);
  print(p.getName());
}

15.3 late接口

接口就是约定、规范

abstract class DB { //接口: 约定、规范
    late String uri; //数据库的链接地址
    add(String data);
    save();
    delete();
}
  • 在定义抽象类时,如果添加了成员变量,未初始化的情况下就会报错,这个时候我们使用late修饰,懒加载执行.

15.4 required关键词

required关键词

  • 最开始@required是注解; 现在它已经作为内置修饰符
  • 主要用于允许根据需要标记任何命名参数(函数和类),使得它们不为空.因为可选参数中必须有个 required参数或者该参数有个默认值.

案例1:

  • 如果想要age和sex可以为空,那么我们这样表示
String printUserInfo(String username, {int age=10, String sex="男"}) {//行参    
  return "姓名:$username---性别:$sex--年龄:$age";
}

案例2:

  • 如果想让age和sex必须有值,那么添加关键字required
String printInfo(String username, {required int age, required String sex}) {//行参    
  return "姓名:$username---性别:$sex--年龄:$age";
}
  • 案例调用
void main(args) {
    print(printUserInfo('张三'));
    print(printUserInfo('张三',age: 20,sex: "女"));
    //age 和 sex必须传入
    print(printInfo('张三',age: 22,sex: "女"));
}

15.5 required命名参数

案例:

  • 在定义一个类时,我们想让构造方法中的参数作为必须传入的命名参数时,采用required修饰
//表示 name 和age 是必须传入的命名参数
class Person {
  String name;
  int age;
  Person({required this.name,required this.age});  //表示 name 和age 必须传入
  String getName() {
    return "${this.name}---${this.age}";
  }
}
  • 案例调用
void main(args) {
   Person p=new Person(
     name: "张三",
     age: 20
   );
   print(p.getName());
}

15.6 required命名参数可选

  • 在定义一个类,构造函数中的参数,我们想让一部分可以传入也可以不传入,另一部分必须传入,采用可控类型修饰.

案例:

  • name可以传入也可以不传入,age必须传入
class Person {
    String? name;//可空属性
    int age;
    Person({this.name, required this.age});//表示name可空,age必须传入.
    String getName(){
        return "${this.name} --- ${this.age}";    
    }
}
  • 案例调用
void main(args) {
   Person p=new Person(
     name: "张三",
     age: 20
   );
   print(p.getName());  //张三---20
  Person p1=new Person(    
     age: 20
   );
   print(p1.getName());  //null---20
}

15.7 总结

  1. null safety 可以帮助开发者避免一些日常开发中很难被发现的错误,并且额外的好处是可以改善性能。
  • ? 可空类型(相当于Swfit中的可选值Optional)
  1. ! 类型断言(相当于Swift中的强制解包)
  2. late关键字主要用于延迟初始化.声明构造方法时,要求初始化成员不能为空,于是得加上late懒加载修饰.
  3. required关键词
  • 最开始@required是注解; 现在它已经作为内置修饰符
  • 主要用于允许根据需要标记任何命名参数(函数和类),使得它们不为空.因为可选参数中必须有个 required参数或者该参数有个默认值.
  1. 在定义一个类,构造函数中的参数,我们想让一部分可以传入也可以不传入,另一部分必须传入,采用可控类型修饰.

十六、Dart性能优化之常量、identical函数、常量构造函数详解

16.1 final和const修饰符

Dart常量用: final和const修饰符修饰.

  • const声明的常量是在编译时确定的,永远不会改变.
  • final声明的常量允许声明后再赋值,赋值后不可改变,final声明的变量是在运行时确定的.
  • final不仅有const的编译时常量的特性,最重要的它是运行时常量,并且final是惰性初始化,即在运行时第一次使用前才初始化.

final和const区别:

  • final可以开始不赋值,只能赋一次;而final不仅有const的编译时常量的特性,最重要的它是运行时常量,并且final是惰性初始化,即在运行时第一次使用前才初始化.
//const常量
const PI=3.14;
//PI=3.14159;  //const定义的常量没法改变
print(PI);
// final 常量
final a;
 a = 13;
//  a=14; //不能再重新赋值
 print(a);
 final d = new DateTime.now();
 print(d);//2023-05-01 14:16:39.145643

16.2 Dart中const identical函数

dart:core 库中identical函数的用法介绍

  • 用法:
bool identical(
    Object? a,
    Object? b
)
  • 检查两个引用是否指向同一个对象
var o = new Object();
var isIdentical = identical(o,new Object());//false, different objects
print(isIdentical);
isIdentical = identical(o,o);// true , same object
print(isIdentical);
isIdentical = identical(const Object(),const Object());//true, const canonicalizes 规范化
print(isIdentical);
isIdentical = identical([1], [1]); // false
print(isIdentical);
isIdentical = identical(const [1], const [1]); // true
print(isIdentical);
isIdentical = identical(const [1], const [2]); // false
print(isIdentical);
isIdentical = identical(2, 1 + 1); // true, integers canonicalizes
print(isIdentical);

案例1: identical对两个对象的引用检查

  var o1 = new Object();
  var o2 = new Object();

  print(identical(o1,o2));  //false  不共享存储空间
  print(identical(o1,o1));   //true 共享存储空间

案例2: 采用const修饰的常量构造函数通过identical引用检查

  //const Object()表示实例化常量构造函数
  //o1 和 o2共享了存储空间
  var o1 = const Object();
  var o2 = const Object();
  print(identical(o1,o2));  //true 共享存储空间
  print(identical(o1,o1));  //true 共享存储空间

案例3: 两个相同的List列表

  var a=[2];
  var b=[2];
  print(identical(a,b)); //false 不共享存储空间

案例4: const修饰的两个相同元素的List列表

print(identical(const [2],const [2])); //true
const a=[2];
const b=[2];
print(identical(a,b)); //true 共享存储空间

案例5: const修饰的两个不同元素的List列表

  const c=[2];
  const d=[3];
  print(identical(c,d)); //false  不共享存储空间
  • 因此: const关键词在多个地方创建相同的对象的时候,内存中只保留了一个对象
  • 共享存储空间条件: 1、常量 2、值相等.

16.3 Dart普通构造函数

案例:

class Container{
    int width;
    int height;
    Container({required this.width,required this.height});
}
void main(){
    var c1 = new Container(width: 100, height: 100);
    var c2 = new Container(width: 100, height: 100);
    print(identical(c1,c2));//false, c1和c2在内存中存储了2份
}

16.4 常量构造函数

常量构造函数总结如下:

  1. 常量构造函数需以const关键字修饰
  2. const构造函数必须用于成员变量都是final的类
  3. 如果实例化时不加const修饰符,即使调用的是常量构造函数,实例化的对象也不是常量实例
  4. 实例化常量构造函数的时候,多个地方创建这个对象,如果传入的值相同,只会保留一个对象.
  5. Flutter中const修饰不仅仅是节省组件构建时的内存开销,Flutter在需要重新构建组件的时候,由于这个组件不应该改变的,重新构建没有任何意义,因此Flutter不会重新构建const组件.

案例: 常量构造函数

class Container {
    final int width;
    final int height;
    const Container({required this.width,required this.height});
}
  • 案例调用
var c1 = Container(width: 100, height: 100);
var c2 = Container(width: 100, height: 100);
print(identical(c1,c2));//false
var c3 = const Container(width:100,height:100);
var c4 = const Container(width:100,height:100);
print(identical(c3,c4));//true
var c5 = const Container(width:100,height:100);
var c6 = const Container(width:100,height:100);
print(identical(c5,c6));//false
  • 实例化常量构造函数的时候,多个地方创建这个对象,如果传入的值相同,只会保留一个对象.

16.5 总结

  1. Dart常量用: final和const修饰符修饰.
  • const声明的常量是在编译时确定的,永远不会改变.
  • final声明的常量允许声明后再赋值,赋值后不可改变,final声明的变量是在运行时确定的.
  • final不仅有const的编译时常量的特性,最重要的它是运行时常量,并且final是惰性初始化,即在运行时第一次使用前才初始化.
  1. final和const区别:
  • final可以开始不赋值,只能赋一次;而final不仅有const的编译时常量的特性,最重要的它是运行时常量,并且final是惰性初始化,即在运行时第一次使用前才初始化.
  1. identical: 检查两个引用是否指向同一个对象
  2. 常量构造函数总结如下:
  • 常量构造函数需以const关键字修饰
  • const构造函数必须用于成员变量都是final的类
  • 如果实例化时不加const修饰符,即使调用的是常量构造函数,实例化的对象也不是常量实例
  • 实例化常量构造函数的时候,多个地方创建这个对象,如果传入的值相同,只会保留一个对象.
  • Flutter中const修饰不仅仅是节省组件构建时的内存开销,Flutter在需要重新构建组件的时候,由于这个组件不应该改变的,重新构建没有任何意义,因此Flutter不会重新构建const组件.
  1. 实例化常量构造函数的时候,多个地方创建这个对象,如果传入的值相同,只会保留一个对象.

猜你喜欢

转载自blog.csdn.net/SharkToping/article/details/130510489
今日推荐