面试复盘

发现问题了,就要针对这些问题解决;

面试考察无非就是基础,项目,算法三个大模块

其中基础包含java技术栈(上层开发)和jvm(中层衔接),计网,操作系统(底层原理)三大部分;

项目是对基础知识的应用,如何将这些理论知识投入实战,自己有什么思考,项目有哪些亮点,哪个模块最能体现技术深度,简历上的项目要点没必要很多,但一定要很深,任意一条抽出来都能体现自己是这个小模块领域的专家

算法这块先不要纠结acm模式和leetcode模式这些细节,我现在最大的问题是没思路,哪怕不会写代码,每个算法能先把思路想明白,其他的都不是问题;尤其是反转链表这种基础中的基础再这种表现确实很说不过去了,想捞想要都没法要

规划:

+ 面试前最好有一个逐字稿,同时自己先自问自答做一个录音,用不同问题问自己看那些地方薄弱需要改进,没有模拟直接进考场不仅对自己不负责影响自己的心态,也替面试官尴尬

+ Java技术栈:先结合自己的项目延申扩展,项目中的技术要点要吃透讲明白,不能囫囵吞枣,感觉会了就过了

+ Java基础:JVM的每个细节都要精通,这两天把jvm那本书当作笔记啃透了,能提到哪个点都能熟练精确的描述出来,尤其是线程,内存,gc,将来想扩展试试客户端的要把jni原理也给整明白,让面试官眼前一亮

面试中问到了sychronize的锁升级,juc中对各个锁要有深入理解

+ 计网和os:无论哪个岗位都非常重视这两块,之前一直不当回事,真面试后才发现这两块考察的可能比java还多,要有系统的学习,针对开发中会面对的知识点要非常深入

+ 项目与实习:修改下自己的简历,只留下技术深度非常能聊的模块,让面试官看的有重点,聊的有深度有思考,实习中保持对实习内容的思考,也许没有高深的技术,但要体现面对实际生产问题时自己的处理能力与责任感

+ 算法:如果一个目标非常难实现,就先一步步拆解成小目标,代码写完就忘那就先学思路,对着每个题先描述思路该怎么做,就像给面试官讲的时候那个标准一样,再用伪代码把思路写下来,当能做到常见题直接出思路,没见过的简单题也能简单思考后有思路,再去落实代码怎么写,如何处理输入输出这些细节问题

ai对我第一次面试的总结指导也记录一下:

腾讯 WXG 客户端实习一面回答点评与改正:#牛客AI配图神器#

结合你的面经和客户端开发面试的核心考点(网络、Java 基础、内存 / 线程、项目、算法),我会逐题点评原回答的问题,并给出准确、有条理、适配实习面试的改正方案,同时补充面试官想听到的关键细节。

一、基础学科与网络相关问题(3-7 题)

3. 学校教 OS 和计网了没,自己有了解过吗?

原回答问题:

过于被动:只说 “大三才学”“OS 没深入”,未主动展示已有的知识储备,显得学习意识不足。

关联度低:提到 “JVM 较底层” 但没衔接 OS 的基础概念,浪费展示机会。

改正回答:

“目前大三,学校还没正式开 OS 课,但计网已经学了核心模块(TCP/UDP、HTTP、IP 协议),课后自己也通过实验加深过理解(比如用 Wireshark 抓过 TCP 报文);OS 方面,虽然没系统学,但因为学 JVM 时接触过‘进程 / 线程调度’‘内存分区’的基础概念(比如 JVM 堆对应 OS 的虚拟内存),所以对 OS 的核心作用有初步认知,后续计划结合 OS 补 JVM 的底层原理。”

关键加分点:

主动关联 “JVM 与 OS 的联系”,体现知识串联能力;

提 “实验 / 工具(Wireshark)”,比单纯说 “了解” 更有说服力。

5. TCP 如何保证连接(三次挥手)?

原回答问题:

逻辑混乱:没按 “步骤 + 报文 + 目的” 梳理,细节丢失(如 SYN/ACK 的作用、ISN);

未讲核心:没说明 “三次挥手的目的是确认双方‘收发能力正常’”。

改正回答:

“TCP 通过三次挥手建立可靠连接,核心是确认客户端和服务端的‘发送 + 接收’能力都正常,步骤如下:

