Dart语言基础

1、变量与常量

1.1 标识符

在Dart中,所有的类名、变量名、方法名都是标识符。

  • 标识符称必须由数字、字母、下划线和美元符($)组成。
  • 标识符不能以数字开头
  • 标识符不能是保留字和关键字
  • 标识符名字是区分大小写
  • 标识符最好要见名思意 :变量名称建议用名词,方法名称建议用动词

1.2 变量

变量的什么方式大致分为两种:

1、明确声明:明确的指定变量所属的类型。

  String str="why";
  int num;
  bool isRed=true;

2、类型推导:在声明变量时没有明确指定变量的类型,有dart根据赋值的数据类型进行自动推导。

注意:使用类型推导方式声明的变量虽然没有明确指定类型,但是在变量第一次赋值时已经确定了变量的类型,所以在变量重新赋值时只能赋值与第一次赋值时类型相同的数据。

  var str="why";
  var num;
  num=200;
  var isRed=true;

1.3 常量

使用final关键字和const关键字修饰的变量称为常量,常量只可以赋值一次。

  • final:final修饰的常量可以是一个确定的值或对象,也可以是一个表达式在程序运行时的结果。
  • const:const修饰的变量必须在程序编译时就有确定的一个值或对象
  • 声明常量是可以省略常量的类型,由赋值行确定。
  const int f=10;
  const int m=20;
  const c=f+m;  //数值运算在编译时就可以确定,所以可以赋值
  const list=[1,2,3];
  //const item=list[1];   这样就不可以,以为右边的值必须要到程序运行时才能确定。

  final p=new Person();
  final name=p.getName;  //这样可以
  //const name=p.getName; //这样不可以

2、数据类型

2.1、int、double、String、bool

2.1.1、类型声明

int 整型:取值范围是 -2^63 ~ 2^63 - 1

int i=100;
int j=300;

double 双浮点型:取值范围是 -2^63 ~ 2^63 - 1

double d=3.1415;

bool 布尔型

//bool,布尔值没有其他语言的非零即真,它的取值只能是true或false;
bool flag1=true;
bool flag2=false;

String 字符串

String str="今天你微笑了吗?";
String sub=str.substring(1,str.length);
print(sub);

字符串的定义

  //用双引号或者单引号将文字括起来,就得到了一个字符串
  String str1="hello word!";
  String str2='hello word!';

  String name="张三";
  int age=33;
  double alipay=1000.9;
  double weixin=3000.7;

  //字符串拼接,dart使用${表达式}进行字符串的拼接
  String str3='My name is ${name} and age is ${age}.';

  //在字符串的前面加一个r字符,就会忽略到字符串里面的$符号,得到一个原始字符串
  String str4=r'My name is ${name} and age is ${age}.';

  //当表达式是一个确定的值的时候,可以省略{}
  String str5='My name is $name and age is $age.';
  //当表达式不是一个确定的值的时候,{}必须要有
  String str6='I have ${alipay+weixin} yuan.';

  print(str1);
  print(str2);
  print(str3);
  print(str4);
  print(str5);
  print(str6);
2.1.2、数据类型的相互转换

int &double

  int a=10;
  double b=a.toDouble();
  a=b.toInt();
  print(a);
  print(b);

double& String

  double age=18.8;
  String ageStr=age.toString();
  age=double.parse(ageStr);
  print(age);
  print(ageStr);

int & String

  int c=100;
  String cc=c.toString();
  c=int.parse(cc);
  print(cc);
  print(c);

2.2、 集合

2.2.1、list 列表

list在Dart中叫做列表,它同时拥有其他语言中的数值和list集合的特性。

  • 创建
