Java 学习笔记(二十二)

学习内容来自尚硅谷Java入门视频教程(在线答疑+Java面试真题)

简介

  • 速度更快
  • 代码更少(lambda 表达式)
  • 强大的 stream API
  • 便于并行
  • 最大化减少空指针异常
  • Nashorm引擎,运行 JVM 运行 JS 应用

Lambda 表达式

Lambda 是一个匿名函数,可以理解为一段可传递的代码(将代码像数据一样传递)
使用 Lambda 表达式可以写出更加简洁灵活的代码

首先看两个例子:

@Test
    public void test1() {
        // 传统方式,创建匿名内部类实现一个线程对象
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("遇到困难睡大觉");
            }
        };
        runnable.run();

        System.out.println("====================");
        // lambda 方式
        Runnable r2 = () -> System.out.println("遇到困难就干饭");
        r2.run();
    }

    @Test
    public void test2() {
        List<Integer> list = new ArrayList<>();
        list.add(5);
        list.add(554);
        list.add(465);
        list.add(9);
        list.add(6);
        // 传统方法,重写排序的 compare 方法
        Collections.sort(list, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;
            }
        });
        for (Integer integer : list) {
            System.out.println(integer);
        }
        System.out.println("==================");
        // lambda 实现自定义逆序排序
        Collections.sort(list, (a, b) -> b - a);

        for (Integer integer : list) {
            System.out.println(integer);
        }
    }

在上述例子中

  • -> 就是 lambda 操作符,也叫做箭头操作符
  • -> 左边是 lambda 形参列表,其实就是接口中抽象方法的形参列表
  • -> 右边是 lambda 体,就是就是重写的抽象方法的方法体

lambda 的本质还是接口的匿名内部类的实例对象

lambda 的语法大致分为六类:

  1. 要实现的抽象方法无参,无返回值(void)
public void test1() {
        // 传统方式,创建匿名内部类实现一个线程对象
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("遇到困难睡大觉");
            }
        };
        runnable.run();

        System.out.println("====================");
        // lambda 方式
        Runnable r2 = () -> {
        	System.out.println("遇到困难就干饭");
        };
        r2.run();
    }
  1. 要实现的抽象方法需要一个参数,无返回值(void)
public void test3() {
        // 传统方式
        LambdaInterface lambdaInterface = new LambdaInterface() {
            @Override
            public void eat(String food) {
                System.out.println("遇到困难吃" + food);
            }
        };
        lambdaInterface.eat("牛肉烩面");
        System.out.println("================");
        // lambda 方式
        LambdaInterface lambdaInterface1 = (String a) -> {
            System.out.println("遇到困难也不吃" + a);
        };
        lambdaInterface1.eat("牛肉烩馍");
    }
  1. 要实现的抽象方法需要一个参数(省略参数类型,可有编译器自行推断出,称为”类型维护“),无返回值(void)
public void test3() {
        // 传统方式
        LambdaInterface lambdaInterface = new LambdaInterface() {
            @Override
            public void eat(String food) {
                System.out.println("遇到困难吃" + food);
            }
        };
        lambdaInterface.eat("牛肉烩面");
        System.out.println("================");
        // lambda 方式
        LambdaInterface lambdaInterface1 = (a) -> {
            System.out.println("遇到困难也不吃" + a);
        };
        lambdaInterface1.eat("牛肉烩馍");
    }
  1. 要实现的抽象方法需要一个参数,无返回值(void),省略参数类型和参数的小括号
public void test3() {
        // 传统方式
        LambdaInterface lambdaInterface = new LambdaInterface() {
            @Override
            public void eat(String food) {
                System.out.println("遇到困难吃" + food);
            }
        };
        lambdaInterface.eat("牛肉烩面");
        System.out.println("================");
        // lambda 方式
        LambdaInterface lambdaInterface1 = a -> {
            System.out.println("遇到困难也不吃" + a);
        };
        lambdaInterface1.eat("牛肉烩馍");
    }
  1. 要实现的抽象方法需要多个参数,多条执行语句,并且可以有返回值
