Java8新特性

目录

1,Lambda表达式

2,方法引用

3,stream

4,Optional

5,Nashorn JavaScript

6,日期时间

7,Base64编码


Java8即JDK1.8新增了很多新的特性,一起来探索一下吧。

1,Lambda表达式

Java8支持Lambda表达式,来支持函数式编程,将函数作为方法的参数。经常用于代替匿名内部类的使用。函数接口可以使用lambda表达式

函数接口,接口有且只有一个抽象方法,并且在类上标记注解@FunctionalInterface注解,表示该类是函数接口。另外,只要接口是只有一个抽象函数的接口,虚拟机会自动判断为函数接口,但是为了理解清晰,最好在接口函数上增加@FunctionalInterface注解,以示区分。

函数接口可以转化为Lambda表达式,如:

    public static void main(String[] args) {
        Predicate<Integer> predicate = n -> n%2 == 0;
        log.info(predicate.test(3)+"");
        Runnable runnable = ()->{
            while(true){
                log.info(System.currentTimeMillis()+"");
                try {
                    Thread.sleep(5000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        Comparator<Integer> comparator = (a,b) -> a.compareTo(b);
    }

当不符合函数接口规则的接口,标记为函数接口,会编译报错。

default方法,jdk8可以允许接口有Default默认的已实现的方法存在,接口的实现可以重写default方法,也可以补充协议default方法。当接口中有多个方法定义,但是又要将接口标记为函数接口时,可以主要的一个方法外的其他改为default方法。

Lambda表达式,格式为(parameters) -> {statements} 或者 parameter -> statement,语句的特性包括:

  • 可选类型声明,可以不声明参数的类型,编译器自动识别。
  • 可选参数小括号,当参数只有一个时,可以不要小括号。
  • 可选语句花括弧,当语句只有一个时,可以不要花括弧。
  • 可选返回关键字,当语句只有一个时,默认返回该语句的返回值,不需要return关键字。有多个语句时,需要return关键字。

代码样例如下:

public class Java8Test {
    static void main(String[] args) {
        //lambda表达式,初始化Java8Interface接口的实例,一个参数一个语句
        I1 i1 = a -> Math.sqrt(a);
        Double sqrt = i1.sqrt(4);
        Double pow2 = i1.pow2(2);
        System.out.println(sqrt);
        System.out.println(pow2);
        //lambda表达式,一个多个参数多个语句
        I2 i2 = (a, b) -> {return Math.pow(a, b);};
        Double cbrt = i2.cbrt(4);
        double pow = i2.pow(2,3);
        System.out.println(cbrt);
        System.out.println(pow);
    }
}
//函数式接口,有且只有一个抽象方法
@FunctionalInterface
interface I1 {
    //有且只有一个抽象方法
    Double sqrt(Integer a);

    //default方法
    default Double pow2(Integer a){
        return Math.pow(a, 2);
    }
}

@FunctionalInterface
interface I2 {
    //有且只有一个抽象方法
    Double pow(Integer a, Integer b);

    //default方法
    default Double cbrt(Integer a){
        return Math.cbrt(a);
    }
}

输出结果:

2.0
4.0
1.5874010519681996
8.0

变量的作用域,lambda表达式中的可以访问外部的变量,但是不可以修改外部的变量,表达式内的变量也不可以和外部的变量同名。否则编译报错。

2,方法引用

Java8提供了新的方法引用方式“::”,功能等价于“.”,通过实例来了解Java8如何通过新的方式饮用方法。

引用构造函数时,需借助于Supplier<T>类,引用构造方法,返回一个Supplier<T>类型的对象。

public class Apple {

    private static final Logger log = LoggerFactory.getLogger(Apple.class);

    public Apple(){
        log.info("create Apple object");
    }

    static void deck(Apple apple){
        log.info("apple deck "+apple+", wow beautiful");
    }

    void call(Apple apple){
        log.info(this+" calls "+apple);
    }

    void juicing(){
        log.info("juicing apple "+this);
    }

    public static  void  main(String[] args){
        //调用构造函数,借助于Supplier类,实现构造方法的引用
        Supplier<Apple> supplier = Apple::new;
        Apple apple = supplier.get();

        List<Apple> appleList = Arrays.asList(apple);
        //Apple类静态方法deck引用,foreach也是java8新特性,遍历
        appleList.forEach(Apple::deck);

        //特定类的任意对象方法引用,类型Apple的遍历对象的juicing方法引用
        appleList.forEach(Apple::juicing);

        //指定对象的方法引用,调用apple1的call方法,参数为遍历对象,
        Supplier<Apple> supplier1 = Apple::new;
        Apple apple1 = supplier1.get();
        appleList.forEach(apple1::call);
    }
}

输出结果为

2019-08-13 11:17:17	[main]	[INFO ]	[com.weihao.java8.Apple]create Apple object
2019-08-13 11:17:17	[main]	[INFO ]	[com.weihao.java8.Apple]apple deck com.weihao.java8.Apple@53aad5d5, wow beautiful
2019-08-13 11:17:17	[main]	[INFO ]	[com.weihao.java8.Apple]juicing apple com.weihao.java8.Apple@53aad5d5
2019-08-13 11:17:17	[main]	[INFO ]	[com.weihao.java8.Apple]create Apple object
2019-08-13 11:17:17	[main]	[INFO ]	[com.weihao.java8.Apple]com.weihao.java8.Apple@8317c52 calls com.weihao.java8.Apple@53aad5d5

3,stream

Java8新增了stream操作,stream是一个来自数据源的元素队列,并支持聚合计算等操作。stream可以类似于sql一样,对集合元素进行查询,运算,排序,聚合等。

  • 元素,特定类型的对象,形成一个队列。stream不存储,只用于计算。
  • 数据源,流的来源,集合,数组,channel,产生器generator。
  • 操作,类似SQL一样的操作,过滤,排序,查询,分组,合并,匹配等

stream的中间操作会返回中间操作的结果,结果是stream对象本身,迭代方式是通过访问者模式实现的内部迭代。通过代码来了解具体的作用。

public class StreamTest {

    private static final Logger log = LoggerFactory.getLogger(StreamTest.class);

    public static void main(String[] args) {
        //借助Supplier,引用Random构造器,初始化Random实例
        Supplier<Random> supplier =Random::new;
        Random random = supplier.get();
        //随机int数组
        //boxed(),返回流的元素组成的流,元素都是Integer包装的
        //collect(),在流的元素上执行聚合操作,Collectors.toList(),返回将输入元素积累成List的Collector
        List<Integer> list = random.ints(30,1,100).boxed().collect(Collectors.toList());
        log.info("random ary:"+list.toString());
        log.info("random ary length:" + list.stream().count());
        log.info("random ary distinct length:" + list.stream().distinct().count());
        log.info("random ary distinct and greater or equal to 50:"+list.stream().distinct().filter(a -> a >= 50).collect(Collectors.toList()).toString());
        log.info("random ary skip 10 and limit 10 result:"+ list.stream().sorted().skip(10).limit(10).collect(Collectors.toList()));
        //遍历
        //list.stream().forEach(a -> log.info(a+""));
        log.info("random ary distributed and sorted result:"+list.stream().map(i -> i/10).sorted().collect(Collectors.toList()).toString());
        log.info("random ary distributed and group result:"+list.stream().collect(Collectors.groupingBy(i -> i/10)).toString());
        log.info("random ary distributed and group ,count result:"+list.stream().collect(Collectors.groupingBy(i -> i/10, Collectors.counting())).toString());

        IntSummaryStatistics summaryStatistics = list.stream().mapToInt(i -> i).summaryStatistics();
        log.info("random ary max:"+summaryStatistics.getMax());
        log.info("random ary min:"+summaryStatistics.getMin());
        log.info("random ary sum:"+summaryStatistics.getSum());
        log.info("random ary average:"+summaryStatistics.getAverage());
        log.info("random ary count:"+summaryStatistics.getCount());

        //增加元素100
        log.info("add elements 100");
        summaryStatistics.accept(100);
        log.info("random ary max:"+summaryStatistics.getMax());
        log.info("random ary min:"+summaryStatistics.getMin());
        log.info("random ary sum:"+summaryStatistics.getSum());
        log.info("random ary average:"+summaryStatistics.getAverage());
        log.info("random ary count:"+summaryStatistics.getCount());

        //合并其他统计结果
        log.info("combine other statistics");
        summaryStatistics.combine(random.ints(10,0,100).summaryStatistics());
        log.info("random ary max:"+summaryStatistics.getMax());
        log.info("random ary min:"+summaryStatistics.getMin());
        log.info("random ary sum:"+summaryStatistics.getSum());
        log.info("random ary average:"+summaryStatistics.getAverage());
        log.info("random ary count:"+summaryStatistics.getCount());

        //并行处理
        log.info("random ary parallel filter less than 10:"+list.parallelStream().filter(i -> i < 10).collect(Collectors.toList()).toString());
    }
}

代码执行结果如下

2019-08-13 15:26:30	[main]	[INFO ]	[com.weihao.java8.StreamTest]random ary:[81, 90, 99, 57, 64, 54, 89, 44, 47, 9, 13, 71, 87, 85, 79, 75, 91, 34, 65, 39, 13, 92, 93, 30, 33, 6, 12, 24, 30, 62]
2019-08-13 15:26:30	[main]	[INFO ]	[com.weihao.java8.StreamTest]random ary length:30
2019-08-13 15:26:30	[main]	[INFO ]	[com.weihao.java8.StreamTest]random ary distinct length:28
2019-08-13 15:26:30	[main]	[INFO ]	[com.weihao.java8.StreamTest]random ary distinct and greater or equal to 50:[81, 90, 99, 57, 64, 54, 89, 71, 87, 85, 79, 75, 91, 65, 92, 93, 62]
2019-08-13 15:26:30	[main]	[INFO ]	[com.weihao.java8.StreamTest]random ary skip 10 and limit 10 result:[39, 44, 47, 54, 57, 62, 64, 65, 71, 75]
2019-08-13 15:26:30	[main]	[INFO ]	[com.weihao.java8.StreamTest]random ary distributed and sorted result:[0, 0, 1, 1, 1, 2, 3, 3, 3, 3, 3, 4, 4, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9]
2019-08-13 15:26:30	[main]	[INFO ]	[com.weihao.java8.StreamTest]random ary distributed and group result:{0=[9, 6], 1=[13, 13, 12], 2=[24], 3=[34, 39, 30, 33, 30], 4=[44, 47], 5=[57, 54], 6=[64, 65, 62], 7=[71, 79, 75], 8=[81, 89, 87, 85], 9=[90, 99, 91, 92, 93]}
2019-08-13 15:26:30	[main]	[INFO ]	[com.weihao.java8.StreamTest]random ary distributed and group result:{0=2, 1=3, 2=1, 3=5, 4=2, 5=2, 6=3, 7=3, 8=4, 9=5}
2019-08-13 15:26:30	[main]	[INFO ]	[com.weihao.java8.StreamTest]random ary max:99
2019-08-13 15:26:30	[main]	[INFO ]	[com.weihao.java8.StreamTest]random ary min:6
2019-08-13 15:26:30	[main]	[INFO ]	[com.weihao.java8.StreamTest]random ary sum:1668
2019-08-13 15:26:30	[main]	[INFO ]	[com.weihao.java8.StreamTest]random ary average:55.6
2019-08-13 15:26:30	[main]	[INFO ]	[com.weihao.java8.StreamTest]random ary count:30
2019-08-13 15:26:30	[main]	[INFO ]	[com.weihao.java8.StreamTest]add elements 100
2019-08-13 15:26:30	[main]	[INFO ]	[com.weihao.java8.StreamTest]random ary max:100
2019-08-13 15:26:30	[main]	[INFO ]	[com.weihao.java8.StreamTest]random ary min:6
2019-08-13 15:26:30	[main]	[INFO ]	[com.weihao.java8.StreamTest]random ary sum:1768
2019-08-13 15:26:30	[main]	[INFO ]	[com.weihao.java8.StreamTest]random ary average:57.03225806451613
2019-08-13 15:26:30	[main]	[INFO ]	[com.weihao.java8.StreamTest]random ary count:31
2019-08-13 15:26:30	[main]	[INFO ]	[com.weihao.java8.StreamTest]combine other statistics
2019-08-13 15:26:30	[main]	[INFO ]	[com.weihao.java8.StreamTest]random ary max:100
2019-08-13 15:26:30	[main]	[INFO ]	[com.weihao.java8.StreamTest]random ary min:6
2019-08-13 15:26:30	[main]	[INFO ]	[com.weihao.java8.StreamTest]random ary sum:2223
2019-08-13 15:26:30	[main]	[INFO ]	[com.weihao.java8.StreamTest]random ary average:54.21951219512195
2019-08-13 15:26:30	[main]	[INFO ]	[com.weihao.java8.StreamTest]random ary count:41
2019-08-13 15:26:30	[main]	[INFO ]	[com.weihao.java8.StreamTest]random ary parallel filter less than 10:[9, 6]

从上面的实例中可以看到stream的功能十分强大。流式的处理,类似sql的查询统计分析等功能,简洁的代码书写方式,也可以看到Java发展的方向。

4,Optional

Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。

Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。

Optional 类的引入很好的解决空指针异常。

因为新增的是API,通过代码实例来看Optional的用法。

@Test
public void testOptional(){
    //创建null optional
    Optional<String> emptyOptional = Optional.empty();
    //创建不为空的optional, of()方法,不能传null
    Optional<String> optionalS = Optional.of("1");
    //创建空字符串的optional
    Optional<String> optionalS1 = Optional.of("");
    //创建null的optional
    Optional<String> optionalS2 = Optional.ofNullable(null);

    log.info("isPresent");
    log.info(emptyOptional.isPresent()+"");
    log.info(optionalS.isPresent()+"");
    log.info(optionalS1.isPresent()+"");
    log.info(optionalS2.isPresent()+"");

    log.info("toString");
    log.info(emptyOptional.toString());
    log.info(optionalS.toString());
    log.info(optionalS1.toString());
    log.info(optionalS2.toString());

    log.info("get");
    try{
        log.info(emptyOptional.get());
    }catch (Exception e){
        log.info("emptyOptional.get() ERROR");
    }
    log.info(optionalS.get().toString());
    log.info(optionalS1.get().toString());
    try{
        log.info(optionalS2.get());
    }catch (Exception e){
        log.info("optionalS2.get() ERROR");
    }

    //如果存在,执行lambda表达式
    log.info("ifPresent");
    emptyOptional.ifPresent(a -> log.info(a));
    optionalS.ifPresent(a -> log.info(a));
    optionalS1.ifPresent(a -> log.info(a));
    optionalS2.ifPresent(a -> log.info(a));

    //过滤
    log.info("filter");
    log.info(emptyOptional.filter(s -> !s.isEmpty()).isPresent()+"");
    log.info(optionalS.filter(s -> !s.isEmpty()).isPresent()+"");
    log.info(optionalS1.filter(s -> !s.isEmpty()).isPresent()+"");
    log.info(optionalS2.filter(s -> !s.isEmpty()).isPresent()+"");

    //如果optional存在,则返回optional的map结果,如果不存在则返回空optional
    log.info("flatMap: "+optionalS.flatMap(a -> Optional.of("map"+a)).toString());
    //不存在返回其他
    log.info("orElse: "+optionalS.orElse("0"));
    //不存在返回其他对象
    log.info("orElseGet: "+optionalS.orElseGet(String::new));
    try{
        log.info("orElseThrow: "+ optionalS2.orElseThrow(RuntimeException::new));
    }catch (Exception e){
        log.info("catch exception",e);
    }
}

代码执行结果

2019-08-13 16:14:29	[main]	[INFO ]	[com.weihao.TestJava8]isPresent
2019-08-13 16:14:29	[main]	[INFO ]	[com.weihao.TestJava8]false
2019-08-13 16:14:29	[main]	[INFO ]	[com.weihao.TestJava8]true
2019-08-13 16:14:29	[main]	[INFO ]	[com.weihao.TestJava8]true
2019-08-13 16:14:29	[main]	[INFO ]	[com.weihao.TestJava8]false
2019-08-13 16:14:29	[main]	[INFO ]	[com.weihao.TestJava8]toString
2019-08-13 16:14:29	[main]	[INFO ]	[com.weihao.TestJava8]Optional.empty
2019-08-13 16:14:29	[main]	[INFO ]	[com.weihao.TestJava8]Optional[1]
2019-08-13 16:14:29	[main]	[INFO ]	[com.weihao.TestJava8]Optional[]
2019-08-13 16:14:29	[main]	[INFO ]	[com.weihao.TestJava8]Optional.empty
2019-08-13 16:14:29	[main]	[INFO ]	[com.weihao.TestJava8]get
2019-08-13 16:14:29	[main]	[INFO ]	[com.weihao.TestJava8]emptyOptional.get() ERROR
2019-08-13 16:14:29	[main]	[INFO ]	[com.weihao.TestJava8]1
2019-08-13 16:14:29	[main]	[INFO ]	[com.weihao.TestJava8]
2019-08-13 16:14:29	[main]	[INFO ]	[com.weihao.TestJava8]optionalS2.get() ERROR
2019-08-13 16:14:29	[main]	[INFO ]	[com.weihao.TestJava8]ifPresent
2019-08-13 16:14:30	[main]	[INFO ]	[com.weihao.TestJava8]1
2019-08-13 16:14:30	[main]	[INFO ]	[com.weihao.TestJava8]
2019-08-13 16:14:30	[main]	[INFO ]	[com.weihao.TestJava8]filter
2019-08-13 16:14:30	[main]	[INFO ]	[com.weihao.TestJava8]false
2019-08-13 16:14:30	[main]	[INFO ]	[com.weihao.TestJava8]true
2019-08-13 16:14:30	[main]	[INFO ]	[com.weihao.TestJava8]false
2019-08-13 16:14:30	[main]	[INFO ]	[com.weihao.TestJava8]false
2019-08-13 16:14:30	[main]	[INFO ]	[com.weihao.TestJava8]flatMap: Optional[map1]
2019-08-13 16:14:30	[main]	[INFO ]	[com.weihao.TestJava8]orElse: 1
2019-08-13 16:14:30	[main]	[INFO ]	[com.weihao.TestJava8]orElseGet: 1
2019-08-13 16:14:30	[main]	[INFO ]	[com.weihao.TestJava8]catch exception
java.lang.RuntimeException: null
	at java.util.Optional.orElseThrow(Optional.java:290)
	at com.weihao.TestJava8.testOptional(TestJava8.java:78)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)

5,Nashorn JavaScript

Nashorn 一个 javascript 引擎。

从JDK 1.8开始,Nashorn取代Rhino(JDK 1.6, JDK1.7)成为Java的嵌入式JavaScript引擎。Nashorn完全支持ECMAScript 5.1规范以及一些扩展。它使用基于JSR 292的新语言特性,其中包含在JDK 7中引入的 invokedynamic,将JavaScript编译成Java字节码。与先前的Rhino实现相比,这带来了2到10倍的性能提升。

jjs

jjs是一个Java命令,接受Javascription代码或者文件为参数,并执行代码。

命令行使用jjs如下

[root@ecs-7bc6-0001 ~]# jjs
jjs> print("hello world");
hello world
jjs> ^C[root@ecs-7bc6-0001 ~]#

使用jjs执行javascription文件,首先新建a.js文件如下:

function f(a){
    print("hello "+a);
}
f("world");

执行js程序代码

[root@ecs-7bc6-0001 ~]# jjs a.js
hello world

Java中执行JavaScript,通过以下样例实现校验用户的身份证,手机号码是否合法。

@Test
public void testJavaScript() throws ScriptException {
    //创建脚本引擎管理
    ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
    //获取脚本引擎管理
    NashornScriptEngine scriptEngine = (NashornScriptEngine) scriptEngineManager.getEngineByName("nashorn");
    //创建脚本参数
    Map<String, Object> map = new HashMap<>();
    map.put("idcardNO", "610300199801016666");
    map.put("phone", "18888888888");
    Bindings bindings = new SimpleBindings();
    bindings.put("user",map);
    //定义脚本
    String javascript = "" +
            "var idcardReg = /^[1-9]\\d{7}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}$|^[1-9]\\d{5}[1-9]\\d{3}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}([0-9]|X)$/;" +
            "var phoneReg = /^1[3456789]\\d{9}$/;" +
            " var a = idcardReg.test(user.idcardNO);" +
            " var b = phoneReg.test(user.phone);" +
            "function f(c,d){return c&&d};" +
            "f(a,b);";
    //编译脚本
    CompiledScript compiledScript = scriptEngine.compile(javascript);
    //执行脚本
    Object obj = compiledScript.eval(bindings);
    log.info(obj.toString());
}

JavaScript中调用Java,返回当前时间。

@Test
public void testJavaScriptCallJava() throws ScriptException {
    //创建脚本引擎管理
    ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
    //获取脚本引擎管理
    NashornScriptEngine scriptEngine = (NashornScriptEngine) scriptEngineManager.getEngineByName("nashorn");
    //创建脚本参数
    //定义脚本
    String javascript = "var SimpleDateFormat = Java.type('java.text.SimpleDateFormat');" +
            "var Date = Java.type('java.util.Date');" +
            "function currentTime(){" +
            "var sdf = new SimpleDateFormat('yyyy-MM-dd HH:mm:ss');" +
            "var cur = new Date();" +
            "return sdf.format(cur);" +
            "};" +
            "currentTime();";
    //编译脚本
    CompiledScript compiledScript = scriptEngine.compile(javascript);
    //执行脚本
    Object obj = compiledScript.eval();
    log.info(obj.toString());
}

6,日期时间

Java8引入了新的日期和时间处理Api,针对之前版本的日期时间的优化API,主要包括:

Local,简化日期时间的处理,没有时区问题。

Zoned,通过制定的时区处理日期和时间。

同样,通过具体样例来了解新的日期和时间:

@Test
public void testNewDateAndTime(){
    LocalDateTime current = LocalDateTime.now();
    //格式化
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    log.info(dtf.format(current));

    current.getDayOfMonth();
    current.plusDays(1);
    log.info(dtf.format(current));

    current.minusHours(20);
    log.info(dtf.format(current));
    log.info(dtf.parse("2018-08-08 12:00:00").toString());
}

输出结果如下

2019-08-13 18:13:42	[main]	[INFO ]	[com.weihao.TestJava8]2019-08-13 18:13:42
2019-08-13 18:13:42	[main]	[INFO ]	[com.weihao.TestJava8]2019-08-13 18:13:42
2019-08-13 18:13:42	[main]	[INFO ]	[com.weihao.TestJava8]2019-08-13 18:13:42
2019-08-13 18:13:42	[main]	[INFO ]	[com.weihao.TestJava8]{},ISO resolved to 2018-08-08T12:00

7,Base64编码

Java8支持了Base64编码,不需要再引入其他第三方库支持Base64编码了,如下:

@Test
public void testBase64(){
    String a = "Hello World!";
    String base64String = new String(Base64.getEncoder().encode(a.getBytes(Charsets.UTF_8)), Charsets.UTF_8);
    log.info("encode: "+ base64String);

    byte[] bytes = Base64.getDecoder().decode(base64String);
    log.info("decode:" + new String(bytes, Charsets.UTF_8));
}

运行结果如下:

2019-08-13 18:11:28	[main]	[INFO ]	[com.weihao.TestJava8]encode: SGVsbG8gV29ybGQh
2019-08-13 18:11:28	[main]	[INFO ]	[com.weihao.TestJava8]decode:Hello World!

(完)(^_^)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值