2024 Java核心知识点精心整理(全是精华),2024年腾讯Java高级面试题及答案

垃圾回收是java管理内存的一种机制,作用是清理无用对象避免内存泄漏,gc主要发生在java堆上,而堆可以细分为新生代和老年代(分代是为了提高gc效率,其实不分代也可以完成gc,只不过gc机制会对堆的所有区域进行扫描,浪费资源),新生代还可以细分为三个虚拟的区,Eden区、FromSurvivor区、ToSurvivor区,一开始对象都在Eden区,Eden区的对象经过一次新生代gc(复制算法)后若还能存活,就会移动到survivor区(ToSurvivor区),在此次新生代gc时,在survivor发生的改变就是,From区中的对象会根据年龄来决定去留,达到阈值,会移动到老年代,没达到就移动到To区,经过此次新生代gc,Eden和From区都已被清空,From区和To区会互换;

新生代gc(Minor GC)触发时机:当Eden区没有足够空间进行分配时;

老年代gc(Major GC/Full GC)触发时机:当老年代中没有足够的内存空间来存放对象时,虚拟机会发起一次Major GC/Full GC。只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小就会进行Minor GC,否则将进行Full GC。

25、如何优化JVM频繁Minor GC?

最简单的就是适当扩大堆内存太小

26、简述类加载机制,什么是双亲委派?

1、加载:将源文件(代码)编译成.class文件,传递给类加载器

2、验证:类加载器拿到这些字节码文件后开始执行检查(通过本地类库),确保加载进来的字节流格式符合java虚拟机要求

3、准备:对类变量(不是实例变量)分配内存,并且给一个默认初始化的值(0或者null)

4、解析:将常量池中的符号引用加载成直接引用(如string str = new string(“123”),str就是符号引用,123是直接引用)

5、初始化:对类的静态变量初始化为指定的值,执行静态代码块

双亲委派机制:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此。

因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。

27、synchronized底层实现原理?它与lock相比有什么优缺点?

首先那些说看过synchronized源码的基本都是大聪明,synchronized根本点不进去,想弄懂它的实现原理,我们只能通过看编译好的字节码文件

原理:

基于对象的监视器(ObjectMonitor),我们在字节码文件里面可以看到,在同步方法执行前后,有两个指令,方法前monitorenter,方法后monitorexit;

关于字节码一两句话说不清楚,我单独写了篇synchronized底层实现原理,保证你五分钟看懂

与lock对比:

1、synchronized不需要手动释放锁,lock需要在锁用完后进行unlock;

2、synchronized只能是默认的非公平锁,lock可以指定使用公平锁或者非公平锁;

3、lock提供的Condition(条件)可以指定唤醒哪些线程,而synchronized只能随机唤醒一个或者全部唤醒;

28、jvm指令重排原因?怎么避免?

原因:计算机内存操作速度远慢于CPU运行速度,所以就造成CPU空置,为了将提高CPU利用率,虚拟机会按照自己的一些规则会跳过执行慢的代码,去执行快的代码(即对代码重新排序),从而提升jvm的整体性能。

怎么避免:给关键的代码加上volatile关键字,所谓关键,就是会被执行顺序影响结果。

volatile关键字的三个特征是:线程可见、不具备原子性、禁止指令重排,volatile 的读性能消耗与普通变量几乎相同,但是写操作稍慢,因为它需要在本地代码中插入许多内存屏障指令来保证处理器不发生乱序执行。

29、ReentrantLock是什么?有什么用?怎么用?和synchronized的区别?

ReentrantLock是Lock的一个子类

在这里插入图片描述

作用:

用来给资源加锁,避免高并发造成的数据异常问题;

使用:

public void lockTest() {

int i = 0;

//创建lock实例,可传参数true或者false,表示是否是公平锁,默认是非公平锁

ReentrantLock lock = new ReentrantLock();

//在需要保证同步的代码前lock

lock.lock();

i++;

//代码后unlock

lock.unlock();

}