// list  用法类似于其他语言的数组,但实际上是一个集合
  //创建集合,用[]或者new 的方式创建
  List list1 = [];  //创建一个长度为0的list
  List<int> list2 = [4,2,6,7];
  List list3 = new List(4); //创建一个长度为4的list,里面的元素都是null
  List list4 = new List(0); 创建一个长度为0的list

  //List.from()根据一个iterable创建列表
  List list5 = List.from(list2);

  //castFrom<S,T>() ,将元素为S类型的列表,修改为元素为T类型的列表,这里的元素必须本来就
  // 是T类型,相当于将列表中的每个元素都做了强转操作。
  //List<String> list6=List.castFrom<int,String>(list2);

  list1.add("haha");
  list1.add("xixi");

  //将cast<T> ,将列表中的元素全部强转为T类型,里面的元素必须本来就是T类型
  List<String> list7= list1.cast<String>();

  print(list1);
  print(list2);
  print(list3);
  print(list4);
  print(list5);
  //print(list6);
  print(list7);
  • 添加元素
  //添加元素
  list.add(10);
  print(list);
  //添加另一个list里面的所有元素
  List list8 = [4,2,6,7];
  list.addAll(list8);
  print(list);
  • 删除元素
  //移除元素
  List list=[2,4,6,8,2,4];
  list.remove(4);
  print2(8,list);

  //根据角标移除元素
  list.removeAt(4);
  print2(9,list);

  //移除最后元素
  list.removeLast();
  print2(10,list);

  //移除符合条件的元素
  list.removeWhere((e)=> e==8);
  print2(11,list);

  //removeRange(startIndex,endIndex) ,移除从startIndex到endIndex的元素,包头不包尾
  list.removeRange(0, 2);
  print2(12,list);
  • 遍历
  //循环遍历
  for(var item in list){
    print("item = $item");
  }

  //使用forEach方法遍历
  list.forEach((e){
    print("值为:$e");
  });
  • 排序
  //排序,可以指定规则,默认从小到大
  list.sort();
  print2(15,list);

  //匿名函数的返回值大于0为倒序,反之则正序,为0则维持原样
  List list9=[4,2,6,7];
  list9.sort((a,b) => -(b-a));
  print2(16,list9);
  • 元素筛选
//从0的位置开始取指定数量的元素
  List iterable = list.take(3).toList();
  print2(17,iterable);

  //从0的位置跳过指定数量的元素,选取剩下的的元素,与take刚好相反
  List iterable1 = list.skip(3).toList();
  print2(18,iterable1);

  //从0的位置开始取符合指定规则的元素,遇到不符合的元素就会停止选取
  Iterable iterable2 =list9.takeWhile((e) => e==6);
  print2(19,List.from(iterable2));

  //从0的位置开始取符合指定规则的元素,遇到不符合的元素就跳过,选取下一个
  Iterable iterable3 =list9.where((e) => e==6);
  print2(20,List.from(iterable3));
  • 元素类型变换
  //使用map方法将元素原来的int类型转为String类型
  Iterable<String> iterable4 = list.map<String>((e) =>e.toString());
  print2(21, List.from(iterable4));
2.2.2、set 集合
  //创建Set集合
  Set set =new Set();
  Set set1 ={};
  
  //Set集合中的元素是不可重复的,我们可以利用Set集合的这个特性对list列表做去重处理。
  Set set2 ={1,2,3,2,4};
  print(set2);
  
  //给list集合去重
  List list=[1,2,2,3,1,4];
  list=Set.from(list).toList();
  print(list);

set集合与list非常相似,list有的方法set大部分都有。

