30道高频面试题答案

springcloud常用组件

Eureka:服务注册和发现,由Netflix开源

Nacos:服务注册和发现,还有配置管理功能,由阿里巴巴开源

SpringCloudGateway:微服务网关,实现微服务统一路由,统一鉴权,跨域,限流等功能

Feign:微服务之间远程调用,由Netflix开源

Ribbon:负载均衡组件,在网关路由和Feign远程调用过程中的底层都会用到Ribbon做负载均衡。

springboot的常用注解
  • @SpringBootApplication:SpringBoot项目最核心的主键。每个SpringBoot启动类上都有,用于引导SpringBoot项目启动加载。
  • @ComponentScan:用于扫描Spring的组件,并将其放入IOC容器。
  • @Configuration:声明该类为配置类
  • @ConditionOnClass:一般和@Configuration注解同时使用,项目中导入了@ConditionOnClass声明的类,@Configuration中的@Bean才会构建。
  • @ControllerAdvice和@RestControllerAdvice:声明该类为全局异常拦截类。
springboot自动装配
  • 在启动类启动时加载@SpringBootApplication注解
  • 在@SpringBootApplication注解里面包含三个注解:@ComponentScan,@Configuration,@EnableAutoConfiguration
  • @Configuration表明启动类是一个配置类
  • @ComponentScan自动扫描启动类所在目录及子目录在Spring组件,让其实例化
  • @EnableAutoConfiguration注解里面包含AutoConfigurationImportSelector配置类
    在AutoConfigurationImportSelecto配置类中会读取springboot自动配置包中的META-INF的spring.factories文件
  • 该spring.factories文件包含一百多个SpringBoot写好的自动配置类,但这些自动配置类并不是默认生效的,而是根据环境中导入starter启动器依赖及自动配置类上@ConditionalOnClass注解来决定该配置类是否生效。一旦自动配置类生效了,里面@Bean注解会把创建实例放入IOC容器,我们在项目中就可以随时使用@Autowired进行注入并使用
springmvc和springboot的关系

Spring MVC提供了一种轻度耦合的方式来开发web应用。它是Spring的一个模块,是一个web层框架。

Spring Boot实现了自动配置,降低了Spring项目搭建的复杂度。

Spring Boot 只是 辅助你简化Spring项目搭建过程的 ,如果搭建的是Web项目,Web层采用SpringMVC,那么SpringMVC的工作原理还是和原来一样的,并没有因为用了SpringBoot而被改变。

谈谈对spring的理解

Spring的核心组件是:IOC(控制翻转)、DI(依赖注入)和AOP(面向切面编程)

  • IOC意思是控制反转,IOC让对 象的创建不用去new了,可以由spring根据我们提供的配置文件自动生产,我们需要对象的时候, 直接从Spring容器中获取即可。
  • DI的意思是依赖注入,意思是程序在运行时依赖Ioc容器来动态注入对象需要的对象。
  • AOP,一般称为面向切面编程,可以方便将项目中与核心业务无关的代码进行抽取分离,最大程度地解耦。SpringAOP的底层一般采用JDK动态代理或者CGLIB动态代理实现。
spring bean的生命周期
  • Spring启动,查找并加载需要被Spring管理的bean,进行Bean的实例化
  • Bean实例化后对将Bean的引入和值注入到Bean的属性中
  • 如果Bean实现了BeanNameAware接口的话,Spring将Bean的Id传递给setBeanName()方法
  • 如果Bean实现了BeanFactoryAware接口的话,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入
  • 如果Bean实现了ApplicationContextAware接口的话,Spring将调用Bean的setApplicationContext()方法,将bean所在应用上下文引用传入进来。
  • 如果Bean实现了BeanPostProcessor接口,Spring就将调用他们的postProcessBeforeInitialization()方法。
  • 如果Bean 实现了InitializingBean接口,Spring将调用他们的afterPropertiesSet()方法。类似的,如果bean使用init-method声明了初始化方法,该方法也会被调用
  • 如果Bean 实现了BeanPostProcessor接口,Spring就将调用他们的postProcessAfterInitialization()方法。
  • 此时,Bean已经准备就绪,可以被应用程序使用了。他们将一直驻留在应用上下文中,直到应用上下文被销毁。
  • 如果bean实现了DisposableBean接口,Spring将调用它的destory()接口方法,同样,如果bean使用了destory-method 声明销毁方法,该方法也会被调用。
