Flutter 学习第三天 面向对象 和 库的使用

这个转自我自己的有道云 想看图片去那里

文档:Day2_26 Dart 面向对象 异步语法.md
链接:http://note.youdao.com/noteshare?id=6bfd9ee6019989e583ecbb5009342f84&sub=C287257DA6244F2F9366855BE54A320B

Dart 面向对象 异步语法

Dart语言很重要的一个特质就是, 它是一个面向对象的语言

所以我们必须将它的一些语法搞清楚

这个语言非常的新, 所以它的一些技术就是为了解决之前的语言没有办法解决的痛点

3.2.1 普通的构造函数
class 类名 {
    修饰词(final/var/const) 变量类型(String/int/double...) 属性名
    
    返回值 方法名(参数列表) ...
}
main(List<String> args) {
  print(new Person("test", 12));
  print(Person1("test", 13));
}

class Person {
  String name;
  int age;

  Person(String name, int age ) {
    this.name = name;
    this.age = age;
  } 

  @override
  String toString() {
    // TODO: implement toString
    return "name = $name, age = $age";
  }
}

class Person1 {
  String name;
  int age;
  
  Person1(this.name, this.age);

  @override
  String toString() {
    // TODO: implement toString
    return "name = $name, age = $age";
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-06G5zPzH-1582795200894)(FAF91B9E80D74B7F97ADE2882C39BF4E)]

3.2.2 命名构造函数

为什么要用这个玩意

因为我们的Dart没有函数重载, 所以如果想要重载函数我们就需要命名构造函数

main(List<String> args) {
  print(new Person());
  print(new Person.withArgments("test", 12));
  print(new Person.withMapArgments({"name": "test2", "age": 123}));
}

class Person{
  String name;
  int age;

  Person() {
    name = "";
    age = 0;
  }

  Person.withArgments(String name, int age) {
    this.name = name;
    this.age = age;
  }

  Person.withMapArgments(Map<String, Object> map) {
    name = map["name"];
    age  =map["age"];
  }

  @override
  String toString() {
    // TODO: implement toString
    return "name = $name, age = $age";
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f0egh9FJ-1582795200897)(9B8D5B05519742DBA3DF94A52E3323F7)]

3.2.3 类的初始化列表

构造函数的简化写法就是 Person(this.name) 其实也就是简化的初始化列表的写法

我们可以在初始化列表中初始化 final定义的变量

import 'dart:math';

main(List<String> args) {
  var p1 = Point(2.0, 3.0);
  print(p1);

  var p = Person2("why");
  print(p.name);
  print(p.age);
  var p2 = Person2("why", age: 30);
  print(p2.name);
  print(p2.age);
}

class Point {
  final num x;
  final num y;
  final num distance;

  // 错误的写法
  //  因为在执行下面的代码的时候对象的初始化已经完成了, 所以不能对它再进行赋值
  //  Point(this.x, this.y) {
  //    distance = sqrt( x * x + y * y);
  //  }

  // 正确的写法
  Point(this.x, this.y) : distance = sqrt(x * x + y * y);

  @override
  String toString() {
    // TODO: implement toString
    return "x  = $x, y = $y, distance = $distance";
  }
}

class Person2 {
  final String name;
  final int age;

  Person2(this.name, {this.age = 10});
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mrgegsBL-1582795200897)(0F095807073E462A8EE8ECBE5A609138)]

  • 初始化列表和可选参数的默认值的区别

直接的区别就是, 初始化列表里面是可以写一些语句的

但是可选参数的默认值是不行的

main(List<String> args) {
  print(  Person("why").age);
  // 可见这种写法没有问题 Person(String name, {this.age = 10})
  print(Person("why", age: 20).age);
}

const temp = 20;

class Person {
  final String name;
  // final 意味着这个age只能赋值一次
  final int age;