2.2.3、map 映射
  • 创建
  //1、使用new 关键字创建
  Map map1 = new Map();
  Map<String,String> map2 =new Map<String,String>();
  //print("map2=> ${map2.runtimeType}");

  //2、使用{} 创建
  Map<int,Person> map3 ={1:new Man("haha"),2:new Man("张三"),3:new Man("李四")};

  //3、通过工厂方法创建,由其他的Map或Iterable变换而来
  // castFrom 强制类型转换,与cast类似,Map<K2,V2> map2= map1.cast<K1,V1,K2,V2>();
  Map map6=Map.castFrom<int,Person,int,Man>(map3);
  print("map6的元素类型为 ${map6.runtimeType}");

  //from 以源map中的元素创建新的map,指定泛型必须与源map一致或不指定,键可以为null。
  Map<int,String> map7={1:"李四",2:"王五",3:"赵六"};
  Map<int,String> map8=Map.from(map7);
  print("map8的元素类型为 ${map8.runtimeType}");
  print("map8 => $map8");

  //fromEntries 从一个iterable实例化一个map
  List<MapEntry<int,String>> testList = List<MapEntry<int,String>>();
  testList.add(MapEntry(1,"哈哈哈"));
  testList.add(MapEntry(2,"嘻嘻嘻"));
  Map<int,String> map9=Map.fromEntries(testList); //List是Iterable的子类
  print("map9 => $map9");
  print("map9的元素类型为 ${map9.runtimeType}");

  // of ,键可以为null
  Map<int,String> map10 = Map.of(map7);
  print("map10的元素类型为 ${map10.runtimeType}");
  
  //fromIterable 根据Iterable创建Map,后面两个参数可以对元素进行变换
  List<int> list=[1,2,3,4];
  Map map13=Map.fromIterable(list,key:(a)=> a+a,value:(a)=>a*a);
  print("map13=> $map13");

  //fromIterables 由两个Iterable,第一个提供key,第二个提供value,创建一个Map,
  // 两个Iterable长度要相等
  List<int> list1=[1,2,3];
  List<String> list2=["haha","xixi","hehe"];
  Map map14=Map.fromIterables(list1, list2);
  print("map14=> $map14");


  //4、通过cast、map变换得到
  //cast  强制类型转换 Map<K,V> map2= map1.cast<K,V>();
  Map<int,Person> map4={1:new Man("haha"),2:new Man("张三"),3:new Man("李四")};
  Map<int,Man> map5=map4.cast<int,Man>();
  print("map4的元素类型为 ${map4.runtimeType}");
  print("map5的元素类型为 ${map5.runtimeType}");

  //map ,对Map中的每个MapEntry做修改,得到新的Map集合
  Map<int,int> map12 = map4.map<int,int>((k,v){
    k=k+1;
    return new MapEntry(k, v.name.length);
  });
  print("map12的元素类型为 ${map12.runtimeType}");
  print("map12=> $map12");

  //5、创建不可修改的(只读的)Map,需传入另外一个Map,不能传null
  Map map15=Map.unmodifiable(null);
  print("map15=> $map15");

  //6、Map.identity(); 搞不懂
  • 添加元素
  //创建一个空的map集合
  Map map=new Map();

  // 使用 putIfAbsent 方法添加元素
  // 第三个参数:如果key已经存在,则忽略这一步,如果可以不存在吗,就添加一个新的键值对。
  map.putIfAbsent(1, ()=> new Person("haha"));
  print("map=> $map");

  // 使用addAll添加另一个map中的所有数据
  Map mapData={2:"张三",3:"李四"};
  map.addAll(mapData);
  print("map=> $map");

  //使用 addEntries 添加一个以MapEntry为元素的Iterable的所有数据
  List<MapEntry> list=[MapEntry(5,"王五"),MapEntry(6,"赵六")];
  map.addEntries(list);
  print("map=> $map");
  • 删除元素
  //更据可以来删除元素
  map.remove(3);
  print("map=> $map");

  //删除符合指定条件的元素
  map.removeWhere((key,value) => key>5 );
  print("map=> $map");
  • 修改元素
  //第三个参数,如果这个key不存在,那么就添加这个key并且将这个方法的返回值作为value
  map.update(7, (value)=> "李倩",ifAbsent: ()=> "张娜娜");
  print("map=> $map");

  // updateAll 更新所有的value
  map.updateAll((k,v){
    return "${v.toString()} 去吃饭吗?";
  });
  print("map=> $map");
  • 遍历
  //for循环
  for(var key in map.keys){
    print(map[key]);
  }

  //forEach
  map.forEach((k,v){
    print("$k=>$v");
  });

3、 函数

3.1 函数的定义

函数的定义和java非常类似,一个完整的函数结构如下:

返回值 函数名(参数类型1 参数1,参数类型1 参数2){
	函数体;
}

与java不同的是,dart中函数的返回值在书写时可以省略,这时返回值的类型就是函数体的返回值类型,以下两种写法都是正确的。

int sum1(int a,int b){
  return a+b;
}
sum2(int a,int b){
  return a+b;
}

当函数的函数体只有一句时,可以使用简写方式:

sum(int a,int b)=> a+b;

3.2 可选参数

      在Dart中,函数不能重载,也就是说不管函数的参数是什么,只要同一个类中有同名函数,就会报错,为了解决传入不同参数的需求,引入了可选参数的概念,可选参数可以设置默认值。
      可选参数又有位置可选参数和命名命名可选参数两种,可选参数必须声明在函数的参数列表的最后面。

  • 位置可选参数:可选参数列表有[]包裹,在调用函数时传入的参数顺序必须与函数的参数列表一致。