spring的ioc、di、aop分别是什么,ioc和di有什么关系
  • IOC意思是控制反转,IOC让对 象的创建不用去new了,可以由spring根据我们提供的配置文件自动生产,我们需要对象的时候, 直接从Spring容器中获取即可。

  • DI的意思是依赖注入,意思是程序在运行时依赖Ioc容器来动态注入对象需要的对象。

  • AOP,一般称为面向切面编程,可以方便将项目中与核心业务无关的代码进行抽取分离,最大程度地解耦。SpringAOP的底层一般采用JDK动态代理或者CGLIB动态代理实现。

  • IOC侧重于构建对象上的解耦,对象构建交给SpringIOC容器构建。

  • DI侧重于使用对象上的解耦,需要使用什么对象从SpringIOC容器获取。

什么是事务

事务是一组原子操作单元,从数据库角度说,就是一组SQL指令,要么全部执行成功,若因为某个原因其中一条指令执行有错误,则撤销先前执行过的所有SQL指令。更简答的说就是:SQL要么全部执行成功,要么撤销不执行。

事务的四大特性和隔离级别

事务特性

  • 原子性:即不可分割性,事务要么全部被执行,要么就全部不被执行。

  • 一致性:事务的执行使得数据库从一种正确状态转换成另一种正确状态

  • 隔离性:在事务正确提交之前,不允许把该事务对数据的任何改变提供给任何其他事务,

  • 持久性:事务正确提交后,其结果将永久保存在数据库中,即使在事务提交后有了其他故障,事务的处

    理结果也会得到保存。

隔离级别

(1)读未提交(read Uncommited):

在该隔离级别,所有的事务都可以读取到别的事务中未提交的数据,会产生脏读问题,在项目中基本不 怎么用, 安全性太差;

(2) 读已提交(read commited):

这是大多数数据库默认的隔离级别,但是不是MySQL的默认隔离级别;这个隔离级别满足了简单的隔离 要求:一个事务只能看见已经提交事务所做的改变,所以会避免脏读问题; 由于一个事务可以看到别的事务已经提交的数据,于是随之而来产生了不可重复读和虚读等问题

(3) 可重复读(Repeatable read): 这是MySQL的默认隔离级别,它确保了一个事务中多个实例在并发读取数据的时候会读取到一样的数 据;不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。简单的说,幻读指当用户读 取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发生幻读。

(4) 可串行化(serializable): 事物的最高级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每 个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争,一般为了提升程序的吞吐量不会采用这个;

悲观锁和乐观锁的区别和应用场景

什么是悲观锁:

  • 总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上 ,这样别人想拿这个数据就会阻塞直到它拿到锁。

悲观锁的应用场景:

  • 关系数据库的行级锁和表级锁等。

什么是乐观锁:

总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断在此期间别人有没有去更新这个数据。

乐观锁的实现方式:

  • 可以使用版本号机制和CAS算法实现 版本号机制 在数据表中加入一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version 指会加一。当线程A要重新更新数据值时,在读取数据的时候也会读取version值,在提交更新 时,若刚才读取到的version值与当前数据库中的version值相等才更新,否则重新更新操作,直到 更新成功。

悲观锁与乐观锁的应用差别:

  • 乐观锁适用于写少读多的场景。这样可以省去了锁的开销,加 大了系统的整个吞吐量
  • 悲观锁更适合读少写多的场景。因为如果在写多的场景下使用乐观锁,会导致应用会不断的进行重试,这样反倒是降低了性能,所以一般写多的场景下更适合才用悲观锁
redis的数据类型,持久化方式

redis有5种数据类型:

  • String
  • Set
  • List
  • Hash
  • SortedSet

Redis持久化方式:

有两种,分别是RDB和AOF。RDB原理是对整个当前内存数据进行快照备份,体积小。AOF原理是每条操作指令都会持久化到文件,导致文件体积比较大RDB的两次备份时间间隔最短1分钟,时间长,容易导致数据丢失。而AOF默认间隔1秒1次,时间短,数据完整性高!恢复速度上来说,RDB比AOF稍快一些,因为体积小。

redis缓存穿透、雪崩、击穿

什么是缓存雪崩:

缓存雪崩,即缓存同一时间大面积的失效,这个时候又来了一波 请求,结果请求都怼到数据库上,从而导致数据库连接异常。

缓存雪崩解决方案:

1、给缓存的失效时间,加上一个随机值,避免集体失效

2、使用互斥锁,但是该方案吞吐量明显下降了。

3、搭建 redis 集群

缓存击穿和缓存雪崩类似的,雪崩的大面积的key失效,击穿一般是个别key失效,解决办法可以和雪崩一样答。

什么是缓存穿透:

缓存穿透,即黑客故意去请求缓存中不存在的数据,导致所有的请求都怼到数据库上,从而数据库连接 异常。

缓存穿透解决方案:

1、利用互斥锁,缓存失效的时候,先去获得锁,得到锁了, 再去请求数据库。没得到锁,则休眠一段时间重试

2、采用异步更新策略,无论 key 是否取到值,都直接返回, value 值中维护一个缓存失效时间,缓存如果过期,异步起一个线程 去读数据库,更新缓存。

java的基本数据类型

字节型(byte)、短整型(short)、整型(int)、长整型(long)、单精度浮点型(float)和双精度浮点型(double)、布尔型(boolean)、字符型(char)

arraylist和linkedlist

arraylist和linkedlist作用

ArrayList和LinkedList都是实现了List接口的容器类,用于存储一系列的对象引用。他们都可以对元素的

增删改查进行操作。 对于ArrayList**,它在集合的末尾删除或添加元素所用的时间是一致的,但是在列表中间的部分添加或删*除时所用时间就会大大增加**。但是它在根据索引查找元素的时候速度很快

对于LinkedList则相反**,它在插入、删除集合中任何位置的元素所花费的时间都是一样的,但是它查询一个元素的时候却**比较慢。

arraylist和linkedlist区别

1**.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表**结构。

2.对于随机访问的get和set方法,ArrayList要优于LinkedList,因为LinkedList要移动指针。

3.对于新增和删除操作add和remove**,**LinkedList比较占优势,因为ArrayList要移动数据。

hashtable和hashmap

1、HashMap 是非线程安全的,HashTable 是线程安全的。

2、HashMap 的键和值都允许有 null 存在,而 HashTable 则不行

3、因为线程安全的问题,HashMap 效率比 HashTable 的要高

4、Hashtable 是同步的,而 HashMap 不是。因此,HashMap 更适合于单线 程环境,而 Hashtable 适合于多线程环境。一般现在不建议用 HashTable, ① 是 HashTable 是遗留类,内部实现很多没优化和冗余。②即使在多线程环境下, 现在也有同步的 ConcurrentHashMap 替代,没有必要因为是多线程而用HashTable。

jvm内存溢出

在Java程序中,出现内存溢出的原因也有很多,常见的有堆内存溢出,直接内存溢出,永久区/元空间溢出

1)堆内存溢出:

堆溢出这种是最常见的一种,在Java中堆是重要的一个空间,Java的大量对象都是直接在堆上分配的(参考内存分配)。当大量对象占据了堆空间而且都是强引用,使之始终无法被回收,当所有对象大小之和大于参数-Xmx指定的值时,就会出现溢出了。

如何处理

  • 首选检查代码是否存在循环或者死循环,是否能够不断的创建对象。
  • 查看启动参数-Xmx-Xms 设置的堆内存是否过小,不足以加载服务中的所有类,可以适当增加。
  • 检查代码中是否存在数据库查询,没有分页一次性返回大量数据。
  • 还可以通过MAT或者VisualVM工具分析,找到占用大量堆空间的对象,然后做出合理优化。

2)直接内存溢出

这个问题遇到的一般比较少,直接内存不是运行时数据区的一部分。