public void test4() {
        Comparator<Integer> comparator = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1.compareTo(o2);
            }
        };
        System.out.println(comparator.compare(45, 21));
        System.out.println("==============");
        Comparator<Integer> comparator1 = (a, b) -> {
            System.out.println(a);
            System.out.println(b);
            return a.compareTo(b);
        };
        System.out.println(comparator1.compare(45, 21));
    }
  1. 要实现的抽象方法参数不限,只有一条执行语句,并且可以有返回值。可以省略大括号和 return
public void test5() {
        // 省略前
        Comparator<Integer> comparator1 = (a, b) -> {
            return a.compareTo(b);
        };
        System.out.println(comparator1.compare(45, 21));
        System.out.println("==============");
        // 省略后
        Comparator<Integer> comparator2 = (a, b) -> a.compareTo(b);
        System.out.println(comparator1.compare(45, 21));
    }

总结:

  • lambda 形参列表的类型可以省略
  • lambda 形参数量为 1 时,可以省略小括号
  • lambda 执行语句只有一条时,可以省略大括号和 return
  • lambda 用来实现接口的匿名内部类的实例对象,且接口中只能由一个抽象方法,这种接口也成为函数式接口

函数式(Functional)接口

若一个接口只声明了一个抽象方法,则该接口为函数式接口

在上面说到了函数式接口,在 Java 中,函数式接口一般会加上注解 @FunctionalInterface,如
在这里插入图片描述
加上注解后,编译器就会检验接口是否为函数式接口,并且 javadoc 也会包含声明,说明该接口为函数式接口

在这里插入图片描述

Java 内置四大核心函数式接口

在这里插入图片描述
小测试:

public class LambdaTest2 {
    @Test
    public void testConsumer() {
        happyTime(500, new Consumer<Double>() {
            @Override
            public void accept(Double aDouble) {
                System.out.println("给" + aDouble + "干饭去");
            }
        });

        System.out.println("---------------------");

        happyTime(500, d -> System.out.println("给" + d + "上网去"));
    }

    public void happyTime(double money, Consumer<Double> consumer) {
        consumer.accept(money);
    }

    @Test
    public void testPredicate() {
        List<String> list = new ArrayList<>();
        list.add("背景");
        list.add("北京");
        list.add("南京");
        list.add("维京");
        list.add("西京");
        List<String> list1 = filterString(list, new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.contains("京");
            }
        });
        System.out.println(list1);
        System.out.println("------------------------");
        List<String> list2 = filterString(list, s -> s.contains("京"));
        System.out.println("list2 = " + list2);
    }

    public List<String> filterString(List<String> list, Predicate<String> predicate) {
        List<String> filterList = new ArrayList<>();

        for (String s : list) {
            if (predicate.test(s)) {
                filterList.add(s);
            }
        }

        return filterList;
    }
}

其他接口

[外链图片转存失败,源站可能有防盗在这里插入!链机制,建描述]议将图片上https://传(imblog.csdnimg.cecZJ2Nf7642c02c84fe7aa52a7482222899e.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiJ5pu06ay8,size_20,color_FFFFFF,t_70,g_se,x_16)https://img-blog.csdnim g.cn/2cf7642c02c84fe7aa52a7482222899e.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiJ5pu06ay8,size_20,color_FFFFFF,t_70,g_se,x_16)]

方法引用与构造器引用

方法引用

当要传递给 Lambda 体的操作已经有实现的方法,可以直接使用方法引用

方法引用可以看作是 Lambda 表达式深层次的表达。换句话说,方法引用就是 Lambda 表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法

使用方式

类(对象) :: 方法名

具体分为三种情况:

  •  对象::非静态方法
    
  •  类::静态方法
    

上面两种的要求:要求 接口中的抽象方法的形参列表和返回值类型方法引用的方法的形参列表和返回值类型 相同(主要针对前两种情况)

  •  类::非静态方法
    

举个栗子:

public class MethodReferenceTest {