与synchronized主要区别,lock需要手动释放锁,可以指定公平锁或者非公平锁

这里只是为了便于理解,实际使用时我们会把代码块try起来,把unlock丢在finally里面

30、volatile关键字解决了什么问题?实现原理是什么?

解决了什么问题:

1、保证了变量的可见性

2、禁止指令重排

比如i=i+1,单线程操作没问题,如果使用多线程,比如两个线程,执行这段代码后(i初始值为0),i应该等于2,但是如果不用volatile修饰变量i,结果会等于1,初始时,两个线程分别读取i的值存入各自所在的CPU的高速缓存当中,然后线程1进行加1操作,然后把i的最新值1写入到内存。此时线程2的高速缓存当中i的值还是0,进行加1操作之后,i的值为1,然后线程2把i的值写入内存。

实现原理

基于内存屏障,关于内存屏障,搞java开发的同学在开发中不可能接触到,所以不用关心太多,知道内存屏障有什么作用,面试官问到你能唬住他就行了,因为面试官自己也不懂

(1)它确保指令重排序时不会把其后面的指令排到内存屏障前面,也不会把前面的指令排到内存屏障后面,总之一句话,他能保证指令按照我们希望的顺序执行

(2)它会强制将对缓存的修改操作立即写入主存,使得其它线程能立马发现;

31、ThreadLocal实现原理?

ThreadLocal中文名叫线程变量,它底层维护了一个map,key就是当前的ThreadLocal对象(可以理解为当前执行该段代码的线程),value就是你set的值,这个map保证了各个线程的数据互不干扰

想进一步了解ThreadLocal的同学请移步我凭ThreadLocal唬住了京东面试官

32、线程池实现原理?

要说实现原理,必须得把线程池的几个参数彻底搞懂,不要死记硬背

1、corePoolSize(必填):核心线程数。

2、maximumPoolSize(必填):最大线程数。

3、keepAliveTime(必填):线程空闲时长。如果超过该时长,非核心线程就会被回收。

4、unit(必填):指定keepAliveTime的时间单位。常用的有:TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)、TimeUnit.MINUTES(分)。

5、workQueue(必填):任务队列。通过线程池的execute()方法提交的Runnable对象将存储在该队列中。

6、threadFactory(可选):线程工厂。一般就用默认的。

7、handler(可选):拒绝策略。当线程数达到最大线程数时就要执行饱和策略。

贴心的博主单独给你们写了篇讲线程池的,五分钟就能看懂的那种,请移步线程池实现原理吐血总结

33、java是如何实现线程安全的?哪些数据结构是线程安全的?

1、锁机制:用synchronize、lock给共享资源加锁;

2、使用java提供的安全类:java.util.concurrent包下的类自身就是线程安全的,在保证安全的同时还能保证性能;

线程安全的数据结构:

常见的有Atomicinteger、ConcurrentHashMap,太多了,截个图吧,大家可以自己去java.util.concurrent包下看

在这里插入图片描述

34、java线程间通信方式

1、基于synchronized+wait() 和 notify()

2、基于reentrantLock的Condition

3、基于volatile

4、基于CountDownLatch

一两句话说不清楚,单独写了篇,每个方式我都给了demo,复制就能运行,请移步java线程间通讯的几种方式

35、springMvc的controller是单例还是多例?

默认是单例,可以通过@Scope(“prototype”)注解指定成多例,但一般没人这么干

36、什么是redis缓存穿透、缓存击穿、缓存雪崩?如何解决?

从事态严重性来讲:穿透 > 雪崩 > 击穿

缓存穿透:请求数据库中根本就不存在的数据,既然数据库中都没有,缓存中更没有,导致每次请求直接怼到数据库;

缓存雪崩:缓存大面积失效;

缓存击穿:请求了很多缓存中没有但是数据库中真实存在的数据,一般是缓存过期导致,也导致请求直接怼到数据库;

解决办法:

缓存穿透:最简单的就是利用布隆过滤器过滤非法key,我写了个 demo来分析具体原理,请移步布隆过滤器原理

