中級面试题总结

1、mysql的事务等级
读未提(READ_UNCOMMITTED):一个事务还没提交时,它做的变更就能被别的事务看到。最低的事务隔离级别,任何情况都无法保证。

读已提交(READ_COMMITTED):保证一个事物提交后才能被另外一个事务读取,另外一个事务不能读取该事物未提交的数据。可避免脏读的发生,但是可能会造成不可重复读。

可重复读(REPEATABLE_READ):多次读取同一范围的数据会返回第一次查询的快照,即使其他事务对该数据做了更新修改。事务在执行期间看到的数据前后必须是一致的。但如果这个事务在读取某个范围内的记录时,其他事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行,这就是幻读。

串行化(SERIALIZABLE):写会加写锁,读会加读锁。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。这是最高的事务隔离级别,也是最可靠的级别,但是花费的代价也是最高的。该事物隔离级别可以做到事务 100% 隔离,可避免脏读、不可重复读、幻读的发生。

2、mysql各个引擎的优缺点和区别
InnoDB是一个健壮的事务型存储引擎MySQL 5.6.版本以后InnoDB就是作为默认的存储引擎。
InnoDB还引入了行级锁定和外键约束,在以下场合下,使用InnoDB是最理想的选择:
更新密集的表。InnoDB存储引擎特别适合处理多重并发的更新请求。
事务。InnoDB存储引擎是支持事务的标准MySQL存储引擎。
自动灾难恢复。与其它存储引擎不同,InnoDB表能够自动从灾难中恢复。
外键约束。MySQL支持外键的存储引擎只有InnoDB。
支持自动增加列AUTO_INCREMENT属性。

使用MySQL Memory存储引擎的出发点是速度。为得到最快的响应时间,采用的逻辑存储介质是系统内存。虽然在内存中存储表数据确实会提供很高的性能,但当mysqld守护进程崩溃时,所有的Memory数据都会丢失。获得速度的同时也带来了一些缺陷。它要求存储在Memory数据表里的数据使用的是长度不变的格式,这意味着不能使用BLOB和TEXT这样的长度可变的数据类型,VARCHAR是一种长度可变的类型,但因为它在MySQL内部当做长度固定不变的CHAR类型,所以可以使用。

MERGE存储引擎是一组MyISAM表的组合,这些MyISAM表结构必须完全相同,尽管其使用不如其它引擎突出,但是在某些情况下非常有用。说白了,Merge表就是几个相同MyISAM表的聚合器;Merge表中并没有数据,对Merge类型的表可以进行查询、更新、删除操作,这些操作实际上是对内部的MyISAM表进行操作。Merge存储引擎的使用场景。对于服务器日志这种信息,一般常用的存储策略是将数据分成很多表,每个名称与特定的时间端相关。例如:可以用12个相同的表来存储服务器日志数据,每个表用对应各个月份的名字来命名。当有必要基于所有12个日志表的数据来生成报表,这意味着需要编写并更新多表查询,以反映这些表中的信息。与其编写这些可能出现错误的查询,不如将这些表合并起来使用一条查询,之后再删除Merge表,而不影响原来的数据,删除Merge表只是删除Merge表的定义,对内部的表没有任何影响。

Rchive是归档的意思,在归档之后很多的高级功能就不再支持了,仅仅支持最基本的插入和查询两种功能。在MySQL 5.5版以前,Archive是不支持索引,但是在MySQL 5.5以后的版本中就开始支持索引了。Archive拥有很好的压缩机制,它使用zlib压缩库,在记录被请求时会实时压缩,所以它经常被用来当做仓库使用。

3、ClickHouse特性
完备的DBMS功能
列式存储与数据压缩
向量化执行引擎
关系模型与SQL查询
多样化的表引擎
多线程与分布式
数据分片与分布式查询