客户端→服务端:发SYN 报文(同步报文),携带初始序列号 ISN_c,目的是告诉服务端‘我能发数据,我的起始序号是 ISN_c’,客户端进入 SYN_SENT 状态;

服务端→客户端:发SYN+ACK 报文(同步 + 确认),携带自己的 ISN_s 和对客户端 ISN_c 的确认号(ISN_c+1),目的是告诉客户端‘我能收也能发,我确认收到你的 SYN,我的起始序号是 ISN_s’,服务端进入 SYN_RCVD 状态;

客户端→服务端:发ACK 报文(确认),携带对服务端 ISN_s 的确认号(ISN_s+1),目的是告诉服务端‘我确认收到你的 SYN,现在双方收发能力都正常,连接可以建立’,客户端和服务端都进入 ESTABLISHED 状态。

三次挥手的关键是‘不省略任何一次确认’—— 如果只两次,服务端无法确认客户端是否能收到自己的 SYN,可能导致服务端一直等客户端的确认,浪费资源。”

6. TCP 的四次挥手过程?

原回答问题:

比喻不当:用情景比喻替代技术流程,面试官更关注 “报文 + 状态 + 为什么四次”;

细节缺失:没提服务端的 CLOSE_WAIT 状态、TIME_WAIT 状态的作用。

改正回答:

“TCP 通过四次挥手断开连接,核心是‘双方都要确认 “我没有数据要发了”’,步骤如下(以‘客户端先发起断开’为例):

客户端→服务端:发FIN 报文,携带确认号,目的是告诉服务端‘我没有数据要发了,但我还能收你的数据’,客户端进入 FIN_WAIT_1 状态;

服务端→客户端:发ACK 报文,确认收到客户端的 FIN,服务端进入 CLOSE_WAIT 状态(此时服务端可能还有未发完的数据,需要先处理),客户端收到后进入 FIN_WAIT_2 状态;

服务端→客户端:等服务端数据发完后,发FIN 报文,携带确认号,目的是告诉客户端‘我也没有数据要发了’,服务端进入 LAST_ACK 状态;

客户端→服务端:发ACK 报文,确认收到服务端的 FIN,客户端进入 TIME_WAIT 状态(等待 2MSL,避免延迟报文干扰新连接),服务端收到后进入 CLOSED 状态;客户端等待 2MSL 后也进入 CLOSED 状态,连接完全断开。

为什么需要四次?因为服务端收到 FIN 后,可能还有数据要处理,不能像三次挥手那样‘SYN+ACK 一起发’,必须分两次(先 ACK 确认 FIN,再发自己的 FIN)。”

6.1 服务端能主动断开连接吗?/ 6.2 服务端主动断开连接的协议?

原回答问题:

核心错误:6.1 答 “不能”,完全违背 TCP 协议设计;6.2 没理解问题,不知道协议是 TCP。

场景缺失:没意识到 “服务端主动断开是日常场景”(如空闲清理、会话超时)。

改正回答(两题合并,逻辑更连贯):

“服务端完全可以主动断开连接,这是 TCP 协议支持的核心能力,常见场景有:

空闲连接清理:比如客户端 30 分钟没发数据,服务端释放闲置端口(微信、HTTP 服务都这么做);

会话超时:比如网银 15 分钟无操作,服务端断开避免安全风险;

系统过载:CPU 达 90% 时,断开非关键连接保障核心服务。

协议层面,服务端通过 TCP 的两种报文实现主动断开:

正常断开:发FIN 报文,触发四次挥手(和客户端发起断开的流程一样,只是服务端先发 FIN),比如 HTTP 响应头加Connection: close,告诉客户端‘本次请求后我主动断开’;

强制断开:发RST 报文(重置连接),直接终止连接(无需四次挥手),比如检测到客户端发恶意请求(SQL 注入)时,快速切断风险连接。”

7. HTTP 与 HTTPS 的区别?

原回答问题:

表述模糊:“发送方私钥与公钥,接收方对称密钥” 逻辑混乱,没讲清 “非对称加密 + 对称加密” 的配合流程;

关键缺失:没提 HTTPS 的证书(CA 签名),这是防止 “中间人攻击” 的核心。

改正回答:

“HTTP 和 HTTPS 的核心区别是‘安全性’,具体有三点:

传输层不同:HTTP 直接跑在 TCP 上,数据明文传输(抓包就能看内容);HTTPS 在 TCP 和 HTTP 之间加了TLS/SSL 层(现在主流是 TLS 1.3),数据加密传输;

加密流程不同:HTTPS 用‘非对称加密 + 对称加密’混合方案,流程是:

服务端把‘公钥 + CA 证书’发给客户端,客户端验证证书(确认服务端身份,防止中间人冒充);

客户端生成‘对称密钥’,用服务端的公钥加密后发给服务端;

服务端用自己的私钥解密,拿到对称密钥;

后续双方用‘对称密钥’加密数据(对称加密速度快,适合大量数据传输);

端口不同:HTTP 默认用 80 端口,HTTPS 默认用 443 端口,且 HTTPS 需要服务端部署 CA 签名的证书(否则浏览器会提示‘不安全’)。”

二、Java 基础与内存 / 线程相关问题(8-18 题)

8. 了解内存泄漏吗?

原回答问题:

概念偏差:“拓扑排序后根未能到达的节点相互引用” 表述错误(GC 用 “可达性分析”,不是拓扑排序;循环引用不会导致泄漏);

场景不完整:只提 ThreadLocal,没讲其他常见场景(如未关闭资源、静态集合)。

改正回答:

“内存泄漏是指‘无用的对象(不再被使用)无法被 GC 回收,一直占用内存,最终可能导致 OOM’,常见场景有三种:

ThreadLocal 未 remove:ThreadLocal 的 key 是弱引用(GC 会回收),但 value 是强引用;如果线程不结束(比如线程池),value 会被 ThreadLocalMap 一直持有,无法回收,所以用完 ThreadLocal 必须调用 remove ();

未关闭资源:比如 IO 流、数据库连接、Socket,若不调用 close (),这些资源的底层对象(如文件句柄)会被 JVM 持有,GC 无法回收;

静态集合持有对象:比如static List<Object> list = new ArrayList<>(),往 list 加对象后不清理,list 一直持有对象引用(静态变量是根对象),即使对象没用了也无法回收。

注意:如果两个无用对象相互引用(a→b,b→a),但都不在‘根对象可达链’上(比如没有栈引用、静态引用指向它们),GC 能识别并回收,不会导致泄漏 —— 泄漏的核心是‘无用对象被根对象间接引用’。”

9. 如果 a 引用 b,b 引用 a 会造成内存泄漏吗?

原回答问题:

核心错误:答 “会”,完全误解 GC 的工作原理(GC 不关心循环引用,只关心 “是否可达根对象”)。

改正回答:

“不会造成内存泄漏,因为 GC 判断对象是否可回收用的是‘可达性分析’:

从‘根对象’(比如栈中的局部变量、静态变量、JNI 引用)出发,遍历对象引用链;

如果 a 和 b 都不在‘根对象可达链’上(比如没有任何代码再使用 a 或 b),即使它们相互引用,GC 也会认为它们是‘无用对象’,直接回收;

只有当 a 或 b 被根对象引用时(比如static Object o = a),a 和 b 才会被保留,但这不是‘循环引用’导致的泄漏,而是‘根引用’导致的。”

11. 了解线程中的内存能共享吗?

原回答问题:

完全错误:答 “不会”,混淆了 “线程内存共享” 和 “ThreadLocal 的变量隔离”;

概念不清:没区分 JVM 中 “线程共享区” 和 “线程私有区”。

改正回答:

“线程的内存既有共享部分,也有私有部分,核心是 JVM 的内存分区:

共享内存(所有线程都能访问):

堆:存储对象实例(比如new Object())、数组,比如多个线程可以同时修改同一个堆中的对象属性;

方法区:存储类信息、静态变量(static修饰的变量),比如static int count = 0,所有线程都能读写 count;

私有内存(每个线程独有,其他线程不能访问):

程序计数器:记录当前线程执行的字节码行号;

虚拟机栈:存储局部变量(比如方法中的int a = 1)、方法调用栈(每个方法调用对应一个栈帧);

本地方法栈:执行本地方法(如 JNI 调用的 C++ 方法)的栈;

比如 ThreadLocal,它不是‘线程内存不共享’,而是‘让每个线程持有变量的独立副本’——ThreadLocal 的底层是 ThreadLocalMap(存在线程的私有内存中),每个线程的副本只自己能用,本质是‘变量隔离’,不是‘内存不共享’。”