Java中NIO(New IO)是支持直接使用直接内存的,可以直接获取一块堆外空间使用,而这块空间是直接向操作系统申请的。直接内存的申请速度一般比堆内存慢,但是其访问速度要快于堆内存,所以如果存在可复用且经常被访问的空间,使用直接内存可以提高系统的性能。但是直接内存没有被Java完全托管,使用不当容易出现溢出的问题。

如何处理:

  • 检查程序中使用直接内存的代码是否恰当。
  • 检查参数-Xmx和-XX:MaxDirectMemorySize 的大小是否合理,可以根据实际情况调整其大小。

3)永久区/元空间溢出

这种错误是永久代或者元空间溢出,在jdk1.8之前会出现这种错误,之后hotspot用元空间代替了永久代来存储class信息。如果一个系统在不断的创建新的类(不是对象实例),那么最终会导致元空间溢出的。

如何处理:

  • 增加元空间的大小,设置其对应参数的值 -XX:MaxMetaspaceSize=512m
  • 减少系统需要的类的数量,检查是否有不需要的类并且清除掉。
  • 使用ClassLoader合理的装载各个类,并定期进行回收。
jvm调优

调优目的:

对JVM内容的系统级的调优主要的目的是减少GC的频率和Full GC的次数。

调优步骤:

a. 分析GC日志及dump文件,判断是否需要优化,确定瓶颈问题点;

b. 确定jvm调优量化目标;

c. 确定jvm调优参数(根据历史jvm参数来调整);

d. 调优一台服务器,对比观察调优前后的差异;

e. 不断的分析和调整,知道找到合适的jvm参数配置;

f. 找到最合适的参数,将这些参数应用到所有服务器,并进行后续跟踪。

调优常见参数:
1)设定堆内存大小(比较常用的)
-Xmx:堆内存最大限制。

2)设定新生代大小。 新生代不宜太小,否则会有大量对象涌入老年代
-XX:NewSize:新生代大小
-XX:NewRatio 新生代和老生代占比
-XX:SurvivorRatio:伊甸园空间和幸存者空间的占比

3)设定垃圾回收器算法
年轻代用 -XX:+UseParNewGC
年老代用-XX:+UseConcMarkSweepGC

创建线程的方式
  • 继承Thread类创建线程类
  • 通过Runnable接口创建线程类
  • 通过Callable和Future创建线程
线程池有几种
  1. newCachedThreadPool:创建一个可进行缓存重复利用的线程池

  2. newFixedThreadPool:创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些

线程,线程池中的线程处于一定的量,可以很好的控制线程的并发量

  1. newSingleThreadExecutor:创建一个使用单个 worker 线程的Executor ,以无界队列方式来运行

该线程。线程池中最多执行一个线程,之后提交的线程将会排在队列中以此执行

  1. newSingleThreadScheduledExecutor:创建一个单线程执行程序,它可安排在给定延迟后运行命

令或者定期执行

  1. newScheduledThreadPool:创建一个线程池,它可安排在给定延迟后运行命令或者定期的执行

  2. newWorkStealingPool:创建一个带并行级别的线程池,并行级别决定了同一时刻最多有少个

线程在执行,如不传并行级别参数,将默认为当前系统的CPU核心数*2

mysql的引擎有几种

InnoDB:MySQL默认存储引擎。支持事务。支持行级锁和表级锁。索引采用聚簇索引(索引和数据存储在一个文件,提升查询性能)。

MyISAM:不支持事务。仅仅支持表级锁。索引采用非聚簇索引(索引和数据分开存储,查询性能差一些)。

项目中有没有设计表,都有哪些字段

根据自己项目自由发挥

sql优化

首先开启数据库慢查询日志,定位到查询效率比较低的sql , 找出对应的sql语句并进行分析

1.表设计是否规范,是否符合三范式的标准

(1)第一范式:保证原子性(不可拆分)

(2)第二范式:每张表都有主键

(3)第三范式(每一列都有主键相关)

2.查看数据表中是否存在大量的冗余字段,字段数据类型是否合理

3.尽可能的使用varchar代替char 建表数据类型,能用数值的绝对不用字符存储