/** 结构:
	返回值 函数名(参数类型1 参数1,[参数类型2 参数2 = 默认值,参数类型3 参数3 = 默认值]){
		函数体; 
	}*/
//声明
String join(String str1,[String str2,String str3="张三"]){
  return "names=>$str1,$str2,$str3";
}

//调用
join("王五","赵六");
join("王五","赵六","胡汉三");
  • 命名可选参数:可选参数列表有{}包裹,在调用函数时传入的参数需要用对应的参数名指明,与参数顺序无关。
//声明
String join2(String str1,{String str2,String str3="张三"}){
  return "names=>$str1,$str2,$str3";
}
//调用
join2("王五",str2:"赵六",str3:"李琪琪");

3.3 函数是一等公民(高级函数)

      在Dart中,一个函数可以作为另一个函数的参数或者返回值进行传递。比如:

main(){

  num(20,30,(res){
    String str="res= $res";
    print(str);
    return res;
  });
}

void num(int a,int b,String fun(int res)){
  int res=a+b;
  String str=fun(res);
  print("str= $str");
}

      作为函数的参数可以写成匿名函数直接在传参时声明。

main(){

  num(20,30,(res){
    String str="res= $res";
    print(str);
    return str;
  });
}

void num(int a,int b,String fun(int res)){
  int res=a+b;
  String str=fun(res);
  print("str= $str");
}

      有些函数的声明书写很复杂,直接写在参数列表可读性非常差,这时我们可以使用typedef关键字对函数的声明起一个别名,这时这个别名就是一个新的类型,可以代表这个函数的类型。

main(){

  num(20,30,(res){
    String str="res= $res";
    print(str);
    return str;
  });
}

void num(int a,int b,PrintResult fun){
  int res=a+b;
  String str=fun(res);
  print("str= $str");
}

typedef PrintResult = String Function(int res);

4 、运算符

4.1 算术运算符(+、-、*、/、~/)

int a = 10;
int b = 2;

//加
print(a + b);
//减
print(a - b);
//乘
print(a * b);
//除
print(a / b);
//取整
int result = a ~/ b;
print(result);
//取余
print(a % b);

4.2 自增、自减(++、–)

//自增
//前++,先给函数打印,后自增
print(a++);
//后++,先自增,再给函数打印
print(++a);

//先给函数打印,再自减
print(a--);
//先自减,再给函数打印
print(--a);

4.3 关系运算符(>、<、>=、<=、==)

int a = 5;
int b = 3;

//判断是否相等
print(a == b);
//判断是否不相等
print(a != b);
//是否大于
print(a > b);
//是否小于
print(a < b);
//是否大于等于
print(a >= b);
//是否小于等于
print(a <= b);

//双等号判断内容相等
String strA = '123';
//String strB = '321';
String strB = '123';
print(strA == strB);

4.4 逻辑运算符(&、&&、|、||、!)

bool isTrue = true;
//取反
print(!isTrue);

//并且
bool isFalse = false;
print(isTrue && isFalse);

//或者
print(isTrue || isFalse);

//日常取反用途
String str = "";
print(!str.isEmpty);

& 和 &&,| 和 || 的区别:

	&:不管&的左边是否为真,都会判断右边是否为真
	&&:如果&的左边是否为假,直接判断定结果为假,不用去看右边的真假
	|:不管&的左边是否为真,都会判断右边是否为真
	||:如果&的左边是否为真,直接判断定结果为真,不用去看右边的真假

4.5 赋值运算符(=、+=、-=、*=、/=、??=)

  • 复合运算符
double a = 10;
//int b;
int b = 5;

//复合运算符
a += 2;
print(a);

a -= b;
print(a);

a *= b;
print(a);

print(a /= b);
  
print(a %= b);
  • ??= ,如果变量没有赋值才进行赋值,否则不进行赋值。