  // 初始化列表这里是可以写一些语句的
  // 以下的这个初始化列表里的语句含义和{int age = 10} 是一样的
  // 如果我们创建对象的时候, 我们就使用传入的age, 如果没有就使用默认值
  // 那我们能不能这样做呢

  // Person(this.name, {int age}) : this.age = age ?? 10 {
  //   // this.age = 10; 不能在里面进行初始化
  // }

// 可见这种写法没有问题
// 而且在上面的这种需求之下这种做法是最好的
// 但是我们下面的这种写法它有一个局限性
// flutter 经常用初始化列表的方式 初始化变量的方式
// 因为初始化列表里面可以写语句所以在里面做一些初始化初始化变量的方式dart很喜欢这样做
// 但是为什么不直接在对应的位置给它默认值呢
// 在这个里面做初始化只能给它赋值一个确定的值
//  这个参数列表里面只能做赋值操作 或者是 虽然后面用了一些其它的, 但是整个的语句话还是一个赋值语句
// Person(this.name, {this.age = 10});
// Person(this.name, {this.age = temp ?? 30});

// 但是如果我们想要做一些三元运算符之类的操作 我们就在这个里面写就不行了
// 而且这样比较好看
// 就像下面这样如果想要做一个判断再赋值 还是初始化列表比较方便
Person(this.name, {int age}) : this.age = temp > 20 ? 30 : 50 {

}

// Person(this.name, {int age = 10});  这样初始化只能初始化普通的非final类变量

  // 保留
  // Person(this.name, {this.age = 10});
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-COg8sQUy-1582795200898)(BEAD4574BF14497D89E2914CA6D729BC)]

同时我们也可以看到dart源码里面很多的初始化列表的使用方式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TnJ8JwzI-1582795200898)(FB3DE271DA144FFCBF8E7EDE09D02603)]

3.2.4 重定向构造函数

我们希望在构造函数中使用这个类的其他构造函数

我们就可以使用重定向构造函数

其实也是为了解决没有函数重载带来的问题

main(List<String> args) {
  print( Person.fromName("test") );
}

class Person {
  String name;
  int age;

  Person(this.name, this.age);
  Person.fromName(String name) : this(name, 0);

  @override
  String toString() {
    // TODO: implement toString
    return "name = $name, age = $age";
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BcL1KuY3-1582795200898)(F1995644040C4BF4907045B6DD3A1F1F)]

3.2.5 类常量构造函数

常量类的构造函数 可以做到如果传入同样的参数 就可以得到同样的对象

这样做可以节省内存, 避免一个对象多次创建

main(List<String> args) {
  var p1 = Person("same");
  // 这个省略的是new 
  var p2 = new Person("same");
  // identical(a, b) 这个函数可以判断
  // a, b两个变量是否指向同一个对象
  print(identical(p1, p2));

  const p3 = Person2("same");
  // 这个省略的是const
  const p4 = const Person2("same");
  print(identical(p3, p4));
}

class Person {
  String name;

  Person(name) {
    this.name = name;
  } 
}

class Person2 {
  final String name;
  final int age;

  // const Person2(this.name);
  // 相当于 多个参数用逗号隔开
  const Person2(name) : this.name = name, this.age = 10;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kqWV7iPv-1582795200899)(DBF6F2434A0B42B3AF124F04FE7FD427)]

3.2.6 工厂构造函数

普通构造函数会默认返回创建的对象

工厂函数需要手动返回一个对象, 不管这个对象是自己创建的还是别的

main(List<String> args) {
  print(identical(Person("test"), Person("test")));
}

class Person {
  String name;

  static final Map<String, Person> _cache = <String, Person>{};

  factory Person(String name) {
    if( _cache.containsKey(name) ) {
      return _cache[name];
    } else {
      final p = Person._internal(name);
      _cache[name] = p;
      return p;
    }
  }

  Person._internal(this.name);
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lj5n3ynd-1582795200899)(B58AE8E883624502A84ECAF3946EED73)]

  • 工厂构造函数和常量构造函数

