2021 面试冲刺

为 2021 面试准备

时间越来约少了,希望志同道合的朋友一起学习进度。
目录准备
1. 基础 :对象,集合,数据结构,异常
2. jvm:类加载原理,jvm 参数,jvm 调优经验
3. 数据库 mysql :B+树,索引原理,索引失效场景,索引条件,分表分库策略,连接池的基本参数
4. mq :性能对比,HA策略,消息重复与丢失场景
5. redis :性能对比,HA策略,redis key策略,redis 持久化,缓存击穿,缓存雪崩,连接池的基本参数
6. 并发 :线程的创建,线程池的基本参数
7. Spring 基本知识
8. spring cloud
9. K8S
10. flink
11. 技术框架
12. 项目实践

基础知识

1 基本数据类型

  • 列举基本数据类型及 长度
    byte (8),char(16),short(16),int(32),long(64),boolean(1),double(64),float(32)
  • int 与 Integer
    1 int 为基本数据类型,Integer为 包装类
    2 int默认值 为 0 ,Integer 默认值 为 null,需要实例化才能使用
    3 Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值
    比较时 int a 可以用 == ,Integer 通常用equals
    java对于-128到127之间的数,会进行缓存,Integer i = 127时,会将127进行缓存,下次再写Integer j = 127时,就会直接从缓存中取,就不会new了
    源码:
public static Integer valueOf(int i) {
        assert IntegerCache.high >= 127;
         if (i >= IntegerCache.low && i <= IntegerCache.high)
             return IntegerCache.cache[i + (-IntegerCache.low)];
         return new Integer(i);
     }

所以
Integer a = 127 ;Integer b = 127; System.out.print(a == b); //true
当integer 为 null 时 做 == 比较 拆箱时会 空指针异常

  • String,StringBuffer和StringBuilder
    String 是 flinally 类 无法被继承, 也无法被改变,此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,这样不仅效率低下,而且大量浪费有限的内存空间,所以经常改变内容的字符串最好不要用 String 。
    StringBuffer与StringBuilder 都是可变的
    StringBuffer 是 线程安全的 ,append 方法加了 同步方法锁 synchronized ,速度慢
    StringBuilder 是线程不安全的,速度快

2 集合

  • ArrayList 和 LinkList
    array list 采用 数组结构 存储
    linklist 采用 链表结构 存储
    数据结构 是 内存中开辟 一定大小的 连续的内存空间,通过下标指针可以快速的找到指定位置的数据,但是如果是插入一个数据需要改变 n 个 数据 ,且超过大小时 需要 扩容
    链表结构 是通过 next 指向 下个 节点或上个节点,在 查询时 ,需要从第一个元素开始 n 次 才能找到 对应位置数据,但是插入时 只 需要在指定位置 next 到 新的 节点,新 节点next 下一个节点即 完成插入
    -哪些 集合是 线程安全
    vector,hashtable,ConcurrentHashMap,Stack
  • 集合的结构
    Iterator
    -Colletion
    List (linkList,arrayList,Vector)Set(HashSet,TreeSet)
    -Map
    HashMap HashTable TreeMap
    基本方法:
    add(Object o) , clear(), isEmpty() ,contains(Object o), iterator() ,remove(Object o),int size()
  • 谈下hashmap 底层实现 及 1.8 的变动
    hashmap 底层采用 数据加链表 存储
    node 结构:
static class Entry<K,V> implements Map.Entry<K,V> {
    final K key;  // 键
    V value;  // 值
    Entry<K,V> next; // 指向下一个节点 ,也是一个Entry对象,从而形成解决hash冲突的单链表
    int hash;  // hash值
    }

1.7
插入流程,
1 key 值 通过 hashCode 得到hash
2 通过 hash值 与 map table (数组大小) 计算下标值
3 如果下标值 的 链表 没值 则插入
4 判断链表中的 key 是否 重复,有重复则覆盖,没有则插入到 最尾端 (超过 阀值 则需要扩容)