4、ES的创建和使用
1> 集群和节点
一个es集群是由一个或多和es节点组成的集合
每一个集群都有一个名字, 如之前的wali
每个节点都有自己的名字, 如之前的master, slave1, slave2
节点是可以存储数据, 参与索引数据等的独立服务

2> 索引(类似于数据库里面的database)
索引是含有相同属性的文档集合
索引在es中是通过一个名字来识别的, 必须是英文字母小写, 且不含中划线

3> 类型(相当于sql中的table)
一个索引可以定义一个或多个类型, 文档必须属于一个类型

4> 文档(相当于sql中的一行记录)
文档是可以被索引的基本数据单位

5> 分片
每个索引都有多个分片, 每个分片都是一个luncene索引
分片的好处: 分摊索引的搜索压力, 分片还支持水平的拓展和拆分以及分布式的操作, 可以提高搜索和其他处理的效率

6> 备份
拷贝一个分片就完成了分片的备份
备份的好处: 当主分片失败或者挂掉, 备份就可以代替分片进行操作, 进而提高了es的可用性, 备份的分片还可以进行搜索操作, 以分摊搜索的压力.
ES在创建索引时, 默认创建5个分片, 一份备份, 可以修改, 分片的数量只能在创建索引的时候指定, 索引创建后就不能修改分片的数量了, 而备份是可以动态修改的。

5、Hashmap和HashTable和Concurrenthashmap的区别
HashTable
底层数组+链表实现,无论key还是value都不能为null,线程安全,实现线程安全的方式是在修改数据时锁住整个HashTable,效率低,ConcurrentHashMap做了相关优化
初始size为11,扩容:newsize = olesize2+1
计算index的方法:index = (hash & 0x7FFFFFFF) % tab.length
HashMap
底层数组+链表实现,可以存储null键和null值,线程不安全
初始size为16,扩容:newsize = oldsize
2,size一定为2的n次幂
扩容针对整个Map,每次扩容时,原来数组中的元素依次重新计算存放位置,并重新插入
插入元素后才判断该不该扩容,有可能无效扩容(插入后如果扩容,如果没有再次插入,就会产生无效扩容)
当Map中元素总数超过Entry数组的75%,触发扩容操作,为了减少链表长度,元素分配更均匀
计算index方法:index = hash & (tab.length – 1)

HashMap的初始值还要考虑加载因子:
哈希冲突:若干Key的哈希值按数组大小取模后,如果落在同一个数组下标上,将组成一条Entry链,对Key的查找需要遍历Entry链上的每个元素执行equals()比较。
加载因子:为了降低哈希冲突的概率,默认当HashMap中的键值对达到数组大小的75%时,即会触发扩容。因此,如果预估容量是100,即需要设定100/0.75=134的数组大小。
空间换时间:如果希望加快Key查找的时间,还可以进一步降低加载因子,加大初始大小,以降低哈希冲突的概率。
HashMap和Hashtable都是用hash算法来决定其元素的存储,因此HashMap和Hashtable的hash表包含如下属性:

容量(capacity):hash表中桶的数量
初始化容量(initial capacity):创建hash表时桶的数量,HashMap允许在构造器中指定初始化容量
尺寸(size):当前hash表中记录的数量
负载因子(load factor):负载因子等于“size/capacity”。负载因子为0,表示空的hash表,0.5表示半满的散列表,依此类推。轻负载的散列表具有冲突少、适宜插入与查询的特点(但是使用Iterator迭代元素时比较慢)
除此之外,hash表里还有一个“负载极限”,“负载极限”是一个0~1的数值,“负载极限”决定了hash表的最大填满程度。当hash表中的负载因子达到指定的“负载极限”时,hash表会自动成倍地增加容量(桶的数量),并将原有的对象重新分配,放入新的桶内,这称为rehashing。