工厂构造函数和常量构造函数有相似的作用 但是他们是不一样的

另外讲一个vscode的快捷键 alt + shift + 向下, 向下拷贝

  • 这个是常量构造函数
main(List<String> args) {
  var p1 = Person("why", "22");
  var p2 = Person("why", "22");
  print("${identical(p1, p2)}"); // false

  const p3 = Person2("why");
  const p4 = Person2("why");
  print(identical(p3, p4));
}

// 常量构造函数
// 需求:放入相同color的参数的时候就会返回相同的对象
// 放入相同name的时候也会产生相同的对象
// 常量构造函数一般一个函数只能有一个常量参数 如果有多个就没有原来的功能了
class Person {
  final String name;
  final String age;

  const Person(this.name, this.age);
}

class Person2 {
  final String name;

  const Person2(name): this.name = name;
  // const Person2(this.name);
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m204Ujt8-1582795200899)(F8187E5F863C4FF99E990C24B9EF9E77)]

  • 使用工厂构造函数
main(List<String> args) {
  var p5 = Person3.withName("why");
  var p6 = Person3.withName("why");
  print(identical(p5, p6));
}

class Person3 {
  final String name;
  final String color;

  // _作用就像是private 只能在本模块(文件)中使用
  // 这里存储的是Person对象的地址
  static Map<String, Person3> _nameMap = <String, Person3>{};
  static Map<String, Person3> _colorMap = <String, Person3>{};

  // 工厂构造函数需要手动返回对象
  factory Person3.withName(String name) {
    if( _nameMap.containsKey(name) ) {
      return _nameMap[name];
    } else {
      var p3 = Person3._internal(name, "default");
      // 这里传递的是指针
      _nameMap[name] = p3;
      return p3;
    }
  }

  factory Person3.withColor(String color) {
    if(_colorMap.containsKey(color)) {
      return _colorMap[color]; 
    } else {
      var p3 = Person3._internal("default", color);
      _colorMap[color] = p3;
      return p3;
    }
  }

  Person3._internal(this.name, this.color);
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p8uEYsrL-1582795200900)(5218F14ADA994D73BFC50FE8A19459E9)]

3.3 setter 和 getter

默认情况下, Dart中定义的属性是可以被外界直接访问的

但是如果我们希望监控这个类的属性被访问的过程, 这个时候我们就可以使用setter 和 getter了

main(List<String> args) {
  var p1 = Person("why");
  p1.name = "test";
  print(p1.name);

  p1.setName = "name";
  print(p1.getName);
}

class Person {
  String name;

  Person(this.name);

  // 这个方法的名字虽然可以乱取, 但是还是建议就这样取
  // setter
  set setName(String name) => this.name = name;
  // set setName(String name) {
  //   this.name = name;
  // }

  // getter
  String get getName => name;
  // 注意啊getter 不需要传参
  // String get getName {
  //   return name;
  // }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d2p8ccaK-1582795200900)(EC3B81A08B604386B7300D66462BCB9E)]

3.4 类的继承

面向对象的一大特性就是, 继承可以减少我们的代码量, 可以多态的使用前提

Dart 的继承使用extends关键字, 字类中使用super来访问父类

父类中的所有成员都会被继承, 但是构造方法除外 基本上和java没有区别

  • 注意一点: _xxx 前面有_的变量是包内的私有变量

如果还有东西想要在初始化列表的地方初始化, 我们可以继续在super后面加,添加 : super(color), this.age = age;

main(List<String> args) {
  
}

class Animal {
  int age;

  Animal(this.age);
}


class Person extends Animal {
  String name;
  Person(this.name, int age): super(age);

  @override
  String toString() {
    // TODO: implement toString
    return "name = $name, age = $age";
  }
}

3.5 抽象类

dart的抽象类和java还不太像, 但是大致一样

main(List<String> args) {
  
}

abstract class Shape {
  // 可以有方法的实现
  int getArea();