缓存雪崩:设置key过期时间的时候加上一个随机数,关键点就在于让key错开时间失效;

缓存击穿:延长热点数据过期时间,或者直接设置永远不过期;

37、说说数据库设计三范式?

第一范式

属性(字段)的原子性约束,要求属性具有原子性,不可再分割; 比如个人信息,个人信息不能作为一个字段,它可以再分为姓名、name、age等;

第二范式

记录的惟一性约束,要求记录有惟一标识,每条记录需要有一个属性来做为实体的唯一标识;

第三范式

字段冗余性的约束,即任何字段不能由其他字段派生出来;主键没有直接关系的数据列必须消除,消除的办法就是再创建一个表来存放他们,当然外键除外;

误区:

并不是非得严格按照三范式来设计,好的数据库设计一定不是这样的,而是根据实际情况柔性处理;

38、简单阐述Java中的io、nio、bio

i/o即input/output,就是指读写操作

面试官经常问io和nio的区别,如果把io和nio放一起比较的话,那这里的io其实可以理解为bio,即blocking-io:

bio:同步阻塞

bio是java传统的io模型,他是同步阻塞io,一个线程触发io操作后,必须等待这个io操作执行完成,期间不能去做其他事情;

nio:同步非阻塞

nio(non-blocking-io)是同步非阻塞io,一个线程触发io操作后它可以立即返回,但是他需要不断地轮询去获取返回结果;

aio:异步非阻塞

aio(Asynchronous-io)是异步非阻塞io,一个线程触发io操作后她可以立马返回去做其他事情,内核系统将io操作执行完成后会通知线程;

多路复用io:异步阻塞

io多路复用:可以理解为异步阻塞io,但官方没这么叫,一个线程可以管理多个连接,不用来回切换;

39、什么是内存泄漏,怎么确定内存泄漏?

概念:内存泄漏就是指jvm内存没有及时释放,用人话说就是使用完的对象没有被及时回收

怎么确认:linux有个工具叫valgrind,一两句话说不清楚,单独拎出来讲,移步使用valgrind来检查内存泄漏

40、实现单例设计模式(懒汉、饿汉)

//懒汉,顾名思义比较懒,在用的时候才实例化

public class Singleton {

//创建实例,注意,此时没有new

private static volatile Singleton instance = null;

//构造方法私有化,无法在外部获取实例,只能通过下方的公有静态方法

private Singleton() {}

//公有的静态方法,返回实例对象

public static synchronized Singleton getInstance() {

//先看下是否存在实例,有的话就不再new了

if (instance == null) {

//这里才new

instance = new Singleton();

}

return instance;

}

}

//饿汉,顾名思义很饥饿,创建对象的时候就直接new

public class Singleton {

//创建实例的时候就new

private static Singleton instance = new Singleton();

// 私有化构造方法,外部不能new

private Singleton() {}

//公有的静态方法,返回实例对象

public static Singleton getInstance() {

//直接将事先new好的实例返回

return instance;

}

}

41、手写生产者消费者模型

生产者消费者是并发编程很经典的一个模型

牵涉三个对象:仓库、生产者、消费者

仓库:代表共享变量

生产者:表示在仓库生产货物

消费者:表示从仓库拿出货物

实现思路:线程间通信

具体demo请见一看就懂的生产者消费者模型

42、int和Integer有什么区别?

int是基本数据类型,默认值为0,integer是其包装类型,默认值为null

  • 原始类型: boolean,char,byte,short,int,long,float,double
  • 包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double

java实体类尽量都用integer,假设一个学生类,有个考试成绩属性,如果学生缺考,该属性不应该有值,应该为null,你用int的话默认值为0,考试成绩为0和缺考是两码事

43、String和StringBuilder、StringBuffer的区别?