    // 1. 对象::非静态方法
    @Test
    public void test1() {
        Consumer<String> con1 = str -> System.out.println(str);
        con1.accept("杰瑞狗");

        System.out.println("----------------------");

        PrintStream ps = System.out;
        Consumer<String> con2 = ps::println;
        con2.accept("jerryDog");
    }

    // 1. 对象::非静态方法
    @Test
    public void test2() {
        Employee emp = new Employee(1001, "jerry", 23, 5000);
        Supplier<String> sup1 = () -> emp.getName();
        System.out.println(sup1.get());

        System.out.println("---------------------");

        Supplier<String> sup2 = emp::getName;
        System.out.println(sup2.get());
    }

    // 2. 类::静态方法
    @Test
    public void test3() {
        Comparator<Integer> com1 = (a, b) -> Integer.compare(a, b);
        System.out.println(com1.compare(45, 12));

        System.out.println("------------------------");

        Comparator<Integer> com2 = Integer::compare;
        System.out.println(com2.compare(45, 12));
    }

    // 2. 类::静态方法
    @Test
    public void test4() {
        Function<Double, Long> func1 = d -> Math.round(d);
        System.out.println(func1.apply(5.65478));

        System.out.println("---------------------");

        Function<Double, Long> func2 = Math::round;
        System.out.println(func2.apply(6.89798));
    }

    // 3. 类::非静态方法
    @Test
    public void test5() {
        Comparator<String> com1 = (s1, s2) -> s1.compareTo(s2);
        System.out.println(com1.compare("abc", "dsw"));

        System.out.println("-----------------------------");

        // compareTo 有两个参数
        // 这里默认是第一个参数调用该类的方法,而传进方法的形参为第二个参数
        // :: 左侧为第一个参数的类,右边为需要调用的方法
        Comparator<String> com2 = String::compareTo;
        System.out.println(com2.compare("abc", "dsw"));
    }

    // 3. 类::非静态方法
    @Test
    public void test6() {
        BiPredicate<String, String> pre1 = (s1, s2) -> s1.equals(s2);
        System.out.println(pre1.test("abc", "abc"));

        System.out.println("-------------------------");

        BiPredicate<String, String> pre2 = String::equals;
        System.out.println(pre2.test("abc", "sss"));
    }

    // 3. 类::非静态方法
    @Test
    public void test7() {
        Employee emp = new Employee(1001, "jerry", 45, 7984);
        Function<Employee, String> func1 = e -> e.getName();
        System.out.println(func1.apply(emp));

        System.out.println("---------------------------");

        Function<Employee, String> func2 = Employee::getName;
        System.out.println(func2.apply(emp));
    }
}

构造器引用

与方法引用类似,不过调用的不是方法,还是构造器,返回的结果即为对应类的对象

如:

// 构造器引用
    @Test
    public void test1() {
        Supplier<Employee> sup1 = () -> new Employee();
        System.out.println(sup1.get());

        System.out.println("-------------------------");

        Supplier<Employee> sup2 = Employee::new;
        System.out.println(sup2);
    }

对应的构造器为:

public Employee() {
        System.out.println("调用空参构造器");
    }

输出结果为:

调用空参构造器
course.javaBase.java8.Employee@12f41634
-------------------------
调用空参构造器
course.javaBase.java8.Employee@262b2c86

可见,通过构造器引用直接生成了对象

再看个例子:

// 构造器引用
    @Test
    public void test2() {
        Function<Integer, Employee> func1 = id -> new Employee(id);
        System.out.println(func1.apply(1001));

        System.out.println("-----------------------------");

        Function<Integer, Employee> func2 = Employee::new;
        System.out.println(func2.apply(1002));
    }

对应的构造器

public Employee(int id) {
        this.id = id;
        System.out.println("构造器调用..");
    }

输出结果:

构造器调用..
course.javaBase.java8.Employee@12f41634
-----------------------------
构造器调用..
course.javaBase.java8.Employee@371a67ec

数组引用