  String getInfo() {
    return "形状";
  }
}
  • 注意一: 继承自抽象类以后, 必须实现抽象类的抽象方法
main(List<String> args) {
  
}

abstract class Shape {
  // 可以有方法的实现
  int getArea();

  String getInfo() {
    return "形状";
  }
}

class Rectangle extends Shape {
  @override
  int getArea() {
    // TODO: implement getArea
    return null;
  }
  // 子类构造函数不能调用父类的工厂函数
  
}
  • 注意二: 抽象类不能实列化

但是我们可以通过工厂函数返回它的字类对象

所以dart的源码里面有些时候会看到直接用抽象类的构造函数返回抽象类的对象

main(List<String> args) {
  var s1 = Shape();
  print(s1);
}

abstract class Shape {
  // 可以有方法的实现
  int getArea();

  String getInfo() {
    return "形状";
  }

  factory Shape() {
    return null;
  }
}

同时还有external 关键字

这个东西的作用是将方法的声明和实践分离 同时使用的还有@patch 和 @servlet一样是一个注释

有些时候你看到dart源码里面它没有实现其实是放到其他的地方去了

3.6 隐式接口

Dart 中没有一个关键字是用来定义接口的

没有诸如 interface/protocol 的关键字

默认情况下所有的类都是隐式接口

  • 注意: 当我们把一个类当成接口来使用的时候, 那么我们必须实现所有的方法(重写方法) 就算有实现也要重写
main(List<String> args) {
  var superM = SuperMan();
  superM.flying();
  superM.running();
}

class Runner {
  void running() {
    print("Runner running");
  }
}

class Flyer {
  void flying( ) {
    print("Flyer flying");
  }
}

class SuperMan implements Flyer, Runner {
  @override
  void flying() {
    // TODO: implement flying
  }

  @override
  void running() {
    // TODO: implement running
  }
  
}

执行的重写的代码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MM3mSmKF-1582795200901)(60B621AF74AE48D083A7A4DA45AD2BFD)]

接口主要是为了解决一个多继承的问题, 多继承使用起来问题太多, 接口就比较好

  • 如果在继承的方法里面有我们就不用重写了
main(List<String> args) {
  var superM = SuperMan();
  superM.flying();
  superM.running();
}

class Runner {
  void running() {
    print("Runner running");
  }
}

class Flyer {
  void flying( ) {
    print("Flyer flying");
  }
}

class Animal {
  void eating() {
    print("eating Animal");
  }

  void running() {
    print("running");
  }
}

class SuperMan extends Animal implements Runner {

}


3.7 混入

虽然继承 使用接口抽象类都很好 可以大幅度的减少代码的行数, 但是我们还是想要直接使用这些方法 我们使用接口就可以了

我们可以使用混入

  1. 我们在定义可以混入的类的时候, 我们用mixin进行定义, 不用class
  2. 用with进行混入
main(List<String> args) {
  var s1 = SuperMan();
  s1.running();
}

mixin Runner {
  void running() {
    print("running");
  }
}

mixin Flyer {
  void flying() {

  }
}

class SuperMan with Runner, Flyer {

}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3mp9Feuw-1582795200901)(9F7D25D11A274ADB961DAF618A95F2AE)]

3.8 类属性和类方法

main(List<String> args) {
  Person.count = 10;
  print(Person.count);
  Person.staticMethod();
}

class Person {
  // 普通属性
  String name;

  // 类属性
  static int count = 0;

  // 类方法
  static void staticMethod() {
    print("method");
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JX5XHOb3-1582795200901)(B9ADBED303C44388881C51613BBD6E1E)]

3.8 枚举

枚举是用enum定义的

有些语言没有这个东西 比如js 没有枚举其实是它的缺陷, 所以ts对它做了扩充