4.尽量避免null值,使用默认值替代空值,数值型可以使用0,字符型可以使用空字符串

查看sql语句是否规范:

(1)避免使用关键字:or ,in,not in ,!=,<>,避免使用select *

(2)尽量避免子查询,大部分子查询都可以连接查询

(3)用到or的地方可以使用union去代替实现

(4)用到in的地方可以使用exists去代替

分析sql的索引是否可以用上:

(1) explain查询sql的执行计划,重点关注的几个列就是,type是不是全表扫描

(2)看一下索引是否能够用的上,主要看key使用的是哪个索引

(3)看一下rows扫描行数是不是很大

eureka和nacos的区别

1)Nacos可以实现服务注册发现,也可以做配置管理;Eureka只能做服务注册发现。
2)Nacos临时实例心跳不正常会被剔除,非临时实例(永久实例)则不会被剔除;而Eureka只能注册临时实例,实例失效会被剔除(Eureka不支持永久实例)
3)Nacos支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式;而Eureka只有心跳模式;
4)Nacos支持服务列表变更的消息主动通知模式,服务列表更新更及时,减少服务调用失败的机率;而Eureka采用被动定时服务列表拉取更新;

项目中用到什么设计模式,单例有几种
  • 单例设计模式
  • 代理模式
  • 装饰者
  • 工厂模式
  • 建造者模式

单例有懒汉式 和 饿汉式

SpringMVC的执行流程
  1. 用户发送请求到前端控制器(DispatcherServlet)

  2. 前端控制器(DispatcherServlet)收到请求调用处理器映射器(HandlerMapping),去查找处理器 (Handler)

  3. 处理器映射器(HandlerMapping)找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。

  4. 前端控制器(DispatcherServlet)调用处理器映射器(HandlerMapping)

  5. 处理器适配器(HandlerAdapter)去调用自定义的处理器类(Controller,也叫后端控制器)。 自定义的处理器类(Controller,也叫后端控制器)将得到的参数进行处理并返回结果给处理器映 射器(HandlerMapping)

  6. 处理器适配器(HandlerAdapter)将得到的结果返回给前端控制器(DispatcherServlet)

  7. DispatcherServlet(前端控制器)将ModelAndView传给视图解析器(ViewReslover)

  8. 视图解析器(ViewReslover)将得到的参数从逻辑视图转换为物理视图并返回给前端控制器

(DispatcherServlet)

  1. 前端控制器(DispatcherServlet)调用物理视图进行渲染并返回

  2. 前端控制器(DispatcherServlet)将渲染后的结果返回

mybatis的$和#

1, #是一个占位符,$是拼接符。

#是一个占位符,$是拼接符。

**(1)**使用#parameterName方式引用参数的时候,Mybatis会把传入的参数当成是一个字符串,自动添加双引号。

(2)使用$parameterName引用参数时,不做任何处理,直接将值拼接在sql语句中。

2,使用 # 能够防止sql注入,$不能避免注入攻击。

#的方式引用参数,mybatis会先对sql语句进行预编译,然后再引用值,能够有效防止sql注入,提高安全性。$的方式引用参数,sql语句不进行预编译。

为什么要用es,es的倒排索引是什么

为什么用ES?

1)MySQL的海量数据时,搜索效率比较低,而Elasticsearch采用倒排索引法检测数据,从而效率更高。
2)MySQL的搜索功能比较弱,只有like这种模糊搜索,而Elasticsearch拥有大量复杂搜索的API,更加适合数据搜索场景

ES的倒排索引:

1)首先,Elasticsearch将文档数据进行索引构建。将文档数据需要分词的字段内容使用分词器进行分词,并记录每个词条和原文档的出现位置和出现频率等信息,构建出文档的索引库。
2)然后,用户搜索时,可以对关键词进行分词,使用分词后词条来匹配索引库,在索引库匹配到记录后,通过文档位置频率信息,反查具体的文档数据。

rabbitmq/rocketmq、kafka的了解