HashMap和Hashtable的构造器允许指定一个负载极限,HashMap和Hashtable默认的“负载极限”为0.75,这表明当该hash表的3/4已经被填满时,hash表会发生rehashing。
“负载极限”的默认值(0.75)是时间和空间成本上的一种折中:
较高的“负载极限”可以降低hash表所占用的内存空间,但会增加查询数据的时间开销,而查询是最频繁的操作(HashMap的get()与put()方法都要用到查询)
较低的“负载极限”会提高查询数据的性能,但会增加hash表所占用的内存开销。

ConcurrentHashMap
底层采用分段的数组+链表实现,线程安全
通过把整个Map分为N个Segment,可以提供相同的线程安全,但是效率提升N倍,默认提升16倍。(读操作不加锁,由于HashEntry的value变量是 volatile的,也能保证读取到最新的值。)
Hashtable的synchronized是针对整张Hash表的,即每次锁住整张表让线程独占,ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术
有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而而不仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁
扩容:段内扩容(段内元素超过该段对应Entry数组长度的75%触发扩容,不会对整个Map进行扩容),插入前检测需不需要扩容,有效避免无效扩容。

6、cookie和session区别
会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话。常用的会话跟踪技术是Cookie与Session。Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端记录信息确定用户身份。

7、ioc和aop的区别
IOC(Inverse of Control):控制反转,也可以称为依赖倒置。
所谓依赖,从程序的角度看,就是比如A要调用B的方法,那么A就依赖于B,反正A要用到B,则A依赖于B。所谓倒置,你必须理解如果不倒置,会怎么着,因为A必须要有B,才可以调用B,如果不倒置,意思就是A主动获取B的实例:B b = new B(),这就是最简单的获取B实例的方法(当然还有各种设计模式可以帮助你去获得B的实例,比如工厂、Locator等等),然后你就可以调用b对象了。所以,不倒置,意味着A要主动获取B,才能使用B;到了这里,就应该明白了倒置的意思了。倒置就是A要调用B的话,A并不需要主动获取B,而是由其它人自动将B送上门来。

AOP:即面向切面编程
从Spring的角度看,AOP最大的用途就在于提供了事务管理的能力。

8、spring的循环依赖如何解决
Spring整个解决循环依赖问题的实现思路已经比较清楚了。对于整体过程,读者朋友只要理解两点:
Spring是通过递归的方式获取目标bean及其所依赖的bean的;
Spring实例化一个bean的时候,是分两步进行的,首先实例化目标bean,然后为其注入属性。
结合这两点,也就是说,Spring在实例化一个bean的时候,是首先递归的实例化其所依赖的所有bean,直到某个bean没有依赖其他bean,此时就会将该实例返回,然后反递归的将获取到的bean设置为各个上层bean的属性的。

9、线程的生命周期
新建:就是刚使用new方法,new出来的线程;

就绪:就是调用的线程的start()方法后,这时候线程处于等待CPU分配资源阶段,谁先抢的CPU资源,谁开始执行;

运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态,run方法定义了线程的操作和功能;

阻塞:在运行状态的时候,可能因为某些原因导致运行状态的线程变成了阻塞状态,比如sleep()、wait()之后线程就处于了阻塞状态,这个时候需要其他机制将处于阻塞状态的线程唤醒,比如调用notify或者notifyAll()方法。唤醒的线程不会立刻执行run方法,它们要再次等待CPU分配资源进入运行状态;

销毁:如果线程正常执行完毕后或线程被提前强制性的终止或出现异常导致结束,那么线程就要被销毁,释放资源。

10、Java类加载机制
加载
类加载过程的一个阶段,ClassLoader通过一个类的完全限定名查找此类字节码文件,并利用字节码文件创建一个class对象。

验证
目的在于确保class文件的字节流中包含信息符合当前虚拟机要求,不会危害虚拟机自身的安全,主要包括四种验证:文件格式的验证,元数据的验证,字节码验证,符号引用验证。