main(List<String> args) {
  final color = Colors.red;

  switch (color) {
    case Colors.red:
      print("这个是红色");
      break;
    case Colors.blue:
      print("这个是蓝色");
      break;
    default:
  }
  print(Colors.values);
  print(Colors.red.index);
  print(Colors.red.toString());
}

// 枚举它有固定的值
// 使用它最大的好处就是因为它类型安全
// 它能有的值就这些 所以它安全
enum Colors {
  red,
  blue
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PXvBo0YV-1582795200902)(E7C4A90573DE4606BFAE0D5044C62BA2)]

四. 使用库

一般来说一个dart文件就是一个库

4.1 系统系统的库

// import "dart:io";
// import "dart:isolate";
// import "dart:async";
// import "dart:math";

// 系统的库: import "dart:库的名字"

// 不是主要的库 就需要引入
import 'dart:math';

main(List<String> args) {
  // 系统库是不需要导入的 dart:core 是核心库是默认会导入的
  final num1 = 20;
  final num2 = 30;
  print(min(num1, num2));
}

4.2 使用自定义的库

我们建立这样一个目录

+ 库的使用
  + utils
    math_utils.dart
  使用自定自定义的库.dart 

math_utils.dart

int sum(int num1, int num2) {
  return num1 + num2;
}

double mul(double num1, double num2) {
  return num1 * num2;
}

然后我们就想要在自建的库里面使用这几个东西

使用自定义的库.dart

import 'utils/math_utils.dart';

main(List<String> args) {
  print(sum(10, 20));
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AFePxziL-1582795200902)(F283490FDA954C93969AD4CCFB9562E9)]

  • 补充一: 如果命名冲突 我们可以通过as 关键字 给库起别名
/**
 * 1. 补充一:如果命名冲突 我们可以通过 as 关键字给库起别名
 */

import 'utils/math_utils.dart' as mUtils;

main(List<String> args) {
  print(mUtils.sum(10, 20));
}

void sum(num1 , num2 ) {
  print(num1 + num2);
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OXj6vCdS-1582795200902)(1B97133EDEFA481482821A5D818CA6EA)]

  • 补充二: 导入库是默认导入其中的所有东西
/**
 * 1. 补充一:如果命名冲突 我们可以通过 as 关键字给库起别名
 * 2. 补充二: 默认情况下是导入库中的所有的内容
 */

// import 'utils/math_utils.dart' as mUtils;
import "utils/math_utils.dart";

main(List<String> args) {
  // print(mUtils.sum(10, 20));
  print(sum(10, 20));
  print(mul(10, 20));
}

// void sum(num1 , num2 ) {
//   print(num1 + num2);
// }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xzzMakQ2-1582795200902)(0B3D812D0B4D4C35B45551A267DE0D23)]

但是如果你只想导入某些东西, 或者某个东西呢

  • 补充二: 默认情况下是导入库中的所有的内容
  • 但是如果不想这样呢

我们可以使用show, hide关键字

/**
 * 1. 补充一:如果命名冲突 我们可以通过 as 关键字给库起别名
 * 2. 补充二: 默认情况下是导入库中的所有的内容
 *                     但是如果不想都导入的话
 *    * show: 指定要导入库
 *    * hide: 隐藏某个要导入的内容, 导入其他内容
 */

// import 'utils/math_utils.dart' as mUtils;
// import "utils/math_utils.dart";
import "utils/math_utils.dart" show sum, mul;
import "utils/math_utils.dart" hide sum, mul;

main(List<String> args) {
  // print(mUtils.sum(10, 20));
  print(sum(10, 20));
  print(mul(10, 20));
}

// void sum(num1 , num2 ) {
//   print(num1 + num2);
// }
  • 补充三: 如果我们有多个要导入的库想要一次性导入怎么办呢

一般情况我们是一个一个导入

/**
 * 1. 补充一:如果命名冲突 我们可以通过 as 关键字给库起别名
 * 2. 补充二: 默认情况下是导入库中的所有的内容
 *                     但是如果不想都导入的话
 *    * show: 指定要导入库
 *    * hide: 隐藏某个要导入的内容, 导入其他内容
 */