RabbitMQ:
优势:
1)支持语言非常广
2)稳定性很好,采用Erlang语言开发
3)吞吐量不算低,万级
4)RabbitMQ官方提供7种消息发送模式,开发者轻松选择合适的模式进行开发即可
缺点:
1)采用Erlang,太小众,研究源码很难

Kafka:
优势:
1)高吞吐量,百万级
2)稳定性好,采用zookeeper进行注册(Zookeep采用CP模式,高一致模式)
3)可以应用在大数据数据处理领域(KafkaStream)
缺点:
1)支持的开发语言比较少
2)耦合zk,依赖zookeeper进行注册

spring中@Autowired和@Resource的区别
  • @Autowired是Spring的,@Resource是javax包下的
  • @Autowired默认按类型匹配,@Resource默认按名称匹配
  • @Autowired和@Qualifier一起用,@Resource单独用
mysql索引数据结构,为什么用的是b+树不用红黑树

1)Hash哈希,只适合等值查询,不适合范围查询
2)一般二叉树,可能会特殊化为一个链表,相当于全表扫描
3)红黑树,是一种特化的平衡二叉树,MySQL 数据量很大的时候,索引的体积也会很大,内存放不下的而从磁盘读取,树的层次太高的话,读取磁盘的次数就多了。
4)B树在范围查询时,存在回旋查找的问题,导致性能不高。B+树叶子节点是有序链表,更有利于范围查询。

综上所述,MySQL的索引数据结构最适合采用B+树来实现。而且为了提高索引前后范围检索效率,MySQL改造了传统的B+树,形成了双向链指针。

mysql和redis如何保证数据一致性

1)缓存延迟双删

  • 先删除缓存

  • 写数据库

休眠 500 毫秒,然后删除缓存

这样,读取脏数据的时间最多只有500毫秒。关键是如何确定睡眠时间? 延迟时间的目的是为了保证读请求结束,写请求可以删除读请求引起的缓存脏数据。 因此,我们需要自己评估项目的数据读取业务逻辑的耗时,在读取时间的基础上加上几百毫秒的延迟时间。

2)删除缓存重试机制

缓存删除失败怎么办?比如延迟双删的第二次删除失败,说明脏数据无法删除。 使用重试机制保证缓存删除成功。 比如重试3次,失败3次,就会将日志记录到数据库中,并发出警告进行人工干预。 在高并发场景下,重试最好采用异步方式,比如向MQ中间件发送消息,实现异步解耦。

3)读取 bin-log 异步删除

  • 更新数据库。

  • 数据库会将操作信息记录在bin-log日志中。

  • 使用 canal 订阅 bin-log 日志获取目标数据和密钥。

  • 缓存删除系统获取canal数据,解析目标key,尝试删除缓存。

  • 如果删除失败,将消息发送到消息队列。

  • 缓存删除系统再次从消息队列中获取数据,再次执行删除操作。

扩展具难面试题:
1、多线程在项目中的使用场景
  • 多线程批量导入MySQL数据到ES
  • CRM采用异步线程执行多个统计SQL
2、项目中是否有高并发导致的问题
  • mysql和redis数据一致性问题
  • ThreadLocal存储登录用户ID
3、线程不安全导致的问题

共享数据安全问题(分布式锁解决)

4、并发安全的集合有哪些

ConcurrentHashMap

CopyOnWriteArrayList

5、sql慢查询优化、行锁、表锁

1)避免回表查询

2)联合索引

3)避免索引失效

4)使用执行计划优化

表级锁:对整个表记录锁定,一个事务修改表数据的时候,另一个事务无法修改表数据。

语法:
lock table tb_user read local;
update tb_user ssss
insert into tb_user
unlock;

特点:锁定范围比较大,比较影响性能
应用场景:在数据迁移场景下使用

行级锁:对表的某条(某些)记录锁定。

行级锁分为 共享锁 和 排他锁

共享锁:一个事务在修改记录的时候,另一个事务无法修改记录,但是可以读取。
update tb_user set sex=‘女’ where id = 1 lock in share mode;

排他锁:一个事务在查询/修改记录的时候,另一个事务无法修改和读取数据。
select * from tb_user where id = 1 for update;

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值