准备
为类变量(static修饰的字段变量)分配内存并且设置该类变量的初始值,(如static int i = 5 这里只是将 i 赋值为0,在初始化的阶段再把 i 赋值为5),这里不包含final修饰的static ,因为final在编译的时候就已经分配了。这里不会为实例变量分配初始化,类变量会分配在方法区中,实例变量会随着对象分配到Java堆中。

解析
这里主要的任务是把常量池中的符号引用替换成直接引用

初始化
这里是类记载的最后阶段,如果该类具有父类就进行对父类进行初始化,执行其静态初始化器(静态代码块)和静态初始化成员变量。(前面已经对static 初始化了默认值,这里我们对它进行赋值,成员变量也将被初始化)

11、Mybatis工作流程
1、加载mybatis全局配置文件(数据源、mapper映射文件等),解析配置文件,MyBatis基于XML配置文件生成Configuration,和一个个MappedStatement(包括了参数映射配置、动态SQL语句、结果映射配置),其对应着<select | update | delete | insert>标签项。

2、SqlSessionFactoryBuilder通过Configuration对象生成SqlSessionFactory,用来开启SqlSession。

3、SqlSession对象完成和数据库的交互:
a、用户程序调用mybatis接口层api(即Mapper接口中的方法)
b、SqlSession通过调用api的Statement ID找到对应的MappedStatement对象
c、通过Executor(负责动态SQL的生成和查询缓存的维护)将MappedStatement对象进行解析,sql参数转化、动态sql拼接,生成jdbc Statement对象
d、JDBC执行sql。

e、借助MappedStatement中的结果映射关系,将返回结果转化成HashMap、JavaBean等存储结构并返回。

12、oauth2使用详解
第一步:用户打开客户端以后,客户端要求用户给予授权。
第二步:用户同意给予客户端授权。
第三步:客户端使用上一步获得的授权,向认证服务器申请令牌。
第四步:认证服务器对客户端进行认证以后,确认无误,同意发放令牌。
第五步:客户端使用令牌,向资源服务器申请获取资源。
第六步:资源服务器确认令牌无误,同意向客户端开放资源。

13、什么是MVCC

MVCC,全称Multi-Version Concurrency Control,即多版本并发控制。MVCC是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存。
多版本控制: 指的是一种提高并发的技术。最早的数据库系统,只有读读之间可以并发,读写,写读,写写都要阻塞。引入多版本之后,只有写写之间相互阻塞,其他三种操作都可以并行,这样大幅度提高了InnoDB的并发度。在内部实现中,与Postgres在数据行上实现多版本不同,InnoDB是在undolog中实现的,通过undolog可以找回数据的历史版本。找回的数据历史版本可以提供给用户读(按照隔离级别的定义,有些读请求只能看到比较老的数据版本),也可以在回滚的时候覆盖数据页上的数据。在InnoDB内部中,会记录一个全局的活跃读写事务数组,其主要用来判断事务的可见性。
MVCC是一种多版本并发控制机制。
MVCC在MySQL InnoDB中的实现主要是为了提高数据库并发性能,用更好的方式去处理读-写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读。

14、HashMap的put方法内部流程
JDK7中HashMap采用的是位桶+链表的方式,即我们常说的散列链表的方式,而JDK8中采用的是位桶+链表/红黑树。
put方法源码
在这里插入图片描述
put方法的源码内只有简单的一行代码,返回了putVal方法,它的参数内调用了hash方法。