// import 'utils/math_utils.dart' as mUtils;
// import "utils/math_utils.dart";
import "utils/math_utils.dart" show sum, mul;
import "utils/math_utils.dart" hide sum, mul;
import 'utils/data_utils.dart';

main(List<String> args) {
  // print(mUtils.sum(10, 20));
  print(sum(10, 20));
  print(mul(10, 20));
  print(dataFormat());
}

但是如果文件多了怎么办呢

那难道我们一个一个的导入吗

我们可以用part关键字拆分(但是这个官方已经不建议再用了) 然后一次拆分

这个东西用起来非常麻烦, dart也发现这个东西不好用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ouzl7Mtm-1582795200903)(6985997B015244DD92F176D1EEE3F364)]

他们搞了一个东西让很多文件可以通过一个文件一次性引入

这样我们用的时候就可以一次性引入

文件结构

+ 库的使用
  + utils
    math_utils.dart
    data_utils.dart
    utils.dart
  使用自定自定义的库.dart 

utils.dart

export "./data_utils.dart";
export "./math_utils.dart";

这样我们只需要引入一次就把两个文件都引入了

相当于给它抽取除一个公共的头文件

使用自定自定义的库.dart

import "utils/utils.dart";

main(List<String> args) {
  // print(mUtils.sum(10, 20));
  print(sum(10, 20));
  print(mul(10, 20));
  print(dataFormat());
}

  • 补充四: 库的私有变量用 _xx 来定义, 下划线是区分私有和公共的一种方式

math_utils.dart

int sum(int num1, int num2) {
  return num1 + num2;
}

double mul(double num1, double num2) {
  return num1 * num2;
}

// 下划线是区分私有和公共的一种方式
int _min(int num1 , int num2) {
  return num1 > num2 ? num2 : num1;
}

使用自定自定义的库.dart

import "utils/utils.dart";

main(List<String> args) {
  // print(mUtils.sum(10, 20));
  print(sum(10, 20));
  print(mul(10, 20));
  print(dataFormat());

  // _min(10,20); 在外面访问就会博爱错
}

如果外面访问就会直接报错

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QONnptsK-1582795200903)(FB54D611E1CF4316A4597EF2044B760C)]

4.3 使用第三方的库

这个使用第三方库和node很想

node管理第三方的库是用了一个package.json来管理

叫做

pubspec.yaml 就是通过这个东西来管理的

它有几个属性 嘛差不多就是名字的意思

我们要使用第三方的库 我们就可以到这个 pub.dev 去

这个网站是必须记住的

name: codewhy 
description: a dart library
dependences: 

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sNeYFQZk-1582795200903)(1B188382D7E04D98A8F0E875B94207E7)]

我们就安装一个 http

找到以后我们点击 installing

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PTjCDsiz-1582795200904)(5BE1DFB7247D4DF8A41899B47BBBA7F2)]

就发现有个dependencies

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4elLR6ri-1582795200904)(D715958010AA48C09E178898E3BE9817)]

我们就可以来到对应的位置

name: codewhy
description: a dart library
dependencies:
  http: ^0.12.0+4

然后进入对应的文件夹 pub get 就可以了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6j48Us1O-1582795200905)(FC5903FE618E4D9589C51328F556B46D)]

一旦完成, 你就会发现多了几个文件 都是一些日志信息

下面我们用他的代码来试一试吧

import 'package:http/http.dart' as http;

main(List<String> args) async {
  var url = 'http://49.234.222.253:8080/sDemo/register/checkCompanyRigster.do';
  var response = await http.post(url, body: {"uid" : "1"});
  print('Response status: ${response.statusCode}');
  print('Response body: ${response.body}');
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Dn2cOWag-1582795200905)(F9C43B9C1FFE4078A5255AB97276ADB9)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值