1.8 时
数据结构变化:
数组 + 链表(红黑树)
当链表数 > TREEIFY_THRESHOLD = 8; 则 链表变红黑树

  • ConcurrentHashMap 的思想
    由于 hashmap 存在 多线程安全问题,而 hashtable 则存在 效率问题 (synchronized 整个table)
    put 方法1.8
    如果没有初始化就先调用initTable()方法来进行初始化过程
    如果没有hash冲突就直接CAS插入
    如果还在进行扩容操作就先进行扩容
    如果存在hash冲突,就加锁来保证线程安全,这里有两种情况,一种是链表形式就直接遍历到尾端插入,一种是红黑树就按照红黑树结构插入,
    最后一个如果Hash冲突时会形成Node链表,在链表长度超过8,Node数组超过64时会将链表结构转换为红黑树的结构,break再一次进入循环
    如果添加成功就调用addCount()方法统计size,并且检查是否需要扩容

参考

3 JDK8 的 特性

> 连接

  • stream
    Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
    Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
    常用 的 foreach,map,reduce,Collectors(),filter
  • optional
    optional 可以对一些空对象进行判断,orelse 进行处理,
  • hashmap
    对hash map 数据结构进行 优化,采用数据+ 链表/红黑树的方式 ,当 链表长度大于 8 时 转换为 红黑树
  • jvm
    永久代 存储:类加载信息 (jvm 分配的 空间)
    将永久代 替换为 元空间( 本地内存)
    修改默认的垃圾回收器 Parallel Scavenge(新生代)+Parallel Old(老年代)
  • lcoaldate/localdatetime
    对时间方法 处理起来 更加方便
  • lambada 表达式
    Lambda表达式的目标类型是函数式接口 , 匿名方法,简化代码

函数式接口:有且仅有一个抽象方法的接口。
标准格式: (参数类型 参数名称) -> { 代码语句 }

  • default
    关键词 default 对 接口 进行 默认方法 添加,使得 接口有一些统一方法 可以调用 减少 代码 实现
  • 方法引用
    使用方法名字代表 对 方法的调用
System.out::println
  • JDK 1.8 新增加的函数接口:
    java.util.function 包
    @FunctionalInterface 注解表示可以用lambada

4 异常结构
异常分类:
所有异常 父类 为 throwable
子类分为 error, exception (check exception,runtime exception )

异常捕获
对于 run time exception 有两种 处理方式
a try cath flinally:

1、不管有木有出现异常或者try和catch中有返回值return,finally块中代码都会执行;
2、finally中最好不要包含return,否则程序会提前退出,返回会覆盖try或catch中保存的返回值。
3.如果方法中try,catch,finally中没有返回语句,则会调用这三个语句块之外的return结果
4.在try语句块或catch语句块中执行到System.exit(0)直接退出程序

b throw Exception 上层处理
我们在处理 controller 时 可以对 异常 统一 处理
@ControllerAdvice 处理所有 controller 层 的异常

JVM

1 类加载顺序
加载——链接(验证,准备,解析)——初始化——使用——卸载

  1. 加载: 通过全路径类名 加载成二进制数据流保存在 方法区,在 堆中存一个类对象
  2. 链接:
    a 校验 : 类中 结构
    b 准备 : 开辟 静态 空间给 类变量 ,默认值
    c:解析: 将 符号引用 转化为 实际 引用

在java中,一个java类将会编译成一个class文件。在编译时,java类并不知道引用类的实际内存地址,因此只能使用符号引用来代替。比如org.simple.People类引用org.simple.Tool类,在编译时People类并不知道Tool类的实际内存地址,因此只能使用符号org.simple.Tool(假设)来表示Tool类的地址。而在类装载器装载People类时,此时可以通过虚拟机获取Tool类
的实际内存地址,因此便可以既将符号org.simple.Tool替换为Tool类的实际内存地址,及直接引用地址。

  1. 初始化:执行类 构造器 等方法 初始化
    a 父类的静态代码块
    b 子类的静态代码块
    c 初始化父类的属性值/父类的普通代码块(自上而下的顺序排列)
    d 父类的构造方法
    e 初始化子类的属性值/子类的普通代码块(自上而下的顺序排列)
    f 子类的构造方法。