Java平台提供了两种类型的字符串:String和StringBuffer/StringBuilder,它们可以储存和操作字符串。其中String是只读字符串,也就意味着String引用的字符串内容是不能被改变的。而StringBuffer/StringBuilder类表示的字符串对象可以直接进行修改。StringBuilder是Java 5中引入的,它和StringBuffer的方法完全相同,区别在于它是在单线程环境下使用的,因为它的所有方面都没有被synchronized修饰,因此它的效率也比StringBuffer要高。

44、常见的运行时异常

  • ArithmeticException(算术异常)
  • ClassCastException (类转换异常)
  • IllegalArgumentException (非法参数异常)
  • IndexOutOfBoundsException (下标越界异常)
  • NullPointerException (空指针异常)
  • SecurityException (安全异常)

45、事务的ACID是指什么?

  • 原子性(Atomic):事务中各项操作,要么全做要么全不做,任何一项操作的失败都会导致整个事务的失败;
  • 一致性(Consistent):事务结束后系统状态是一致的;
  • 隔离性(Isolated):并发执行的事务彼此无法看到对方的中间状态;
  • 持久性(Durable):事务完成后所做的改动都会被持久化,即使发生灾难性的失败,通过日志和同步备份可以在故障发生后重建数据。

46、事务隔离级别

1、READ_UNCOMMITTED

读未提交,即能够读取到没有被提交的数据,无法解决脏读、不可重复读、幻读

2、READ_COMMITED

读已提交,即能够读到那些已经提交的数据,能够防止脏读,但是无法限制不可重复读和幻读

3、REPEATABLE_READ

可重复读,即在数据读出来之后加锁,类似"select * from XXX for update",明确数据读取出来就是为了更新用的,所以要加一把锁,防止别人修改它。REPEATABLE_READ的意思也类似,读取了一条数据,这个事务不结束,别的事务就不可以改这条记录,这样就解决了脏读、不可重复读的问题,但是幻读的问题还是无法解决

4、SERLALIZABLE

串行化,最高的事务隔离级别,不管多少事务,挨个运行完一个事务的所有子事务之后才可以执行另外一个事务里面的所有子事务,解决了脏读、不可重复读和幻读

表格总结

在这里插入图片描述

47、悲观锁和乐观锁的原理以及应用场景

悲观锁:

顾名思义,比较悲观,每次去拿数据都认为别人会修改,所有每次在操作前都会加锁,如:读写锁、行锁、表锁等,synchronized的原理也是悲观锁;适用于多写操作

乐观锁:

每次拿数据都认为别人不会修改,所以不会加锁,但是在更新的时候,会先判断在此期间有没有人更新该数据,如果有,返回冲突报错信息,让用户决定怎么操作;适用于多读操作

48、对spring IOC和AOP的理解

IOC(控制反转)

也叫DI(依赖注入),是一种思想,不是一种技术,IOC主张把对象的控制权交由spring,底层实现是反射+工厂方法模式,IOC容器实际上就是个Map,存放各种对象;

AOP

面向切面编程,把一些能共用、冗余、繁琐的功能提取出来,AOP能在不改变原有业务逻辑的情况下,增强横切逻辑代码,根本上解耦合,避免横切逻辑代码重复;常见使用场景有事务管理、日志、全局异常处理、用户鉴权;

49、简述java内存模型

注意和本文23点的jvm内存模型区分开,这是两种截然不同的内存模型

以下是本人总结的概念,别看字多,都是精华,我想过删些,一句都没找到,阅读一遍应该理解的差不多了

  • java内存模型的由来:这和cpu和内存技术发展有关,当代cpu运算速度太快,以至于被内存拉低了程序运行的整体效率

  • 应对措施:为了不被内存影响性能,cpu厂商给cpu加了一级二级甚至三级高速缓存,cpu读取数据时先从一级缓存中找,没有的话找二级,再没有就找三级或者主存;java内存模型规定了变量都是存储在主存中,程序运行时操作的是高速缓存中的数据,操作完之后再同步到主存;

  • 后遗症及规范:在cpu和主存之间增加了一个高速缓存固然提升了程序运行效率,但是在多线程并发操作同一个变量时,就可能造成数据不一致的问题,所以就有了JMM(java内存模型,也可叫JMM规范),它保证了并发编程中数据的正确性(正确性可细分为原子性、可见性、有序性),底层具体实现方式比较复杂,好在JMM为我们日常编程提供了一些用于保证数据正确性的关键字,如synchronized(原子性、有序性)、volatile(可见性、有序性);