12. 谈谈你对 GC 的理解?

原回答问题:

逻辑零散:只提 G1 和 “新生代老年代”,没讲 GC 的 “目的 + 算法 + 回收器” 完整逻辑;

概念错误:“G1,G2” 是口误(没有 G2,G1 是 Garbage-First 的简称)。

改正回答:

“GC(垃圾回收)的核心目的是‘自动回收无用对象的内存,避免手动管理内存导致的泄漏或 OOM’,主要包括‘GC 算法’和‘垃圾回收器’两部分:

GC 算法(判断对象是否可回收 + 回收内存的方式):

可达性分析:判断对象是否可回收的核心(根对象可达则保留,不可达则标记为垃圾);

具体回收算法:

标记 - 复制:用于新生代(Eden + Survivor 0 + Survivor 1),把 Eden 和 Survivor 0 的存活对象复制到 Survivor 1,清空原区域(速度快,适合存活对象少的新生代);

标记 - 清除:用于老年代,先标记垃圾,再直接清除(速度慢,会产生内存碎片);

标记 - 整理:用于老年代,标记垃圾后,把存活对象往一端移动,再清除垃圾(无碎片,但移动对象耗时间);

垃圾回收器(算法的具体实现):

新生代回收器:Serial(单线程,适合客户端)、Parallel Scavenge(多线程,注重吞吐量);

老年代回收器:CMS(多线程,注重响应时间,用标记 - 清除)、Serial Old(单线程,CMS 的备用);

跨代回收器:G1(覆盖新生代和老年代,把内存分成多个 Region,优先回收垃圾多的 Region,兼顾吞吐量和响应时间);

对象的生命周期:先在新生代 Eden 区创建,经历 1-15 次 GC(默认 15 次)后存活,会晋升到老年代;大对象(比如大数组)会直接进入老年代。”

13. 聊聊 Java 吧,了解 jni 吗?

原回答问题:

概念混淆:把 JNI 和 “本地方法栈” 混为一谈(JNI 是接口规范,本地方法栈是内存区域);

未讲作用:没说明 JNI 的实际用途,显得理解不深。

改正回答:

“JNI 是 Java Native Interface 的简称,是 Java 调用‘本地代码’(比如 C/C++ 代码)的接口规范,和 JVM 的‘本地方法栈’是两回事(本地方法栈是执行本地方法的内存区域,JNI 是跨语言调用的规则)。

JNI 的核心作用是解决‘Java 做不了或做不好’的事,比如:

调用硬件接口(比如嵌入式开发中控制传感器,Java 没有对应的 API,需要 C++ 操作硬件寄存器);

提升性能(比如复杂的数学计算、图像处理,C++ 比 Java 快,用 JNI 调用 C++ 模块);

复用现有 C/C++ 库(比如调用开源的 C++ 算法库,不用重新用 Java 写)。

之前容易和 JIT 搞混(JIT 是即时编译,把字节码转机器码,提升 Java 执行效率),现在能明确区分了:JNI 是跨语言调用,JIT 是编译优化。”

14. 了解 jni 是怎么调用本地 c++ 代码的吗?

原回答问题:

完全不会:没接触过 JNI 的实际开发流程,这是客户端开发的常见考点(比如 Android 客户端会用 JNI 调用 C++ 写的 Native 层)。

改正回答:

“了解基本流程,主要分 6 步(以 Java 调用 C++ 为例):

写 Java 的 native 方法:在 Java 类中声明本地方法,比如public native int add(int a, int b);(native 关键字表示这是本地方法,实现不在 Java 中);

编译 Java 类:用javac命令生成.class 文件(比如javac Test.java);

生成 C++ 头文件:用javah命令(Java 10 后用javac -h)生成头文件,头文件中包含 C++ 函数的声明(比如JNIEXPORT jint JNICALL Java_Test_add(JNIEnv *, jobject, jint, jint));

用 C++ 实现头文件的函数:比如实现add方法,返回 a+b 的结果,注意 JNI 的函数参数格式(JNIEnv*是 JNI 环境指针,用于调用 JNI API);

编译 C++ 代码为动态链接库:Windows 下编译为.dll 文件,Linux 下为.so 文件,macOS 下为.dylib 文件;