hash方法源码
在这里插入图片描述
当key等于null时,直接返回0。否则返回key的hashCode值与自己的高16位进行异或运算。h>>>16用来取出h的高16位,>>>无符号右移。
putVal方法源码

 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
                   //创建一个Node数组(哈希桶),节点p,数组长度n,数组下标i
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        //判断数组是否为空,若空,给它一个默认数组大小
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
            //如果数组中对应下标的元素为null,则直接创建Node存入
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            //若对应下标元素不为null,判断key是否相同
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                //相同时,覆盖
                e = p;
                //不同时,判断该位置是链表还是红黑树
            else if (p instanceof TreeNode)
            //若是红黑树,将Node存入红黑树
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
            //若是链表,则遍历链表
            //注意,该循环条件写在了循环体内。binCount指当前下标,循环条件是第二个if语句。
                for (int binCount = 0; ; ++binCount) {
                //遍历到最后一位,存入
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        判断链表长度是否超过8
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        //将链表转为红黑树
                            treeifyBin(tab, hash);
                        break;
                    }
                    //若链表中有重复的key,则替换
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                        // e 新的节点,p 链表中已有的节点
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
            //把e的value记录下来
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                //将被替换的值返回
                return oldValue;
            }
        }
        // modCount 哈希映射被修改的次数
        ++modCount;
        // size 数组大小   threshold=capacity*loadFactor
        if (++size > threshold)
        //数组大小大于12时,开始扩容
            resize();
        afterNodeInsertion(evict);
        //若没有重复的key,返回null
        return null;
    }


所以,整个put操作需要判断2次数组是否需要扩容!
第一次:

当链表长度为8时,切记不是立即就转红黑树!很多人都认为它达到8,立刻就转红黑树。虽然它此刻会进到 treeifyBin(tab, hash)方法里面,但是进入它的源码,
在这里插入图片描述
可以发现它会先判断数组长度是否已经达到了64。若没,则会进行数组扩容;若达到64,才开始链表转红黑树。
第二次:
数组大小大于 threshold = capacity * loadFactor = 64 * 0.75 = 12 时,数组开始扩容!
数组下标的确定:
最后,tab[i = (n - 1) & hash]),数组下标是根据key计算出的hash值跟当前数组的最大索引进行按位与运算得来,不是key的hash值,也不是取余运算。

15、封装,继承,多态
一、封装
​ 封装(Encapsulation)是面向对象方法的重要原则,就是把对象的属性和操作(或服务)结合为一个独立的整体,并尽可能隐藏对象的内部实现细节。
(1)将类的某些信息隐藏在类的内部,不允许外部程序进行直接的访问调用。
(2)通过该类提供的方法来实现对隐藏信息的操作和访问。
(3)隐藏对象的信息。
(4)留出访问的对外接口。
​ 举个比较通俗的例子,比如我们的USB接口。如果我们需要外设且只需要将设备接入USB接口中,而内部是如何工作的,对于使用者来说并不重要。而USB接口就是对外提供的访问接口。
​ 说了这么多,那为什么使用封装?
封装的特点
对成员变量实行更准确的控制。
封装可以隐藏内部程序实现的细节。
良好的封装能够减少代码之间的耦合度。
外部成员无法修改已封装好的程序代码。
方便数据检查,有利于保护对象信息的完整性,同时也提高程序的安全性。
便于修改,体高代码的可维护性。
封装的使用
使用private修饰符,表示最小的访问权限。
对成员变量的访问,统一提供setXXX,getXXX方法。

二、继承
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。当然,如果在父类中拥有私有属性(private修饰),则子类是不能被继承的。

三、多态
多态是同一个行为具有多个不同表现形式或形态的能力。
多态的体现形式:继承、父类引用指向子类、重写、重载。

重写遵循“三同一大一小”原则
“三同”:即方法名相同,形参列表相同,返回值类型相同;
“一小”:子类方法声明抛出的异常比父类方法声明抛出的异常更小或者相等;
“一大”:子类方法的访问修饰符应比父类方法更大或相等。

重载对返回类型没有特殊的要求,不能根据返回类型进行区分。

16、枚举类能被继承吗,能实现接口吗
java中枚举类不可以被继承。
java中枚举类是可以实现一个或多个接口的。

使用关键字enum来定义枚举类,枚举类是一个特殊的类,大部分功能和普通类是一样的,区别为:

● 枚举类继承了java.lang.Enum类,而不是默认的Object类。而java.lang.Enum类实现了java.lang.Serializable和java.lang.Comparable接口。