50、什么是泛型擦除?

泛型只是为了在编码过程中,我的理解是泛型存在的意义有两个:一是为了让我们更快地发现错误,比如你把User放进了ArrayList< Dog >中,编译器立马会报错;二是避免类型检查,从而避免在运行时抛出 classCastException;泛型擦除就是指泛型会在编译时被消除

ArrayList users = new ArrayList<>();

ArrayList dogs = new ArrayList<>();

System.out.println(users.getClass() == dogs.getClass());

//true

如上,运行结果表面两个list是相等的,因为经过编译后,泛型被擦除,两个list当然也就相等;

51、你能想到几种方法实现两数交换?

1、第三变量

public void swapOne(){

int a = 4;

int b = 5;

int c = a;

a = b;

b = c;

System.out.println(“a:”+a); //a:5

System.out.println(“b:”+b); //a:4

}

2、数学计算

public void swapTwo(){

int a = 4;

int b = 5;

a=a+b;

b=a-b;

a=a-b;

System.out.println(“a:”+a); //a:5

System.out.println(“b:”+b); //a:4

}

3、异或运算

public void swapThree(){

int a = 4;

int b = 5;

a = a ^ b; // 0101 ^ 0100 ===> 0001,此时a的值为1

b = a ^ b; // 0001 ^ 0100 ===> 0101,此时b的值为5

a = a ^ b; // 0001 ^ 0101 ===> 0100,此时a的值为4

System.out.println(“a:”+a); //a:5

System.out.println(“b:”+b); //a:4

}

52、什么是CAS操作?什么ABA问题?如何解决?

CAS全称compare and swap(比较并交换),作用是保证原子性

CAS操作包含三个操作数 —— 内存位置、预期原值、新值。 如果内存位置的值和预期原值相等,就把该值更新为新值,如果不相等,则什么都不做;

ABA问题:CAS操作存在的一个并发问题,打个比方,有两个线程A、B同时操作变量x,A读取到的预期原值是1,此时线程B先将x设置为2,再设置为1,等线程A再来操作的时候,x变量的预期原值和当前值相等,但是x在整个过程中的值是发生过变化的,这在某些业务场景下是不允许的;

解决:利用版本号,给变量x增加版本号,每次操作增加对本版好的判断和修改;

53、什么是回表查询?如何避免?

对于mysql数据库的InnoDB引擎,它的非主键索引是非聚簇索引,索引文件和数据文件分开存储,索引文件B+树的叶节点只保存主键,在进行查询时,需要先去索引文件找到id,再根据id去数据文件查找具体数据,这种现象就叫回表查询

如何避免:索引覆盖

将查询sql中的字段添加到联合索引里面,只要保证查询语句里面的字段都在索引文件中,就无需进行回表查询;

更多索引知识点请移步mysql索引总结

54、什么是CAP理论?

CAP理论是分布式系统架构设计中的一种猜想,或者说是一种理论

  • 一致性(Consistence) : 所有节点访问的数据都是一致的;
  • 可用性(Availability): 非故障的节点在合理的时间内返回合理的响应(不是错误或者超时的响应);
  • 分区容错性(Partition tolerance) : 分布式系统出现网络分区的时候,仍然能够对外提供服务。网络分区指的是网络设备出现的丢包、阻塞、超时等问题。

误区:我发现很多人说到CAP时,都知道分布式系统中这三者不能同时兼顾,只能满足其二,这种说法不严谨,并不是简单的三选二,而是在一致性和可用性二者中只能选其一,分区容错性是我们必须保证的,不能因为出现网络分区导致系统瘫痪,而一致性和可用性可以根据我们的业务来舍弃其一,即我们只能实现AP方案或者CP方案;