其实就是构造器引用,不过对应的返回类型是数组

如:

// 数组引用
    @Test
    public void test3() {
        Function<Integer, String[]> func1 = len -> new String[len];
        System.out.println(func1.apply(6).length);

        System.out.println("-----------------------");

        Function<Integer, String[]> func2 = String[]::new;
        System.out.println(func2.apply(8).length);
    }

输出结果:

6
-----------------------
8

强大的 Stream API

Stream API (java.util.stream)把真正的函数式编程风格引入 Java,这是目前对 Java 类库最好的补充

Stream API 提供了高效且易于使用的处理数据的方式

Stream

Stream 是用来对数据源进行处理的

Stream 是延迟执行的,具体地讲,分为三个步骤

  1. 创建 Stream 实例,根据数据源获取一个流
  2. 中间操作,即准备好对数据源的操作(可能是一系列操作),但不执行
  3. 终止操作,真正执行刚刚准备好的操作,产生结果,并关闭 Stream

如下图
在这里插入图片描述

实例化方式

有四种方式可以实例化 Stream

  • 通过集合
  • 通过数组
  • 通过 Stream 的 of()
  • 创建无限流
通过集合

可以通过调用 Collection 类的 default 类型方法来返回流

  • default Stream<T> stream()
  • default Stream<T> parallelStream()
public void test1() {
        // 获取一个 List 对象
        List<Employee> employees = EmployeeData.getEmployees();

        // 通过调用 Collection 的 default 类型方法 stream(),返回一个顺序流
        // default Stream<T> stream()
        Stream<Employee> stream = employees.stream();

        // 通过调用 Collection 的 default 类型方法 parallelStream(),返回一个并行流
        // default Stream<T> parallelStream()
        Stream<Employee> parallelStream = employees.parallelStream();
    }
通过数组

通过调用 Arrays 类的静态方法获取流

  • static <T> Stream<T> stream(T[] array)
public void test2() {
        int[] arr = {1, 2, 3, 45, 789};

        // static <T> Stream<T> stream(T[] array)
        // 因为传进去的数组是 int 类型,所以返回的 stream 为 IntStream 类的对象
        IntStream stream = Arrays.stream(arr);

        Employee emp1 = new Employee(1001);
        Employee emp2 = new Employee(1002);
        Employee[] empArr = new Employee[]{emp1, emp2};
        // 通过调用 Arrays 类的静态方法获取流
        Stream<Employee> stream1 = Arrays.stream(empArr);
    }
通过 Stream 的 of()

通过调用 Stream 类的方法 of() 获取

  • public static<T> Stream<T> of(T... values)
public void test3() {
        // 通过调用 Stream 类的方法 of() 获取,of() 为数据
        // public static<T> Stream<T> of(T... values)
        Stream<Integer> integerStream = Stream.of(1, 2, 52, 22);
    }
创建无限流

根据生成的数据获取流

创建无限流有两种方式

  • Stream 类中的迭代方法 iterate()
public void test4() {
        // Stream 类中的迭代方法
        // public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
        /*
        其中 final T seed 可以理解为循环开始的初始值
        final UnaryOperator<T> f 是继承了函数型接口的函数式接口
         */


        // 遍历从 2 开始的 10 个偶数
        Stream.iterate(2, t -> t + 2).limit(10).forEach(System.out::println);
        /*
        其中 final T seed 为 2
        final UnaryOperator<T> f 为 lambda 表达式
        limit(10) 为循环结束条件,表示生成十个数据就终止
        forEach(System.out::println) 是终止操作,参数为每次循环执行的方法,其中 System.out::println 是方法引用
         */
    }
  • Stream 类中的生成方法 generate()
public void test5() {
        // Stream 类中的生成方法
        // public static<T> Stream<T> generate(Supplier<T> s)
        /*
            Supplier<T> s 是供给型接口,用来生成数据
         */
        Stream.generate(Math::random).limit(10).forEach(System.out::println);
        /*
            Math::random 是方法引用,引用 Math 类的 random() 方法生成随机数
            limit(10) 为循环结束条件,表示生成十个数据就终止
            forEach(System.out::println) 是终止操作,参数为每次循环执行的方法,其中 System.out::println 是方法引用
         */
    }