● 非抽象的枚举类默认会使用final修饰,因此不能派生子类

枚举类使用enum定义后在编译后默认继承了java.lang.Enum类,而不是普通的继承Object类。enum声明类继承了Serializable和Comparable两个接口。且采用enum声明后,该类会被编译器加上final声明(同String),故该类是无法继承的。

枚举类的内部定义的枚举值就是该类的实例(且必须在第一行定义,当类初始化时,这些枚举值会被实例化)。

17、什么是红黑树
红黑树是带有着色性质的二叉查找树。

性质如下:
① 每一个节点或者着成红色或者着成黑色。
② 根节点为黑色。
③ 每个叶子节点为黑色。(指的是指针指向为NULL的叶子节点)
④ 如果一个节点是红色的,那么它的子节点必须是黑色的。
⑤ 从一个节点到一个NULL指针的每一条路径必须包含相同数目的黑色节点。
推论: 有n个节点的红黑树的高度最多是2log(N+1) 。

18、悲观锁和乐观锁
悲观锁是基于一种悲观的态度类来防止一切数据冲突,它是以一种预防的姿态在修改数据之前把数据锁住,然后再对数据进行读写,在它释放锁之前任何人都不能对其数据进行操作,直到前面一个人把锁释放后下一个人数据加锁才可对数据进行加锁,然后才可以对数据进行操作,一般数据库本身锁的机制都是基于悲观锁的机制实现的;
特点:可以完全保证数据的独占性和正确性,因为每次请求都会先对数据进行加锁, 然后进行数据操作,最后再解锁,而加锁释放锁的过程会造成消耗,所以性能不高;

乐观锁是对于数据冲突保持一种乐观态度,操作数据时不会对操作的数据进行加锁(这使得多个任务可以并行的对数据进行操作),只有到数据提交的时候才通过一种机制来验证数据是否存在冲突(一般实现方式是通过加版本号然后进行版本号的对比方式实现);
特点:乐观锁是一种并发类型的锁,其本身不对数据进行加锁通而是通过业务实现锁的功能,不对数据进行加锁就意味着允许多个请求同时访问数据,同时也省掉了对数据加锁和解锁的过程,这种方式因为节省了悲观锁加锁的操作,所以可以一定程度的的提高操作的性能,不过在并发非常高的情况下,会导致大量的请求冲突,冲突导致大部分操作无功而返而浪费资源,所以在高并发的场景下,乐观锁的性能却反而不如悲观锁。

19、nacos集群同步策略
(1)直连模式是部署上最简单、最容易理解的一种模式。
(2)VIP(Virtual IP) 模式可以很好的解决直连模式 IP 变化所带来的应用批量修改的问题。
(3)地址服务器模式,可能大家对这个词会感到陌生,因为地址服务器的概念主要在阿里内部比较普及,也是阿里中间件使用最广的一种寻址模式。但是在开源领域,鲜有人会提及,但对于 Nacos 部署模式而言,地址服务器模式是除了 VIP 模式之外,另外一个生产可用的推荐部署方式。

熟悉 RPC 应该能够很好地对 VIP 模式和地址服务器模式做一个类比:
VIP 模式是类似 DNS 的服务端负载均衡技术
地址服务器是类似服务发现机制的客户端负载均衡技术

20、springcloud的负载均衡
负载均衡分为两种,一种是服务端负载均衡,一种是客户端负载均衡。
服务端负载均衡:分为两种,一种是硬件负载均衡,还有一种是软件负载均衡。

使用Ribbon组件做负载均衡:
Ribbon利用了RestTemplate的拦截器(接口是ClientHttpRequestInterceptor)机制,在拦截器中实现的负载均衡。负载均衡的基本实现就是利用从「服务注册中心」获取可用的服务地址列表,然后通过一定算法负载,决定使用哪一个服务地址来进行HTTP调用。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值