2 什么时候初始化?

类的主动引用(一定会发生类的初始化)

1 当虚拟机启动,先初始化main方法所在的类
2 new一个类的对象
3 调用类的静态成员(除了final常量)和静态方法
4 使用Java.lang.reflect包下的方法对类进行反射调用
5 当初始化一个类,如果父类没有初始化,则会先初始化其父类

类的被动引用(不会发生类的初始化)

1 当访问一个静态域时只有真正生命这个域的类才会被初始化。如:通过子类引用父类的静态变量,不会导致子类的初始化
2 通过数组定义类引用,不会触发此类的初始化
3 引用常量不会触发此类的初始化(常量在链接阶段就已经存到调用类的常量池了)

2 JVM 设置参数
常用参数

-Xms: 初始堆大小
-Xmx: 最大堆大小
-XX:NewSize=n: 设置年轻代大小
-XX:NewRatio=n: 设置年轻代和年老代的比值。如:为 3,表示年轻代与年老代比值为 1:3,年轻代占整个年轻代年老代和的 1/4
-XX:SurvivorRatio=n: 年轻代中 Eden 区与两个 Survivor 区的比值。注意 Survivor 区有两 个。如: 3,表示 Eden: Survivor=3: 2,一个 Survivor 区占整个年轻代的 1/5
-XX:MaxPermSize=n: 设置持久代大小

自己使用的参数

-Xmx2048M 最大 堆内存大小
-Xms2048M 初始 堆内存大小
-Xss512k 每线程堆栈 大小
-XX:MetaspaceSize=256M 初始 元空间大小
-XX:MaxMetaspaceSize 最大 元空间大小
-XX:MinMetaspaceFreeRatio 元空间 GC 后的最小增长比例
-XX:MaxMetaspaceFreeRatio 元空间 GC 后的最大增长比例
-XX:+UseConcMarkSweepGC 使用CMS 回收器
-XX:SurvivorRatio=8 新生代 和老年代的比例
-XX:+CMSParallelRemarkEnabled 并行运行最终标记阶段,加快最终标记的速度
-XX:+CMSScavengeBeforeRemark 最终标记之前强制进行一个Minor GC
-XX:+PrintClassHistogram
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-XX:+PrintHeapAtGC
-Xloggc:gc.log 指定 GC 日志位置

CMS 参数

-XX:+UseConcMarkSweepGC 激活CMS收集器
-XX:ConcGCThreads 设置CMS线程的数量
-XX:+UseCMSInitiatingOccupancyOnly 只根据老年代使用比例来决定是否进行CMS
-XX:CMSInitiatingOccupancyFraction 设置触发CMS老年代回收的内存使用率占比
-XX:+CMSParallelRemarkEnabled 并行运行最终标记阶段,加快最终标记的速度
-XX:+UseCMSCompactAtFullCollection 每次触发CMS Full GC的时候都整理一次碎片
-XX:CMSFullGCsBeforeCompaction=* 经过几次CMS Full GC的时候整理一次碎片
-XX:+CMSClassUnloadingEnabled 让CMS可以收集永久带,默认不会收集
-XX:+CMSScavengeBeforeRemark 最终标记之前强制进行一个Minor GC