55、什么是BASE理论?

BASE理论由CAP理论演化而来,因为CAP对系统设计的要求过高,实现CAP的代价太大;

BASE实质上是对CAP 中 AP 方案的一个补充。

BA:Basically Available(基本可用)

S:Soft-state(软状态) E:Eventually

Consistent(最终一致性)

BASE理论核心思想

牺牲强一致性,通过某些逻辑来达到数据的最终一致性

因为大多数系统对强一致性的依赖没那么强,抛开分区容错性不谈,一致性和可用性这两者,可用性才是我们应该保证的,而对于一致性,我们往往只需要保证最终的数据一致即可;

56、@Autowire和@Resource区别?

都是用来装配java bean

  • @Autowired:按类型注入,这是spring的注解,可以搭配@Qualifie实现按名称注入;

  • @Resource:默认情况下是按照名称进行匹配,如果没有找到相同名称的Bean,则会按照类型进行匹配,这是java自己的注解;

注意事项:

@Autowired有个弊端,打个比方,有个userService,然后它有两个实现类userServiceA和userServiceB,这时候用@Autowired就行不通了,因为它不知道找谁,但是你也不能因为这个一上来就直接用@Resource,这玩意儿性能没@Autowired好,因为@Resource要匹配两次

小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
img

总结

我个人认为,如果你想靠着背面试题来获得心仪的offer,用癞蛤蟆想吃天鹅肉形容完全不过分。想必大家能感受到面试越来越难,想找到心仪的工作也是越来越难,高薪工作羡慕不来,却又对自己目前的薪资不太满意,工作几年甚至连一个应届生的薪资都比不上,终究是错付了,错付了自己没有去提升技术。

这些面试题分享给大家的目的,其实是希望大家通过大厂面试题分析自己的技术栈,给自己梳理一个更加明确的学习方向,当你准备好去面试大厂,你心里有底,大概知道面试官会问多广,多深,避免面试的时候一问三不知。

大家可以把Java基础,JVM,并发编程,MySQL,Redis,Spring,Spring cloud等等做一个知识总结以及延伸,再去进行操作,不然光记是学不会的,这里我也提供一些脑图分享给大家:

希望你看完这篇文章后,不要犹豫,抓紧学习,复习知识,准备在明年的金三银四拿到心仪的offer,加油,打工人!

家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**
[外链图片转存中…(img-DcByCEex-1711126801255)]
[外链图片转存中…(img-rGISwWTP-1711126801256)]
[外链图片转存中…(img-FhGP8r80-1711126801256)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
[外链图片转存中…(img-NHLEFcoN-1711126801257)]

总结

我个人认为,如果你想靠着背面试题来获得心仪的offer,用癞蛤蟆想吃天鹅肉形容完全不过分。想必大家能感受到面试越来越难,想找到心仪的工作也是越来越难,高薪工作羡慕不来,却又对自己目前的薪资不太满意,工作几年甚至连一个应届生的薪资都比不上,终究是错付了,错付了自己没有去提升技术。

这些面试题分享给大家的目的,其实是希望大家通过大厂面试题分析自己的技术栈,给自己梳理一个更加明确的学习方向,当你准备好去面试大厂,你心里有底,大概知道面试官会问多广,多深,避免面试的时候一问三不知。

大家可以把Java基础,JVM,并发编程,MySQL,Redis,Spring,Spring cloud等等做一个知识总结以及延伸,再去进行操作,不然光记是学不会的,这里我也提供一些脑图分享给大家:

[外链图片转存中…(img-eesHPzmQ-1711126801257)]

[外链图片转存中…(img-ad6wu6yO-1711126801258)]

[外链图片转存中…(img-sygNopZm-1711126801258)]

希望你看完这篇文章后,不要犹豫,抓紧学习,复习知识,准备在明年的金三银四拿到心仪的offer,加油,打工人!

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值