中间操作

中间操作可以是一个操作链,在 Stream 终止操作之前不会真正执行

筛选与切片操作

在这里插入图片描述
例子:

// 筛选与切片功能演示
    @Test
    public void test1() {
        List<Employee> list = EmployeeData.getEmployees();
        // Stream<T> filter(Predicate<? super T> predicate);
        // filter 方法会对 stream 中的数据进行过滤,保留断定型接口方法判断为真的数据
        // 查询员工表中年龄大于 100 的员工
        list.stream().filter(e -> e.getAge() > 100).limit(2).forEach(System.out::println);
        System.out.println("-----------------------");

        // limit(n) 截断流,取流的前 n 个数据
        list.stream().limit(3).forEach(System.out::println);
        System.out.println("-----------------------");

        // skip(n) 跳过前 n 个数据
        list.stream().skip(5).forEach(System.out::println);
        System.out.println("-----------------------");

        // distinct() 去重,通过对象的 hashCode() 和 equals 方法判断
        list.add(new Employee(1010, "狗杰瑞", 45, 4102.2));
        list.add(new Employee(1010, "狗杰瑞", 45, 4102.2));
        list.add(new Employee(1010, "狗杰瑞", 45, 4102.2));
        list.add(new Employee(1010, "狗杰瑞", 45, 4102.2));
        list.stream().distinct().forEach(System.out::println);
        System.out.println("-----------------------");

    }
映射操作

在这里插入图片描述
例子:

// 映射
    @Test
    public void test2() {
        // map(Function f) 通过函数 f 对流中的所有数据进行处理
        List<String> list = Arrays.asList("aa", "sds", "qwe", "zxc", "dd");
        // 将流中的所有字符串变成大写
        list.stream().map(str -> str.toUpperCase(Locale.ROOT)).forEach(System.out::println);

        // 获取姓名长度大于 3 的员工的姓名
        List<Employee> employees = EmployeeData.getEmployees();
        employees.stream().map(Employee::getName).filter(s -> s.length() > 3).forEach(System.out::println);

        // 将原集合中的每个字符串处理成流,然后把所有的流组合在一起形成一个流(就像集合里有集合一样)
        Stream<Stream<Character>> streamStream = list.stream().map(StreamAPITest02::fromStringToStream);
        // 这里类似双层 for 循环
        streamStream.forEach(s -> s.forEach(System.out::println));

        // flatMap(Function f) 将流中的每个数据处理为流,然后将所有处理后的流整合成一个流
        // 以下输出结果和上方相同
        list.stream().flatMap(StreamAPITest02::fromStringToStream).forEach(System.out::println);
    }

    // 将字符串中多个字符构成的集合转换成对应的 Stream 的实例
    public static Stream<Character> fromStringToStream(String str) {

        ArrayList<Character> list = new ArrayList<>();
        for (Character c : str.toCharArray()) {
            list.add(c);
        }
        return list.stream();
    }
排序

在这里插入图片描述
例子:

// 排序
    @Test
    public void test3() {
        List<Integer> list = Arrays.asList(12, 45, 78, 5, 1, 74865, 456);
        list.stream().sorted().forEach(System.out::println);

        // 自定义降序排序
        list.stream().sorted((a, b) -> b - a).forEach(System.out::println);
    }

终止操作

终止操作会真正执行中间操作(链)并产生结果

匹配与查找

在这里插入图片描述
例子:

// 匹配与查找
    @Test
    public void test1() {
        List<Employee> employees = EmployeeData.getEmployees();
        System.out.println(employees.stream().allMatch(e -> e.getAge() > 10));  // true
        System.out.println(employees.stream().anyMatch(e -> e.getAge() < 11));  // false
        System.out.println(employees.stream().noneMatch(e -> e.getAge() < 11)); // true
        System.out.println(employees.stream().findFirst());
    }