永久代:
方法去 主要用于存储类的信息、常量池、方法数据、方法代码等
永久代 是方法区的一种实现,各大厂商对方法区有各自的实现。永久代存放jvm运行时,需要的类,包含java库的类和方法
由于 常量池容易造成 永久代(相对较小)内存溢出,迁移至 1.7 以后 字符串常量由永久代转移到堆

  • 随着-XX:MinMetaspaceFreeRatio的不断增加,在到达设定的Max之前所经历的GC次数也就越少
  • 随着-XX:MinMetaspaceFreeRatio的不断增加,平均下来每一次GC后对应的MetaSpaceSize相对于之前的增长幅度都会增加。比如在-XX:MinMetaspaceFreeRatio为5的时候,每次增长还基本上维持在设置的增长最小值(-XX:MinMetaspaceExpansion)50MB,而增长到65的时候,每次增长都在200MB以上。这似乎可以说明-XX:MinMetaspaceFreeRatio越大,JVM越认为需要快速的增长MetaSpaceSize以防止频繁的进行的进行GC
  • -XX:MinMetaspaceFreeRatio设定的过小,会影响内存增长,导致比较频繁的GC。而过大怎会导致内存单次增长过多,造成不必要的浪费。默认的40是一个比较好的选择。

3 垃圾回收方法

什么是垃圾:?

常见的判断是否存活有两种方法:引用计数法可达性分析

引用计数法: 为每一个创建的对象分配一个引用计数器,用来存储该对象被引用的个数。当该个数为零,意味着没有人再使用这个对象,可以认为“对象死亡”。

可达性分析: 从树的根结点 GC Roots出发,持续遍历找出所有连接的树枝对象,这些对象则被称为“可达”对象,或称“存活”对象。其余的对象则被视为“死亡”的“不可达”对象,或称“垃圾”。

什么是GC ROOT?

-  虚拟机栈(帧栈中的本地变量表)中引用的对象。 	
 -  方法区中静态属性引用的对象。 	
 -  方法区中常量引用的对象。 	
 - 本地方法栈中JNI引用的对象。

有哪些方法回收垃圾?

  • 1 复制法 (使用回收率高 的 空间)

这种方法比较粗暴,直接把堆内存分成两部分,一段时间内只允许在其中一块内存上进行分配,当这块内存被分配完后,则执行垃圾回收,把所有存活对象全部复制到另一块内存上,当前内存则直接全部清空。
优点:
1 复制算法每次都是对整个半区进行内存回收,这样就减少了标记对象遍历的时间,在清除使用区域对象时,不用进行遍历,直接清空整个区域内存.
2将存活对象复制到保留区域时也是按地址顺序存储的,这样就解决了内存碎片的问题,在分配对象内存时不用考虑内存碎片等复杂问题,只需要按顺序分配内存即可。
缺点:
1、将内存缩小为原来的一半,浪费了一半的内存空间,代价太高;
2、如果对象的存活率很高,极端一点的情况假设对象存活率为100%,那么我们需要将所有存活的对象复制一遍,耗费的时间代价也是不可忽视的。

  • 2 标记-清理 法
    在垃圾收集器进行GC时,必须停止所有Java执行线程(也称"Stop The World")

标记-清理
第一步,所谓“标记”就是利用可达性遍历堆内存,把“存活”对象和“垃圾”对象进行标记,得到的结果如上图;
第二步,既然“垃圾”已经标记好了,那我们再遍历一遍,把所有“垃圾”对象所占的空间直接清空即可。
缺点:
1、效率问题。标记和清除两个阶段的效率都不高,因为这两个阶段都需要遍历内存中的对象,很多时候内存中的对象实例数量是非常庞大的,这无疑很耗费时间,而且GC时需要停止应用程序,这会导致非常差的用户体验。
2、空间问题。标记清除之后会产生大量不连续的内存碎片(从上图可以看出),内存空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾回收动作。

  • 3 标记整理法

既然上面的方法会产生内存碎片,那好,我在清理的时候,把所有存活对象扎堆到同一个地方,让所有存活的对象都向一端移动,然后直接清理掉端边线以外的内存,且没有碎片内存。
优点:没有碎片
缺点 :效率低

效率:复制算法 > 标记/整理算法 > 标记/清除算法(标记/清除算法有内存碎片问题,给大对象分配内存时可能会触发新一轮垃圾回收)
内存整齐率:复制算法 = 标记/整理算法 > 标记/清除算法
内存利用率:标记/整理算法 = 标记/清除算法 > 复制算法

Minor GC触发条件:当Eden区满时,触发Minor GC。