//??=,如果变量没有赋值才进行赋值,否则不进行赋值
b ??= 10;
print(b);
  • 条件运算符(?、??)
 //? 三目运算符:当左边的表达式为真时,使用:左边的值,否则使用:左边的值
 //int gender = 0;
 int gender = 1;
 //String str = gender == 0 ? 'Male' : 'Female';
 //还可以加上插值表达式
 String str = gender == 0 ? 'Male=$gender' : 'Female=$gender';
 print(str);

//?? ,当??左边的值为null时,使用右边的值赋值,否则使用左边的值赋值。
String a;
//String a = 'Dart';
String b = "Java";
//a为null,所以会使用b的值返回
String c = a ?? b;
print(c);

5 面向对象

      面向对象是一种编程思想,用来描述一类事物的方式,包括事物的特征和行为方式。

5.1 类

  • 类的声明:
/**
  类的声明:
    
  class 类名 {

    类的主体

  }

 */
class Animal{

  //动物属性:动物的重量
  int weight;
  //动物属性:动物的年龄
  int age;

  //类的构造方法,用来创建这个类的对象
  //this关键字:代表当前的这个对象
  Animal(int weight,int age){
    this.weight=weight;
    this.age=age;
  }

  //类的成员方法,
  void eat(){
    print("动物的行为:吃吃吃");
  }

}

      在dart中,当类中没有声明构造函数时,会自动生成一个无参数的构造函数,当类中已经声明了构造函数,就不会生成。构造函数可以简写:

  Animal(this.weight,this.age);
  • 创建类的对象并使用
  //使用类的构造方法来创建一个对象
  final animal= new Animal(20, 2);

  //在dart中,关键字 new 可以省略
  final animal2= Animal(20, 2);
  animal2.eat();
  • 命名构造方法:在dart中,函数不能重载,为了满足不同参数构造对象的需求,需要定义命名构造函数。
  Animal.instance(int weight,int age){
    this.weight=weight;
    this.age=age;
  }

      调用

  //使用工厂构造方法来创建一个对象
  final animal= new Animal.instance(20, 2);
  animal.eat();

  //在dart中,关键字 new 可以省略
  final animal2= Animal.instance(20, 2);
  animal2.eat();
  • 常量构造函数:
    拥有常量构造函数的类中的所有的属性必须是使用final修饰的常量,常量构造函数中不能使用this关键字
main(){
  const p=Person("张三", 34);
  p.speek();
  print(p.name);
}
class Person{
  final String name;
  final int age;
  const Person(this.name, this.age);

  void speek()=> print("讲汉语");
}
  • 工厂构造函数:使用factory关键字修饰,里面只能使用静态的成员,按情况返回构建的对象。
main(){
  var p=Person("张三",34);
  p.speek();
  print("p.name=${p.name} p.age=${p.age}");
}

class Person{
  String name;
  int age;

  //对构建的Person做一个缓存
  static Map<String,Person>map=new Map();

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

  factory Person(String name, int age){
    if(name==null){
        return null;
    }
    Person p;

    //当缓存中已经存在这个名字对应的Person对象时,直接返回这个对象
    if(map.containsKey(name)){
      p=map[name];
    }else{
      p=new Person.name(name, age);
    }
    return p;
  }
  
  void speek()=> print("讲汉语");
}
  • 构造函数的初始化列表:
          a、初始化列表会在构造函数的方法体之前执行;
          b、初始化列表多用于设置final关键字修饰的字段。
          c、在构造函数的()后面以冒号开头,表达式之间以逗号隔开。
class Animal{

  //动物属性:动物的重量
  int weight;
  //动物属性:动物的年龄
  int age;

  //动物属性:动物的颜色
  final color;
  //动物属性:动物的食物
  final food;

  //类的构造方法,用来创建这个类的对象
  //this关键字:代表当前的这个对象
  //Animal(this.weight,this.age);
   //普通构造方法
  Animal(this.weight,this.age,this.food):color="red";

  //工厂构造方法
  Animal.instance(int weight,int age):color="red",food="草"{
    this.weight=weight;
    this.age=age;
  }

  //类的成员方法,
  void eat(){
    print("动物的行为:吃$food");
  }
}
  • getter、setter 方法:
          Dart的getter、setter方法与java中有所不同。
main(){
  var person = new Person("二哈");
  print(person.getName);
  person.setName="翠花";
  print(person.getName);
}

class Person{
  String name;
  Person(this.name);
  
