探索Java 8新特性:流式编程(Stream流)

目录

一、导言

二、什么是流式编程(Stream流)

三、Stream流的工作图

四、Stream流中常见的api

一、获取Stream流对象的方式

 二、Stream流中间操作

1.过滤(filter):对Stream流中的数据进行过滤

2.映射(map):对Stream流中的数据进行转换

3.映射(flatMap):对Stream流中的数据作扁平化处理

4.排序(sort):对Stream流中的数据做排序

5.去重(distinct):对Stream流中的数据做去重操作

6.截取(limit):用于截取流中的元素

 三、Stream流终止操作

 1.收集(collect):用于将流中的元素收集到一个集合或者其他容器中

2.规约(reduce):用于将流中的元素进行规约操作,它接收一个BinaryOperator参数,用于定义规约的逻辑

3.循环(forEach):用来遍历流中的数据

4.计数(count):统计流中元素个数

5.查找(find):获取满足条件的第一个元素

6.匹配(match):判断流中元素是否满足条件,返回值为Boolean类型

7.最值(max,min)

五、实际应用案例


一、导言

        简要介绍Java 8中引入的流式编程特性。强调流式编程在简化代码、提高性能和增强可读性方面的重要性。提及本文将深入探讨流式编程的定义、基本操作、中间操作、终端操作以及实际应用案例。