Full GC触发条件:
(1)调用System.gc时,系统建议执行Full GC,但是不必然执行
(2)老年代空间不足
(3)方法区(1.8之后改为元空间)空间不足
(4)创建大对象,比如数组,通过Minor GC后,进入老年代的平均大小大于老年代的可用内存
(5)由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小。

4 垃圾回收器

Serial (单线程),parnew (多线程,能与CMS 配合),parallel Scavenge (多线程,高吞吐)------新生代
CMS (多线程,低停顿, 标记清除,高CPU),parallel old (多线程), SeralOld (单线程) ------老年代
G1 ------- 堆内存
ZGC

jdk8环境下,默认使用 Parallel Scavenge(新生代)+ Parallel Old(老年代)
jdk1.9 默认垃圾收集器G1

CMS 与 G1 的区别,CMS 的回收步骤 和STW 的阶段

5 jvm 调优工具 ,jvm 调优实例

数据库

索引类型

1普通索引
2唯一索引
3组合索引
4全文索引
5主键索引

索引失效场景:

1 列 函数操作/表达式操作
2 like %在前
3 组合索引,未按顺序
4 查询条件中有or
5 查询条件索引 使用null
6 查询条件使用<> !=
7 使用大范围的in
8 查询目标占全表 数据30%

分库分表实践

采用 mycat\sharding jdbc

三范式

第一范式就是属性不可分割
第二范式就是要有主键,要求其他字段都依赖于主键。
第三范式就是要消除传递依赖
1、第一范式:
当关系模式R的所有属性都bai不能在分解为更基本的数据单位时,称R是满足第一范式的,简记为1NF。满足第一范式是关系模式规范化的最低要求,否则,将有很多基本操作在这样的关系模式中实现不了。

2、第二范式:
如果关系模式R满足第一范式,并且R得所有非主属性都完全依赖于R的每一个候选关键属性,称R满足第二范式,简记为2NF。

3、第三范式:
设R是一个满足第一范式条件的关系模式,X是R的任意属性集,如果X非传递依赖于R的任意一个候选关键字,称R满足第三范式,简记为3NF。

https://zhuanlan.zhihu.com/p/29150809/
http://blog.itpub.net/31556440/viewspace-2642668/

事务特性

事务
原子性、持续性、隔离性、一致性

隔离级别

Read Uncommitted(读取未提交内容) 无法避免脏读
读取未提交的 内容 为脏读
Read Committed(读取提交内容) 无法避免 重复读
Repeatable Read(可重读)——可重复读是MySQL的默认事务隔离级别。 无法避免幻读
在同一个事务中,执行两次同样的sql,第二次的sql会返回之前不存在的行,或者之前出现的数据不见了,这种现象被称之为"幻读"。
快照读取,保证每次读取内容一致
Serializable(可串行化)
在这里插入图片描述

事务传播属性

https://blog.csdn.net/weixin_44263023/article/details/109271867

  • REQUIRED
    业务方法需要在一个事务中运行。如果方法运行时,已经处在一个事务中,那么加入到该事务,否则为自己创建一个新的事务。

  • NOT_SUPPORTED
    声明方法不需要事务。如果方法没有关联到一个事务,容器不会为它开启事务。如果方法在一个事务中被调用,该事务会被挂起,在方法调用结束后,原先的事务便会恢复执行。
    应用场景:有数据操作处理(需要事务)+异步调用(不需要事务,挂起)

  • REQUIRESNEW
    属性表明不管是否存在事务,业务方法总会为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务会被挂起,新的事务会被创建,直到方法执行结束,新事务才算结束,原先的事务才会恢复执行。

  • MANDATORY
    该属性指定业务方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务。如果业务方法在没有事务的环境下调用,容器就会抛出异常。

  • SUPPORTS
    这一事务属性表明,如果业务方法在某个事务范围内被调用,则方法成为该事务的一部分。如果业务方法在事务范围外被调用,则方法在没有事务的环境下执行。

  • Never
    指定业务方法绝对不能在事务范围内执行。如果业务方法在某个事务中执行,容器会抛出异常,只有业务方法没有关联到任何事务,才能正常执行。
    例:应用于报表统计程序

  • NESTED
    如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按REQUIRED属性执行.它使用了一个单独的事务, 这个事务拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效
    事务嵌套,子事务的成功与失败不影响主事务,主事务的成功失败影响子事务