  //getter 方法
  String get getName => name;

  //setter 方法
  set setName(String value) =>name = value;
}

5.2 类的继承

  • 使用extends关键字声明子类继承父类。
  • 子类必须在构造函数的初始化列表中使用super关键字调用父类的构造函数以初始化。
  • 子类可以使用父类的属性和方法,但是以下划线“_”开头的属性和方法只有在同一个Dart文件内才能使用。
main(){

  var person = new Man("二哈");
  print(person.name);
  
}

class Person{
  String name;

  Person(this.name);
}

//使用extends关键字使Man类继承Person类
class Man extends Person{
  final String food;
  
  //子类必须在构造函数的初始化列表中使用super关键字调用父类的构造函数以初始化。
  Man(String name):food="米饭",super(name);
  
}

5.3 抽象类

  • 使用abstract关键字修饰的类称为抽象类
  • 抽象类中的方法可以有方法体,也可以没有,没有方法体的方法称为抽象方法
  • 如果一个抽象类的子类不是抽象类,那么该子类必须实现父类的所有抽象方法
abstract class Person{

  String name;

  Person(this.name);

  void eat();
}

//使用extends关键字使Man类继承Person类
class Man extends Person{
  final String food;

  //子类必须在构造函数的初始化列表中使用super关键字调用父类的构造函数以初始化。
  Man(String name):food="米饭",super(name);

  @override
  void eat() {
     print("大口大口的吃$food");
  }
}

5.4、 接口

在Dart中没有专门的关键字用来定义接口,所有的类都可以当做接口来使用。

  • 使用implements 关键字来声明要实现的接口
  • 实现类必须实现或重写接口类中的所有的方法
  • 实现类可以同时实现多个接口
main(){

  var c = new C();
  c.testA();
  c.testB();
}

class A {

  void testA(){
    print("test-->A.a");
  }

}

class B{
  void testA(){
    print("test-->B.a");
  }

  void testB(){
    print("test-->B.b");
  }
}

class C implements A,B{

  @override
  void testA() {
    print("test-->C.a");
  }

  @override
  void testB() {
    print("test-->C.b");
  }

}

打印结果:

test-->C.a
test-->C.b

5.5 混入(mixins)

混入是java中没有新语法,具有以下特点:

  • 使用with关键字声明需要混入的类
  • 可以在当前类使用混入类的属性和方法,但是以下划线“_”开头的属性和方法只有在同一个Dart文件内才能使用。
  • 可以同时混入多个类,类之间使用逗号隔开
  • 如果被混入的类中有抽象方法,那么必须进行实现
  • 如果类或被混入的类中存在同名函数,后混入的类的优先级更高,当前的类优先级最高
main(){
  var c = new C();
  c.testA();
  c.testB();
  c.testC();
  c.test();
  print(c.name);
}

class A {
  final name="张三";
  void testA(){
    print("test-->A.a");
  }

  void testB(){
    print("test-->A.b");
  }

  void testC(){
    print("test-->A.c");
  }
}

abstract class B{
  void testA(){
    print("test-->B.a");
  }

  void testB(){
    print("test-->B.b");
  }

  void test();
}

//使用with声明要混入的类A,B
class C with A,B{
  void testA(){
    print("test-->C.a");
  }

  //实现混入类B中的抽象方法test()
  @override
  void test() {
    print("test-->test");
  }

}

打印结果:

test-->C.a
test-->B.b
test-->A.c
test-->test
张三

5.6 枚举(enum)

枚举的用途:限制参数的取值范围,以保证类型安全,(比如颜色、性别、星期等)

  • 使用enum关键字+枚举的名称进行申明
  • 多个枚举值之间用逗号隔开
  • 枚举值最好都用大写
main(){
  var person = new Person("张三", Sex.MAN);
  print(person);
  //使用Sex.values获取所有的枚举值
  print(Sex.values);
}

enum Sex{
  MAN,WOMEN
}

class Person{
  String name;
  Sex sex;

  Person(this.name, this.sex);

  @override
  String toString() {
    return "$name is $sex";
  }
}

5.7 external 外部扩展

external 关键字用来声明外部扩展的函数,使在类中只有函数的声明,具体的实现交给外部的另一个库去实现,多用于对不同的平台做不同的实现。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值