二、什么是流式编程(Stream流

        流式编程(Stream流)是一种在计算机编程中用于处理数据集合的新型方法。它在Java 8中被引入,并在之后的版本中得到了扩展和优化。流式编程的核心思想是以一种类似于流水线的方式来处理数据,通过一系列的操作来对数据进行筛选、转换、合并和归约,从而实现更简洁、更高效、更可读的代码编写。

三、Stream流的工作图

四、Stream流中常见的api

一、获取Stream流对象的方式

  1. 集合类的stream()方法:通过集合类如List、Set、Map等的stream()方法获取对应集合的流对象。
  2. Arrays类的stream()方法:通过Arrays类的静态方法stream()获取数组的流对象。
  3. Stream类的of()方法:通过Stream类的静态方法of()创建一个包含指定元素的流对象。
  4. Stream类的builder()方法:通过Stream类的builder()方法手动创建一个流对象,并通过add()方法添加元素。
// 通过集合类的stream()方法
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();

// 通过Arrays类的stream()方法
String[] array = {"a", "b", "c"};
Stream<String> stream = Arrays.stream(array);

// 通过Stream类的of()方法
Stream<String> stream = Stream.of("a", "b", "c");

// 通过Stream类的builder()方法
Stream.Builder<String> builder = Stream.builder();
builder.add("a").add("b").add("c");
Stream<String> stream = builder.build();

 二、Stream流中间操作

1.过滤(filter):对Stream流中的数据进行过滤

​
public static void main(String[] args) {

        // 构建数据源
        Person person1 = new Chinese("大白", 25, Sex.woman);
        Person person2 = new Chinese("小赵", 28, Sex.man);
        Person person3 = new Chinese("小明", 20, Sex.man);
        Person person4 = new Chinese("小兰", 17, Sex.woman);
        Person person5 = new Chinese("小赵", 18, Sex.man);
        Person person6 = new Chinese("小马", 50, Sex.man);

        List<Person> peopleList = Arrays.asList(person1, person2, person3, person4, person5, person6);
        // 需求:筛选出年龄大于25的学生信息
        peopleList.stream() // 获取流对象
                .filter(person -> person.getAge() > 25)// 筛选出年龄大于25的学生信息
                .forEach(System.out::println);         // 打印输出满足条件的对象信息
    }

​

2.映射(map):对Stream流中的数据进行转换

public class StreamMap {

    public static void main(String[] args) {
        // 构建数据源
        Person person1 = new Chinese("大白", 25, Sex.woman);
        Person person2 = new Chinese("小赵", 28, Sex.man);
        Person person3 = new Chinese("小明", 20, Sex.man);
        Person person4 = new Chinese("小兰", 17, Sex.woman);
        Person person5 = new Chinese("小赵", 18, Sex.man);
        Person person6 = new Chinese("小马", 50, Sex.man);

        List<Person> peopleList = Arrays.asList(person1, person2, person3, person4, person5, person6);

        // 需求:获取所有对象的姓名
        peopleList.stream() // 获取流对象
                .map(person -> person.getName()) // 将Person类型 --> String类型
                .forEach(System.out::println);   // 打印输出所有对象姓名
    }
}

3.映射(flatMap):对Stream流中的数据作扁平化处理

// Grades类的定义如下:
public class Grades {

    // 班级名称
    public String className;

    public List<Person> personList;
}

public class FlatMapTest {

    public static void main(String[] args) {

        // 构建数据源
        List<Grades> classList = new ArrayList<>();

        Person person = new Chinese("小明", 25, Sex.man);
        Person person1 = new Chinese("小红", 23, Sex.woman);
        Person person2 = new Chinese("小白", 22, Sex.man);
        Person person3 = new Chinese("小黑", 20, Sex.man);
        Person person4 = new Chinese("小兰", 29, Sex.woman);
        Person person5 = new Chinese("小李", 18, Sex.man);
        Person person6 = new Chinese("小黄", 50, Sex.man);
        Person person7 = new Chinese("小青", 29, Sex.woman);
        Person person8 = new Chinese("小倩", 18, Sex.man);
        Person person9 = new Chinese("小灰", 50, Sex.man);

        // 班级1的学生信息
        Grades class1 = new Grades("class1", Arrays.asList(person, person1, person2, person3));
        // 班级2的学生信息
        Grades class2 = new Grades("class2", Arrays.asList(person4, person5, person6));
        // 班级3的学生信息
        Grades class3 = new Grades("class3", Arrays.asList(person7, person8, person9));
        classList.add(class1);
        classList.add(class2);
        classList.add(class3);

        // 需求:获取所有班级的学生信息,并打印输出
        classList.stream() // 获取流对象
                .flatMap(x->x.getPersonList().stream()) // 对Grades对象中的personList做扁平化处理
                .forEach(System.out::println); // 打印展示所有班级学生信息
    }
}

4.排序(sort):对Stream流中的数据做排序

public class StreamSortBy {

    public static void main(String[] args) {
        // 构建数据源
        Person person1 = new Chinese("大白", 25, Sex.woman);
        Person person2 = new Chinese("小赵", 28, Sex.man);
        Person person3 = new Chinese("小明", 20, Sex.man);
        Person person4 = new Chinese("小兰", 17, Sex.woman);
        Person person5 = new Chinese("小赵", 18, Sex.man);
        Person person6 = new Chinese("小马", 50, Sex.man);

        List<Person> peopleList = Arrays.asList(person1, person2, person3, person4, person5, person6);
        // 需求:按照年龄升序排序
        peopleList.stream() // 获取流对象
                .sorted(Comparator.comparing(Person::getAge)) //传入一个比较器,按照年龄升序排列
                .forEach(System.out::println);  // 打印排序后的所有学生信息

    }
}

5.去重(distinct):对Stream流中的数据做去重操作

public class StreamDistinct {

    public static void main(String[] args) {
        // 需求:去除重复元素
        Stream.of("张三", "李四", "王五", "张三", "赵六", "李四")   // 获取流对象
                .distinct() // 对重复元素进行去重操作
                .forEach(System.out::println); // 打印去重后的元素
    }
}

6.截取(limit):用于截取流中的元素

public class StreamLimit {

    public static void main(String[] args) {
        // 构建数据源
        Person person1 = new Chinese("大白", 25, Sex.woman);
        Person person2 = new Chinese("小赵", 28, Sex.man);
        Person person3 = new Chinese("小明", 20, Sex.man);
        Person person4 = new Chinese("小兰", 17, Sex.woman);
        Person person5 = new Chinese("小赵", 18, Sex.man);
        Person person6 = new Chinese("小马", 50, Sex.man);

        List<Person> peopleList = Arrays.asList(person1, person2, person3, person4, person5, person6);
        // 需求:获取年龄最小的学生信息
        peopleList.stream()
                .sorted(Comparator.comparingInt(Person::getAge)) // 按照年龄升序排列
                .limit(1)                                        // 获取排序后的第一个元素
                .forEach(System.out::println);                   // 打印学生信息
    }
}

 三、Stream流终止操作

 1.收集(collect):用于将流中的元素收集到一个集合或者其他容器中

@Slf4j
public class StreamCollect {

    public Person[] peoples;

    public List<Person> personList;

    // 构建数据源
    @Before
    public void before() {
        Person person = new Chinese("小明", 21, Sex.man);
        Person person1 = new Chinese("大白", 25, Sex.woman);
        Person person2 = new Chinese("小赵", 23, Sex.man);
        Person person3 = new Chinese("小兰", 20, Sex.man);
        Person person4 = new Chinese("小红", 18, Sex.woman);
        Person person5 = new Chinese("小李", 17, Sex.man);
        Person person6 = new Chinese("小麻", 50, Sex.man);
        this.peoples =new Person[]{ person, person1, person2, person3, person4, person5, person6};
        this.personList = Arrays.asList(peoples);
    }

    // 收集成List
    @Test
    public void collectToListTest() {
        // 需求:按照年龄升序排列,并收集为集合
        List<Person> persons = this.personList.stream() // 获取流对象
                .sorted(Comparator.comparingInt(Person::getAge)) // 按照年龄进行排序
                .collect(Collectors.toList());  //收集成集合
    }

    // 收集为map
    @Test
    public void groupBySexTest() {
        // 需求: 按照性别信息分组展示学生信息
        this.personList.stream() // 获取一个流对象
                .collect(Collectors.groupingBy(Person::getSex)) // 按照性别进行分组操作
                .forEach((key, value) -> {                      // 按性别分组打印学生信息
                    value.forEach(System.out::println);
                    System.out.println("================");
                });
    }


    // 收集为map
    @Test
    public void groupByAndAveragingIntTest() {
        // 需求:请计算全班男生的平均年龄和全班女生的平均年龄
        this.personList.stream()    // 获取流对象
                .distinct()         // 去重操作
                .collect(Collectors.groupingBy(Person::getSex, Collectors.averagingInt(Person::getAge))) //按性别分组,并求年龄平均值
                .forEach((sex, aLong) -> log.info("性别:{}, 年龄总和:{}", sex, aLong));   //打印信息

    }
}

2.规约(reduce):用于将流中的元素进行规约操作,它接收一个BinaryOperator参数,用于定义规约的逻辑

public class StreamReduce {

    /**
     * reduce()方法有两种重载形式:
     *
     * 1.T reduce(T identity, BinaryOperator<T> accumulator):指定一个初始值identity,通过accumulator定义的规约逻辑,将流中的元素进行累积计算,返回规约结果。
     * 2.Optional<T> reduce(BinaryOperator<T> accumulator):根据accumulator定义的规约逻辑,将流中的元素进行递归计算,返回Optional类型的规约结果。
     */
    
    // 方式一
    @Test
    public void ReduceTest01() {
        // 需求:求解集合中所有元素的和
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        int sum = numbers.stream()
                .reduce(0, (a, b) -> a + b);
        // sum = 15
    }
    
    // 方式二
    @Test
    public void ReduceTest02() {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        Optional<Integer> max = numbers.stream()
                .reduce(Integer::max);
        // max = Optional[5]
    }
}

3.循环(forEach):用来遍历流中的数据

public class Stream_ForEach {
    public static void main(String[] args) {
    //获取一个Stream流
    Stream<String>stream= Stream.of("张三","李四","王五","赵六");
        //使用Stream流的方法forEach对stream流中的数据遍历
        stream.forEach(System.out::println);
    }
}

4.计数(count):统计流中元素个数

public class StreamCount {
    public static void main(String[] args) {
        
        long count = Stream.of("张三", "李四", "赵六", "王五", "小七").count();
        // count --> 5
    }
}

5.查找(find):获取满足条件的第一个元素

public class StreamFind {

    public Person[] peoples;

    public List<Person> personList;

    // 构建数据源
    @Before
    public void before() {
        Person person = new Chinese("小明", 21, Sex.man);
        Person person1 = new Chinese("大白", 25, Sex.woman);
        Person person2 = new Chinese("小李", 23, Sex.man);
        Person person3 = new Chinese("小兰", 20, Sex.man);
        Person person4 = new Chinese("小红", 18, Sex.woman);
        Person person5 = new Chinese("小李", 17, Sex.man);
        Person person6 = new Chinese("小麻", 50, Sex.man);
        this.peoples =new Person[]{ person, person1, person2, person3, person4, person5, person6};
        this.personList = Arrays.asList(peoples);
    }

    @Test
    public void findTest01() {
        // 需求:查找姓名为小明的学生信息,找不到则抛出异常信息
        Person result = this.personList.stream()
                .filter(person -> "小明".equals(person.getName()))
                .findFirst()
                .orElseThrow(RuntimeException::new);
        System.out.println(result);
    }

}

6.匹配(match):判断流中元素是否满足条件,返回值为Boolean类型

@Slf4j
public class AnyMatchStreamTest {
    public Person[] peoples;

    public List<Person> personList;

    @Before
    public void before() {
        Person person = new Chinese("小明", 22, Sex.man);
        Person person1 = new Chinese("小红", 24, Sex.woman);
        Person person2 = new Chinese("小李", 21, Sex.man);
        Person person3 = new Chinese("小明", 21, Sex.man);
        Person person4 = new Chinese("小红", 19, Sex.woman);
        Person person5 = new Chinese("小李", 18, Sex.man);
        Person person6 = new Chinese("小麻", 50, Sex.man);

        this.peoples =new Person[]{ person, person1, person2, person3, person4, person5, person6};
        this.personList = Arrays.asList(peoples);
    }

    @Test
    public void allMatchTest() {
        // 需求:判断学生年龄是否全部大于20岁
        if (this.personList.stream().allMatch(person -> person.getAge() > 20)) { //流中所有元素满足条件即为true
            System.out.println("学生年龄全部大于20岁");
        }
    }

    @Test
    public void anyMatchTest() {
        // 需求:判断所有学生中是否有姓名为"小麻"的学生
        if (this.personList.stream().anyMatch(person -> "小麻".equals(person.getName()))) { // 流中任意一个元素满足条件即为true
            System.out.println("学生中有姓名为小麻的学生");
        }
    }

    @Test
    public void noneMatchTest() {
        // 需求:判断所有学生中是否有姓名为"小麻"的学生
        if (this.personList.stream().noneMatch(person -> "小麻".equals(person.getName()))) { //流中所有元素都不满足条件即为true
            System.out.println("学生中没有姓名为小麻的学生");
        }
    }
}

7.最值(max,min)

public class StreamMaxAndMin {
    public static void main(String[] args) {
        List<Test> list = new ArrayList<>();
        list.add(new Test("小赵",23,new BigDecimal("3000"),new BigDecimal("1.1")));
        list.add(new Test("赵六",24,new BigDecimal("2800"),new BigDecimal("1.2")));
        list.add(new Test("小兰",22,new BigDecimal("3200"),new BigDecimal("1.3")));
        
        //获取年龄最大的人
        Test maxPeople = list.stream().max(Comparator.companing(Test::getAge)).get();
      
        //获取年龄最小的人
        Test minPeople = list.stream().min(Comparator.companing(Test::getAge)).get();
 
    }
}

五、实际应用案例

1.sort用法示例

@Test
    public void sortByAgeTest() {
        // 获取年龄最大的学生信息,没找到这返回"小麻"
        String name = this.personList.stream()
                .max(Comparator.comparing(Person::getAge))
                .orElseGet(() -> new Chinese("小麻", 23, Sex.man)).getName();
        System.out.println(name);
    }

2.flatMap用法示例

@Slf4j
public class FlatMapTest {

    public static void main(String[] args) {

        // 构建数据源
        List<Grades> classList = new ArrayList<>();

        Person person = new Chinese("小明", 25, Sex.man);
        Person person1 = new Chinese("小红", 23, Sex.woman);
        Person person2 = new Chinese("小白", 22, Sex.man);
        Person person3 = new Chinese("小黑", 20, Sex.man);
        Person person4 = new Chinese("小兰", 29, Sex.woman);
        Person person5 = new Chinese("小李", 18, Sex.man);
        Person person6 = new Chinese("小黄", 50, Sex.man);
        Person person7 = new Chinese("小青", 29, Sex.woman);
        Person person8 = new Chinese("小倩", 18, Sex.man);
        Person person9 = new Chinese("小灰", 50, Sex.man);

        // 班级1的学生信息
        Grades class1 = new Grades("class1", Arrays.asList(person, person1, person2, person3));
        // 班级2的学生信息
        Grades class2 = new Grades("class2", Arrays.asList(person4, person5, person6));
        // 班级3的学生信息
        Grades class3 = new Grades("class3", Arrays.asList(person7, person8, person9));
        classList.add(class1);
        classList.add(class2);
        classList.add(class3);

        // 需求:获取所有班级的学生信息,并打印输出
        classList.stream() // 获取流对象
                .flatMap(x->x.getPersonList().stream()) // 对Grades对象中的personList做扁平化处理
                .forEach(System.out::println); // 打印展示所有班级学生信息
    }
}

3.groupBy的用法示例

@Slf4j
public class GroupByStreamTest {
    public Person[] peoples;

    public List<Person> personList;

    public List<ActualByBudgetSubjectOrganization> actualByBudgetSubjectOrganizationList = new ArrayList<>();

    @Before
    public void before() {
        Person person = new Chinese("小明", 21, Sex.man);
        Person person1 = new Chinese("小红", 25, Sex.woman);
        Person person2 = new Chinese("小李", 23, Sex.man);
        Person person3 = new Chinese("小明", 20, Sex.man);
        Person person4 = new Chinese("小红", 18, Sex.woman);
        Person person5 = new Chinese("小李", 17, Sex.man);
        Person person6 = new Chinese("小麻", 50, Sex.man);

        ActualByBudgetSubjectOrganization actualByBudgetSubjectOrganization = null;
        for (int i =0; i<10; i++){
            // 生成0-100之间的随机数
            Random random = new Random();
            long amountMoney = random.nextLong() % 101; // 生成-100到100之间的随机数
            if (amountMoney < 0) {
                amountMoney = -amountMoney; // 将负数转化为正数
            }
            if(i<5){
                actualByBudgetSubjectOrganization=ActualByBudgetSubjectOrganization.builder()
                        .companyCode("10")
                        .operationNo("001")
                        .yearMonth("202210")
                        .budgetSubjectCode("PT101")
                        .amountMoney(amountMoney).build();
                this.actualByBudgetSubjectOrganizationList.add(actualByBudgetSubjectOrganization);
                continue;
            }
            actualByBudgetSubjectOrganization = ActualByBudgetSubjectOrganization.builder()
                    .companyCode("G0")
                    .operationNo("001")
                    .yearMonth("202210")
                    .budgetSubjectCode("PT102")
                    .amountMoney(amountMoney).build();
            this.actualByBudgetSubjectOrganizationList.add(actualByBudgetSubjectOrganization);
        }
        this.peoples =new Person[]{ person, person1, person2, person3, person4, person5, person6};
        this.personList = Arrays.asList(peoples);
    }

    // stream流的分组转换
    @Test
    public void groupByAgeTest() {
        Map<Integer, List<Person>> result = this.personList.stream().collect(Collectors.groupingBy(Person::getAge));
        Set<Integer> keys = result.keySet();
        for (Integer key : keys) {
            result.get(key).stream().forEach(System.out::println);
            System.out.println("================");
        }
    }

    @Test
    public void groupBySexTest() {
        Map<Sex, List<Person>> result = this.personList.stream().collect(Collectors.groupingBy(Person::getSex));
        Set<Sex> keys = result.keySet();
        for (Sex key : keys) {
            result.get(key).stream().forEach(System.out::println);
            System.out.println("================");
        }
    }


    // stream流的分组转换
    @Test
    public void groupByNameTest() {
        this.personList.stream()
                .collect(Collectors.groupingBy(Person::getName))
                .forEach((key, value) -> {
                    value.forEach(System.out::println);
                    System.out.println("================");
                });
    }

    // 先分组在求和
    @Test
    public void groupByAndSummingLongTest() {
        // 先分组在求和,注意Collectors.summingLong(ActualByBudgetSubjectOrganization::getAmountMoney是作为Collectors.groupingBy的一个参数
        Map<String, Long> collect = this.actualByBudgetSubjectOrganizationList.stream()
                .collect(Collectors.groupingBy(ActualByBudgetSubjectOrganization::getBudgetSubjectCode,
                        Collectors.summingLong(ActualByBudgetSubjectOrganization::getAmountMoney)));
        Set<String> keys = collect.keySet();
        keys.forEach(key-> log.info("key:{},value:{}",key, collect.get(key)));
    }

    // 请计算全班男生的平均年龄和全班女生的平均年龄。
    @Test
    public void groupByAndAveragingIntTest() {

        Map<Sex, Double> collect1 = this.personList.stream().distinct().collect(Collectors.groupingBy(Person::getSex, Collectors.averagingInt(Person::getAge)));
        collect1.forEach((sex, aLong) -> log.info("性别:{}, 年龄总和:{}", sex, aLong));
        boolean b = this.personList.stream().distinct().anyMatch(x -> x.getAge() > 9);
//        boolean b = this.personList.stream().distinct().max();
    }
}

4.peek用法示例


@Slf4j
public class PeekStreamTest {

    public Person[] peoples;

    public List<Person> personList;

    @Before
    public void before() {
        Person person = new Chinese("小明", 23, Sex.man);
        Person person1 = new Chinese("小红", 15, Sex.woman);
        Person person2 = new Chinese("小李", 58, Sex.man);
        Person person3 = new Chinese("小明", 62, Sex.man);
        Person person4 = new Chinese("小红", 11, Sex.woman);
        Person person5 = new Chinese("小李", 38, Sex.man);
        Person person6 = new Chinese("小麻", 20, Sex.man);

        this.peoples =new Person[]{ person, person1, person2, person3, person4, person5, person6};
        this.personList = Arrays.asList(peoples);
    }

    @Test
    public void peekTest01() {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

        numbers.stream()
                .peek(number -> System.out.println("Processing number: " + number)) // 打印流的执行过程
                .filter(number -> number % 2 == 0)
                .forEach(System.out::println);
    }

    // 统一将Person对象中的某一属性修改成固定值
    @Test
    public void peekTest02() {
        //  统一将Person对象中的性别属性修改成男, 注意this.personList.stream().peek(x -> x.setSex(Sex.man))仍会返回一个stream流
        this.personList.stream().peek(x -> x.setSex(Sex.man)).forEach(System.out::println);
    }
}

3.first用法示例

  @Test
    public void firstTest() {
        // 获取年龄大于50岁,并取其第一个元素
        String firstElementValue = this.personList.stream()
                .filter(x -> x.getAge() > 50)
                .findFirst().map(Person::getName).orElseGet(() -> "");
        log.info("firstElementValue--->{}", firstElementValue);
    }

3.removeIf用法示例

public class RemoveIfStreamTest {
    public Person[] peoples;

    public List<Person> personList;

    @Before
    public void before() {
        Person person = new Chinese("小兰", 22, Sex.man);
        Person person1 = new Chinese("小赵", 24, Sex.woman);
        Person person2 = new Chinese("小白", 21, Sex.man);
        Person person3 = new Chinese("小黄", 21, Sex.man);
        Person person4 = new Chinese("小黑", 19, Sex.woman);
        Person person5 = new Chinese("小李", 18, Sex.man);
        Person person6 = new Chinese("小麻", 50, Sex.man);

        this.peoples =new Person[]{ person, person1, person2, person3, person4, person5, person6};
        this.personList = Arrays.asList(peoples);
    }

    @Test
    public void removeIfTest() {
        List<Person> personList = new ArrayList<>(this.personList);
        personList.add(null);
        personList.add(null);
        personList.add(null);
        personList.add(null);
        personList.removeAll(Collections.singletonList(null));
        //personList.removeIf(Objects::isNull);
        personList.removeIf(x -> x.getAge() > 30);
        personList.forEach(System.out::println);
    }
}

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值