连接池参数
mybatis 缓存

mq

1 你们有使用一些消息中间件么,在什么场景使用
rabbitmq 。主要使用mq 来进行削峰,解耦,异步。例如一些异步调用,如事件上报,异步通知,订单分解等场景使用过。
优点:专注自生业务开发,减少服务压力
缺点:增加稳定性(需要保证mq可用),增加复杂度(保证消息一致性等)
2 各中间件对比得结果
在这里插入图片描述
对比之下 rabbit mq 适合 中小型公司:

  • 1 apche 加持社区活跃、性能稳定
  • 2 后台管理见面友好
  • 3 支持高可用,功能性多
  • 4 延迟低

3 rabbit mq 得特性

[v/host](https://www.cnblogs.com/hopher/p/7644718.html)
vhost 是最小 得权限控制单元,想要控制不同得用户操作不同得exchange ,需要分配不同vhost
channel
exchange
交换机,消息中转,三个主题,topic(按ruteKey 广播消息),fanout(全部广播),direct(匹配key)
headers ,消息体得header 匹配消息
queue
实际存放得地方
rute key
用于匹配消息规则

死信队列

是一种消息交换 和一个队列
1 建立一个队列,绑定死信交换机,设置该队列不自动入队
2 建立一个死信队列路由死信交换机。

持久化

原因在于每个队列和交换器都有durable属性,该属性默认是false,它决定了RabbitMQ是否需要在崩溃或者重启之后重新创建队列或者交换器。
将它设置为true就代表了持久性,在服务器重启之后就会重新持久的创建队列和交换器。
当消息确认后会执行事务提交至磁盘

淘汰策略

消息拒绝
消息过期

4 HA
普通模式

即在多个服务器上部署多个MQ实例, 每台机器一个实例. 创建的每一个queue,只会存在一个MQ实例上. 但是每一个实例都会同步queue的元数据(即queue的标识信息). 当在进行消费的时候, 就算 连接到了其他的MQ实例上, 其也会根据内部的queue的元数据,从该queue所在实例上拉取数据过来.

优点:吞吐高
缺点:
这种方式只是通过集群部署的方式提高了消息的吞吐量,但是并没有考虑到高可用.
镜像模式

要开启镜像集群模式,需要在后台新增镜像集群模式策略. 即要求数据同步到所有的节点.也可以指定同步到指定数量的节点.

优点:
高可用
缺点:
1: 性能开销大: 因为需要进行整个集群内部所有实例的数据同步
2:无法线性扩容: 因为每一个服务器中都包含整个集群服务节点中的所有数据, 这样如果一旦单个服务器节点的容量无法容纳了怎么办?.

RockMQ

分布式mq

Active MQ

传统 的java 开发的mq。

redis

1 你选用得redis 是什么版本

redis 5.0

2 redis 数据结构 及通用场景

链接
String——字符串
Hash——字典
用户缓存:用户性别,用户头像等
List——列表
消息队列等
Set——集合
保证数据唯一性
Sorted Set——有序集合
Sorted Sets是将 Set 中的元素增加了一个权重参数 score,使得集合中的元素能够按 score 进行有序排列

3 redis 持久化

4 HA

  • redis Sentinel 哨兵模式
    哨兵集群互相监听,并对redis 监听,当master 宕机后,从slave 中选举出 master
    在这里插入图片描述
    特点:
    读写分离,且自动切换master 达到高可用
    选举:

5 redis 连接池

6 redis key 策略

7 缓存击穿策略、缓存雪崩避免

8 redisson

9 布隆过滤器

10 redis 集群

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值