Java 加载链接库并调用:在 Java 中用System.loadLibrary("库名")加载动态链接库,然后像调用普通 Java 方法一样调用 native 方法(比如Test test = new Test(); int result = test.add(1,2);)。”

16. JIT 是什么码转化为什么码?

原回答问题:

表述模糊:“字节码 class 文件转化为机器码” 没错,但没讲 “什么时候转”“为什么转”,显得理解浅。

改正回答:

“JIT 是 Just-In-Time Compilation(即时编译)的简称,核心是把Java 字节码(.class 文件中的指令,比如iconst_1、invokevirtual)转化为CPU 能直接执行的机器码(比如 x86 的mov、add指令)。

这里要区分 Java 的两种执行方式:

解释执行:Java 虚拟机(JVM)的解释器逐行读取字节码,每次执行都转成机器码(速度慢,适合少量代码);

JIT 编译执行:JIT 会监控 “热点字节码”(比如频繁执行的循环、常用方法),当某段代码执行次数超过阈值(比如 10000 次),JIT 会把这段字节码编译成机器码并缓存起来;后续再执行这段代码时,直接用缓存的机器码(不用再解释),速度比解释执行快很多。

所以 JIT 的作用是‘平衡 Java 的跨平台性(字节码跨平台)和执行效率(机器码快)’—— 字节码保证‘一次编写到处运行’,JIT 保证‘运行时速度接近 C++’。”

17. 了解四种引用类型吗?

原回答问题:

记忆缺失:只记得三种,且只会解释强引用;

场景缺失:没讲每种引用的 “GC 行为” 和 “应用场景”,这是面试官的核心关注点。

改正回答:

“Java 有四种引用类型,核心区别是‘GC 对它们的回收策略不同’,从强到弱依次是:

强引用(Strong Reference):

定义:最普通的引用,比如Object o = new Object();

GC 行为:只要强引用存在,GC 绝对不会回收这个对象(即使 OOM 也不回收);

场景:日常开发中最常用(比如创建对象、存储数据);

软引用(Soft Reference):

定义:用SoftReference包装,比如SoftReference<Object> softRef = new SoftReference<>(new Object());

GC 行为:内存充足时不回收,内存不足(快 OOM)时才回收;

场景:用于缓存(比如图片缓存,内存够就存,不够就清);

弱引用(Weak Reference):

定义:用WeakReference包装,比如WeakReference<Object> weakRef = new WeakReference<>(new Object());

GC 行为:只要触发 GC(不管内存是否充足),就会回收弱引用指向的对象;

场景:ThreadLocal 的 key、WeakHashMap(键是弱引用,键无用时自动删除键值对);

虚引用(Phantom Reference):

定义:用PhantomReference包装,必须和ReferenceQueue一起用;

GC 行为:虚引用本身不影响对象回收,对象被回收时,虚引用会被加入ReferenceQueue(唯一作用是 “通知对象已回收”);

场景:管理直接内存(比如 NIO 的 DirectBuffer,虚引用通知 JVM 回收直接内存,避免泄漏)。”

三、项目与算法相关问题(19-23 题)

19-20. 介绍项目亮点 / 难度模块?

原回答问题:

毫无准备:“大脑宕机”“大眼瞪小眼”,没提前梳理项目的 “技术深度”,只说 “缓存、MQ” 太泛泛;

逻辑缺失:没讲 “问题 - 方案 - 效果”,面试官看不到你的贡献和思考。

改正回答(以 “基于 Redis 的缓存设计” 为例,用 STAR 法则):

“我最有技术亮点的模块是‘项目中的缓存优化模块’,当时遇到的问题是‘用户查询商品列表的接口 QPS 冲到 1000,数据库(MySQL)扛不住,响应时间从 50ms 涨到 500ms’,我做的方案和效果如下:

场景(S):商品列表接口是高频接口,用户每次刷新都查数据库,数据库连接池经常满;

任务(T):我负责优化这个接口,目标是把响应时间降到 100ms 以内,数据库 QPS 降低 80%;

行动(A):

用 Redis 做分布式缓存,缓存商品列表数据,设置过期时间(10 分钟,避免数据过期);

解决‘缓存穿透’:比如用户查不存在的商品 ID,会绕过缓存查数据库,我加了布隆过滤器(提前把所有商品 ID 存入,不存在的 ID 直接返回空,不查数据库);