此外还有

在这里插入图片描述

归约

在这里插入图片描述
例子:

// 归约
    @Test
    public void test2() {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
        // T reduce(T identity, BinaryOperator<T> accumulator)
        // 其中 identity 表示初始值,即 accumulator 计算结果加上 identity 为 reduce 结果
        System.out.println(list.stream().reduce(10, Integer::sum));  // 55

        // T reduce(BinaryOperator<T> accumulator)
        // 返回 accumulator 计算结果
        System.out.println(list.stream().reduce(Integer::sum)); // 45
    }
收集

在这里插入图片描述
其中 Collector 接口的实现主要是 Collectors,主要方法有
在这里插入图片描述

例子:

// 收集
    @Test
    public void test3() {
        List<Employee> employees = EmployeeData.getEmployees();
        List<Employee> list = employees.stream().filter(e -> e.getAge() > 100).collect(Collectors.toList());
        list.forEach(System.out::println);

        Set<Employee> set = employees.stream().filter(e -> e.getAge() > 100).collect(Collectors.toSet());
        set.forEach(System.out::println);
    }

Optional 类

Optional<T> 类是一个容器类,可以保存类型 T 的值,表示这个值存在。
比较常用的功能是用其保存 null,表示空值,这样做可以避免空指针异常

Optional 提供很多有用的方法,方便程序员进行空值检测

创建 Optional 类对象的方法

  • Optional.of(T t)创建一个 Optional 实例,t 必须非空
  • Optional.empty()创建一个空的 Optional 实例
  • Optional.ofNullable(T t)t 可以为 null

例子:

// 创建
    @Test
    public void testCreate() {
        Dog dog = new Dog();
        // of(T t) t 必须非空
        Optional<Dog> optionalDog = Optional.of(dog);
        System.out.println(optionalDog);    // Optional[Dog{name='null'}]

        dog = null;
        // ofNullable(T t) t 可以为空
        Optional<Dog> optionalDog1 = Optional.ofNullable(dog);
        System.out.println(optionalDog1);   // Optional.empty
        Dog dog1 = optionalDog1.orElse(new Dog("杰瑞"));
        System.out.println(dog1);   // Dog{name='杰瑞'}
    }

判断 Optional 容器中是否包含对象

  • boolean isPresent()判断是否包含对象
  • void isPresent(Consumer<? super T> consumer)如果有值,就执行 Consumer 接口的实现方法,并且该值会作为方法的实参

获取 Optional 容器的对象

  • T get()如果调用对象包含值,则返回对应值,否则抛出异常
  • T orElse(T other)如果有值则返回,否则返回指定的 other 对象
  • T orElseGet(Supplier<? extends T> other)如果有值则返回,否则返回 Supplier 接口实现方法返回的对象
  • T orElseThrow(Supplier<? extends X> exceptionSupplier)如果有值则返回,否则抛出 Supplier 接口实现方法提供的异常

实例对比

设当前有两个类 Cat 和 Dog,其中 Cat 有 Dog 类型的成员变量 dog,Dog 有 String 类型的成员变量 name

若想实现一个方法获取 Cat 的 dog 的 name,那么有:

public String getDogName(Cat cat) {
        return cat.getDog().getName();
    }

    // 防止空指针异常,优化
    public String getDogNamePro(Cat cat) {
        if (cat != null) {
            Dog dog = cat.getDog();
            if (dog != null) {
                return dog.getName();
            }
        }
        return null;
    }

    // 使用 Optional 优化
    public String getDogName_Optional(Cat cat) {
        Optional<Cat> catOptional = Optional.ofNullable(cat);
        Cat cat1 = catOptional.orElse(new Cat(new Dog("jerry")));
        Dog dog = cat1.getDog();
        Optional<Dog> dogOptional = Optional.ofNullable(dog);
        Dog dog1 = dogOptional.orElse(new Dog("杰瑞狗"));
        return dog1.getName();
    }
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三更鬼

谢谢老板!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值