解决‘缓存击穿’:热门商品缓存过期时,大量请求会同时查数据库,我加了互斥锁(只有一个请求能查数据库,其他请求等锁,避免数据库压力);

结果(R):优化后接口响应时间降到 30ms,数据库 QPS 从 1000 降到 150,上线后稳定运行 1 个月,没出现数据库过载问题。

另外,我还做过 MQ 异步削峰的模块,比如用户下单后,同步调用支付、库存、日志接口会超时,我用 RabbitMQ 把‘日志记录’改成异步,下单响应时间从 300ms 降到 80ms,也解决了‘日志服务挂了导致下单失败’的问题。”

关键加分点:

用 “数据” 说话(QPS、响应时间、效果时长),比 “加快接口响应” 更具体;

提 “解决的问题(缓存穿透 / 击穿)”,体现技术深度(不是只会用 Redis,还懂缓存问题的解决方案)。

23. 翻转链表(标准版)?

原回答问题:

思路错误:用两个指针导致 “断链”(丢失后续节点),没理解 “三个指针” 的核心是 “保存后继节点”;

未讲思路:直接写代码,面试官更关注 “你怎么想的”,而不是只看代码。

改正回答(先讲思路,再写伪代码):

“翻转链表的核心是‘改变每个节点的 next 指针方向,让它指向前驱节点’,用迭代法(三个指针)最直观,思路如下:

定义三个指针:

prev:指向当前节点的前驱节点(初始为 null,因为头节点翻转后会变成尾节点,next 为 null);

curr:指向当前要处理的节点(初始为链表头 head);

next:保存 curr 的后继节点(避免改变 curr.next 后,找不到后续节点);

循环处理每个节点:

先保存 curr 的后继:next = curr.next;

改变 curr 的 next 方向:curr.next = prev;

移动 prev 和 curr:prev = curr,curr = next;

终止条件:curr 为 null(处理完所有节点),此时 prev 就是新的头节点(返回 prev)。

伪代码如下:

public ListNode reverseList (ListNode head) {

ListNode prev = null; // 前驱

ListNode curr = head; // 当前

while (curr != null) {

ListNode next = curr.next; // 保存后继

curr.next = prev; // 翻转指针

prev = curr; // 前驱后移

curr = next; // 当前后移

}

return prev; // 新头节点

}

也可以用递归法,但迭代法更简单,不容易出错(递归需要理解‘从后往前翻转’,比如先翻转 head.next 的子链表,再处理 head 的指针)。”

四、总结:第一次面试的核心改进方向

知识层面:

网络(TCP/UDP、HTTP/HTTPS)和 Java 基础(引用、GC、线程内存)是客户端面试的 “必考点”,必须 “准确记忆 + 逻辑梳理”,避免 “模糊表述”(比如三次挥手的步骤、四种引用的场景);

遇到不会的问题(如 JNI 调用流程),不要直接说 “不知道”,可以说 “了解基本逻辑,但没实际做过,之前学过流程是 XXX”,体现学习意识。

项目层面:

提前用 “STAR 法则” 梳理 2-3 个有技术亮点的模块(每个模块准备 “问题 - 方案 - 效果”),避免 “泛泛而谈缓存 / MQ”;

突出 “你的贡献”(比如 “我设计了缓存策略”“我解决了缓存穿透问题”),而不是 “我们项目做了什么”。

表达层面:

回答时按 “总 - 分” 结构(先讲结论,再分点展开),比如回答 “TCP 三次挥手” 时,先讲 “目的是确认双方收发能力”,再分步骤说;

遇到思路乱的问题(如四次挥手),可以说 “我理一下思路,三次挥手是建立连接,四次挥手是断开,步骤是 XXX”,给自己缓冲时间。

第一次面试能得到 “表达能力不错” 的评价已经很加分了,接下来 2 个月重点补 “网络 + Java 基础 + 项目梳理 + 算法(链表、树、动态规划)”,11 月再面肯定能有明显提升!

全部评论
太狠了 继续更
点赞 回复 分享
发布于 09-18 18:34 四川
蹭一蹭佬的经验
点赞 回复 分享
发布于 09-18 15:07 河南
mark,
点赞 回复 分享
发布于 昨天 12:04 浙江

相关推荐

09-18 20:41
百度_Java
点赞 评论 收藏
分享
评论
2
3
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务