资讯动态

百度Java后端实习一面复盘:对账系统、AQS与Kafka核心考点

发布时间:2026/10/15 13:44:37 来源:尧图企业网站定制
1. 一面流程复盘百度实习面试究竟怎么问先说下背景我投的是Java后端实习岗简历上写了两个项目一个是对账系统一个是消息推送平台。面试大概45分钟左右开头没有自我介绍直接问项目然后一口气把核心Java并发和消息队列的部分挖了个遍最后十五分钟给了一道手写题。整场面下来最大的感受是百度的一面不像很多公司那样喜欢丢一堆八股让你背而是盯着你简历上的一个系统的某条具体链路顺着一条线一直往下问。比如对账系统从你怎么设计对账的开始一路问到文件解析的幂等性怎么做、差异数据怎么处理、怎么保证两边数据一致每个回答都要落在具体方案和边界条件上。问到答不出来或者明显含糊的地方面试官也不会直接给你答案而是换个角度再问一次本质上是在试探你的理解深度。我把整个过程按时间线整理了一下大致是这样阶段内容时长项目深挖对账系统全链路流程、表设计、文件处理、差异单约15分钟并发原理AQS核心思想、公平锁与非公平锁、CLH队列约10分钟消息队列Kafka消费组、位移提交、消息可靠性约10分钟手写代码两线程交替打印数字和字符约10分钟1.1 时间线与问题清单项目环节的问题列表我复盘后记录如下对账系统为什么不用定时任务每天扫一次全量表而是引入文件对账银行/第三方支付的对账文件格式五花八门你怎么做格式兼容对账文件很大几十万行解析性能怎么保证如果对账文件和本地订单对不上差异单怎么生成、怎么流转重复推送同一个对账文件怎么办你的幂等方案是什么本地数据维度本地流水号和渠道方数据的关联键怎么选这些问题的特点很明显不问你用过什么技术栈而是先把业务场景描述清楚再让你自己说出方案然后不断往方案的薄弱处打。比如我说对账文件用多线程分片解析面试官立刻追问分片之后如果有重复行号怎么办我说差异单状态机流转又追问渠道方调账回盘之后状态不对怎么处理。这就是在检查你到底是做过、想过还是只是把网上的方案抄进简历里。1.2 我在面试前踩的一个准备误区现在回头想我面试前最大的失误是花了大量时间背Java集合源码和MySQL索引优化却对自己简历上的对账系统只准备了功能说明没准备边界情况。结果面试官问的问题全是我平时开发过程中遇到但没深究的细节比如幂等、状态机、异常恢复。这里给所有准备实习面试的同学一个建议简历上写系统一定要把系统的异常分支全部过一遍——文件解析到一半挂了怎么办下游调用超时了怎么办重复消息来了怎么办数据库脏数据导致关联不上怎么办面试官问的基本全是这些。2. 对账系统全链路面试官从项目里挖出的十连问2.1 对账系统到底在解决什么问题很多没接触过支付或电商系统的同学听到对账会觉得不就是两个表JOIN一下吗。实际上对账解决的是跨系统的账务一致性问题。我们系统内部有订单、流水、账务但资金最终是经过渠道方银行、支付通道清算的渠道方返回的对账文件代表的是对方视角的账。两边视角不一致的原因非常多掉单我们没收到异步通知、掉状态订单支付成功但流水没更新、渠道方服务异常、金额算错、手续费规则不一致等等。面试官问我的第一个问题就是为什么不用定时任务扫全量订单来判断支付状态而要设计一套对账系统。这个问题很关键。答案是全量扫描只能发现状态异常的订单不能发现金额错误的订单而且渠道方文件里包含的参差数据比如手续费调整、退款冲正在本地根本没有对应记录只有通过逐笔比对渠道文件和本地流水才能发现。所以对账的本质是找差异不是查状态。2.2 从对账文件到差异处理全链路设计思路我当时给面试官讲的链路是这样文件获取层从渠道方的SFTP/OSS拉取日对账文件需要考虑文件命名规范、文件大小、拉取时间窗口。文件解析层不同渠道文件格式不同有TXT、Excel、CSV字段顺序也不一样。我用了策略模式——每个渠道一个Parser解析后统一转换为内部定义的标准对账流水对象。对账执行层本地按商户号、渠道、日期维度拉取本地支付流水两个集合做匹配。差异处理层匹配结果分为匹配成功、本地单边、渠道单边、金额不一致分别入差异表走状态机流转。调账回写层渠道方对差异单的调账结果回盘更新状态。面试官听完这个链路后紧接着问解析层怎么保证性能我说了几个点文件按行迭代、使用可批量提交的线程池分片解析、避免一次性把几十万行读进内存、解析后数据先写临时表再在数据库做匹配。他追问临时表用完之后怎么清理然后问分片解析如果遇到半行数据怎么恢复。这其实就是考察你有没有处理过真实文件。2.3 面试官追问对账幂等、差错处理、掉单检测面试官真正开始发力是在我讲完主流程之后连续抛了三个异常分支问题。第一个是幂等。渠道方可能会重推同一个对账文件尤其是SFTP拉取超时的时候重新拉一遍并不能保证文件下到一半不会重复。我的做法是对文件做MD5校验入库之前先查file_meta表同一个渠道文件名file_md5的记录一旦存在就直接跳过解析。面试官又问如果文件在解析过程中宕机了重启之后还没入库MD5判断会失效你怎么幂等我当时卡了一下后来想到方案是解析过程中每条流水也带上渠道文件批次号作为去重键即使文件重推流水也不会重复入库。这个追问给我的教训是只做到文件级幂等是不够的流水级幂等才能兜底。第二个是差错处理。本地没有但渠道文件有的记录叫渠道长款本地有但渠道没有的叫本地长款。这两种单子的处理方向完全不同。渠道长款意味着用户付了钱但我们没更新订单状态需要补单并通知业务方确认本地长款意味着用户没付钱但订单显示已支付这种要立即止付并走风控。面试官问这两种差异单如果量很大比如渠道文件异常时你怎么办我答为差异单单独建表对账完成后把差异数据批量插入然后状态机从待处理流转到处理中再到已调平。第三个是掉单检测。对账系统除了比对金额还要能发现用户明明支付了但回调没收到的掉单。检测方法是本地有一条pending状态的支付单渠道文件里出现了一笔相同单号的成功记录这时候对账系统要能识别并触发补单通知。这个本质上跟渠道长款是一回事但对账系统要主动向业务侧发起补单事件而不是只记录差异。2.4 我回答得好的地方与遗漏点现在复盘我答得比较好的部分是为什么引入文件对账和策略模式解析文件。当时面试官问如果渠道方文件里没有对账日期只有交易时间你怎么保证只对当天的数据我答了需要取交易时间范围进行过滤而不是简单用文件日期这一点他点了点头。遗漏或者说薄弱的地方很明显我只提了数据库级幂等没提缓存幂等比如用Redis的SETNX做文件级幂等面试官没追问这一步其实是放了我一马对账性能我当时只说分片解析没有主动提并行匹配的细节后来他问匹配这一步是内存做还是数据库做我才补了用内存Map以流水号分组、用排序列表做双向比对的方案没有准备日切场景——跨天支付23:58支付第二天00:01才结算时对账边界怎么切被问到的时候只能说当时没接触这个场景。这部分的总结是项目提问不会停留在你这个项目做了什么而是会一路追到极端情况下你的系统还会不会崩、数据还对得上吗。后面准备面试的人最好提前把类似问题全部写一遍答案不是背而是真的想清楚。3. AQS原理盲区从背八股到真正理解同步器3.1 面试原题AQS的核心思想是什么项目环节结束之后面试官问了一个我自认为准备过、但实际经不起追问的问题AQS的核心思想是什么我当时答的是维护一个volatile的state和一个FIFO等待队列获取锁失败就加入队列等待释放锁时唤醒队头节点。这种回答是纯粹的八股外壳。面试官接着问state是volatile的你解释一下为什么这里用volatile我答为了保证多线程可见性。他又问只是可见性吗AQS里的state在ReentrantLock的lock方法里是怎么用的这时候我才意识到他问的是state的整个状态流转而不是一个简单的volatile保证可见性。AQS的核心思想如果换个更贴近底层的说法应该是用state表示共享资源的状态用内置的FIFO队列记录等待获取资源的线程通过模板方法模式把获取/释放资源的逻辑留给子类实现。state的volatile是为了让所有线程读到最新的状态但真正关键的并不是volatile本身而是state配合CAS操作构成了一个无锁的同步机制。AQS的子类比如ReentrantLock只是通过tryAcquire/tryRelease方法来定义什么情况下state算获取成功排队和唤醒的骨架就被AQS抽出来了。3.2 CLH队列变体为什么AQS不用原版自旋面试官在这个点上深挖了知道CLH队列吗AQS的等待队列和原版CLH有什么差异原版CLH是一种基于隐式前驱节点的自旋锁每个线程通过一个局部变量轮询前驱节点的状态如果前驱释放了就认为自己获得了锁。它的核心特点是自旋等待适合临界区执行时间很短的场景。AQS对这种队列做了几个重要改造原版CLH的节点中维护的是线程自旋状态而AQS节点中每个线程在获取失败后直接Park休眠不再自旋原版CLH的队列是隐式的AQS变成了显式的双向链表每个节点有prev/next指针支持取消cancel操作AQS在节点入队之后如果发现前驱节点状态是SIGNAL当前线程就Park等待前驱释放时调用unpark唤醒。为什么AQS不用自旋因为ReentrantLock这类锁的临界区可能执行很耗时I/O、大量计算自旋一秒钟就意味着CPU空转浪费而且线程多了之后自旋会放大总线竞争。AQS选择入队SIGNAL标记Park挂起的模式以线程切换开销换取CPU资源不被空耗。这个权衡是面试官想听到的核心可惜我当时只说了AQS用队列加Park没有把和CLH的差异说透。3.3 state的三种含义与获取/释放流程AQS里state被设计成int类型但它在不同子类里语义完全不同这也是考察是否真懂的经典切入点。拿ReentrantLock举例state表示重入次数。lock时如果当前线程已经持有锁state1unlock时state-1减到0才真正释放锁。注意关键点这个判定逻辑在tryAcquire里而tryAcquire是子类实现的AQS只负责在acquire方法里统一调用tryAcquire失败才入队。再拿Semaphore举例state表示剩余许可证数量。acquire时CAS尝试对state做减法减成功了就拿到信号量失败则入队等待。这里没有持有者概念state只是一个计数器。CountDownLatch的state则代表剩余门闩数countDown时--stateawait时阻塞直到state归零。面试官在这里追问ReentrantLock的lock方法如果获取锁失败会走什么流程这个问题我倒是能答清楚尝试CAS把state从0改为1成功则直接得到锁并设置当前线程为独占线程失败则调用acquire - 先再试一次tryAcquire非公平锁会再插队一次失败后addWaiter入队入队后在acquireQueued里循环检查如果自己是队头节点的下一个节点排在第二位就再尝试获取锁否则检查前驱状态是否为SIGNAL如果是就Park自己被唤醒后再自旋检查直到成功获得锁。3.4 公平锁和非公平锁在AQS层面的差异面试官最后问公平锁和非公平锁的区别在AQS层面体现在哪里这个我答到了点上核心差异在tryAcquire的实现里。非公平锁在获取锁时不管队列里有没有排队的线程先CAS抢一次抢不到才进入队列而公平锁在获取锁前会先检查队列里是否有非当前线程的等待节点如果有就直接返回失败不参与竞争。面试官补充问了一个我之前没想过的问题非公平锁的性能为什么反而更好我想了一会儿给出的理解是非公平锁让新来的线程有概率直接抢占成功省去了线程挂起和唤醒的开销如果同一个线程连续抢占还能获得时间片连续性降低了上下文切换次数。在临界区竞争不激烈的情况下非公平锁吞吐量更大。这个点他认可了。卡壳的部分在于他问我CLH队列节点状态有几种时我只记得SIGNAL和CANCELLED把0状态和CONDITION、PROPAGATE完全忘记了。所以事后我把五种状态全补了一遍状态值含义00初始状态节点刚入队CANCELLED1线程已取消等待需要从队列中移除SIGNAL-1后继节点的线程需要被唤醒CONDITION-2节点在条件队列中等待PROPAGATE-3共享模式下唤醒需要被无条件传播AQS这一块给我的整体教训是八股可以背结论但面试官深挖的时候每一步收获的为什么才是真正拉开差距的地方。只看ReentrantLock是基于AQS实现的这种句子跟能写出获取锁的完整流程、讲清楚state的三种语义、解释公平非公平源码差异完全是两个level。4. Kafka原理盲区生产级消息队列的可靠性账本4.1 面试原题Kafka如何保证消息不丢失第三个大块是消息队列面试官直接抛出你简历里写了用Kafka做消息推送那Kafka的消息可靠性是怎么保证的这个问题我可以讲一部分但深度明显撑不住我答了生产者端用acksall保证leader和ISR都收到、broker端用复制因子保证副本、消费者端关闭自动提交改用手动提交。面试官听完反问我手动提交就一定能保证不丢吗如果消费者在提交位移之前挂了怎么办我答会重复消费所以要做幂等。他点头说那重复消费和消息丢失Kafka分别是怎么定义的这里暴露了我的盲区我把消息不丢失和消息不重复混在一起了。Kafka在正常配置下保证的是at-least-once至少一次也就是消息不丢失但可能重复。要做到exactly-once精确一次需要引入事务API——通过transaction_id和生产者幂等配合在跨分区写入时保证原子性。Kafka的消息可靠性可以拆成三段来看生产者到Broker不丢的保证是acksall 重试机制。注意如果启用重试还要把max.in.flight.requests.per.connection设为1或者开启幂等生产者否则同分区内会乱序Broker存储副本机制保证leader挂了之后ISR里的follower能顶上。min.insync.replicas设置ISR最小副本数避免在只剩leader时写入成功但数据无法复制Broker到消费者关键在于位移提交。消费者从分区拉到消息后先处理业务再提交位移。如果先提交位移后处理业务处理失败就丢消息如果先处理业务后提交位移可能会重复消费。我当时只把手动提交当作救命的答案没有去展开怎么提交才安全所以这一段的分数应该只拿了一半。4.2 消费组机制与分区再均衡面试官下一个问题是消费组是怎么分配分区的。我说了RangeAssignor和RoundRobinAssignor两个策略的名字但对再均衡的原理讲得很浅——只说了消费者加入或退出时会触发Rebalance但说不清具体过程。实际上这里涉及一个很重要的点Rebalance的代价非常高因为它要让所有消费者暂停消费重新分配分区然后位移要重新调整。基于这个原因Kafka引入了静态成员static membership机制通过配置group.instance.id让消费者重启时不触发Rebalance配合SessionTimeout就能减少频繁的再均衡。消费组协调还依赖**协调者Coordinator**这个概念每个消费者组对应一个Coordinator负责管理成员状态和位移。消费者启动后通过FindCoordinator请求找到协调者然后加入组、同步分配结果。如果消费组内成员心跳超时协调者会踢掉该成员并触发再均衡。面试官当时问了个再均衡期间消费会被暂停多久的问题我没答上来。正确理解是再均衡是分两轮的每个消费者需要完成JoinGroup和SyncGroup两次RPC整体耗时受到网络延迟和消费者负载的影响。如果消费者的poll()方法里处理逻辑很慢迟迟不响应再均衡请求就可能导致组内成员超时被判定下线。4.3 位移提交的三种方式与至少一次语义这一块我答得比上面好一点但依然有细节漏洞。位移提交有三种常见方式自动提交enable.auto.committrue默认每5秒提交一次。问题在于如果业务处理超过5秒某次poll之后自动提交了位移但数据处理失败了就会丢消息。手动异步提交commitAsync()提交失败不会重试可能丢失位移。但配合回调做记录。手动同步提交commitSync()提交失败会重试。在关键逻辑中优先使用。我漏掉的点是Kafka消费者在poll()中如果offset拉不到数据不会无限阻塞等待而是会根据fetch.max.wait.ms的设置返回空记录。所以不能把poll返回了记录当作消息肯定能处理完。很多生产事故是因为消费者处理异常但位移已经被提交导致消息静默丢失。面试官最后问你项目的推送系统里消费端怎么保证幂等的我说了用Redis的SETNX对消息ID做去重并说了key的过期时间设置。他没有继续深挖但我知道这块如果问Redis挂了怎么办我会露怯。所以后来我补了本地表级别的幂等兜底——通过数据库唯一键来做最终的幂等约束。4.4 我当时的卡壳点与系统补课路径这一节我最典型的卡壳点是面试官问Kafka的事务能保证什么时我只知道有个事务API完全说不出事务的原理。后来补课才知道Kafka事务的核心是Transaction Coordinator和隔离级别read_committed。事务开启后生产者的消息先写入事务日志提交时才向消费者暴露通过这种机制下游可以读到要么都成功要么都失败的消息序列。另外一个遗漏是Kafka的日志结构——面试官问Kafka的topic为什么叫topic消息在磁盘上是怎么组织的我只答了日志分段log segment没提到索引文件index和timeindex的作用。如果复习到源码层面会发现Kafka通过稀疏索引和二分查找来定位消息这也是Kafka高吞吐的基础。如果重新准备这一块我的建议是从生产到消费一条消息走过的完整路径去复习而不是单独背若干名词。一条消息从Producer的RecordAccumulator攒批发送、到Broker的Purgatory延迟请求匹配、到partition的Leader写入、到HW和LEO推进、再到消费端的fetch请求、最后到位移提交每一个环节都串联了Kafka的核心概念。面试官问到任何一个节点都能以这个路径为地图去展开而不是零散地回答。5. 两线程交替打印手撕代码的完整思考过程5.1 题目要求与常见变体面试官说来写个题两个线程一个线程打印1到26另一个线程打印A到Z要求交替输出输出结果是1 A 2 B 3 C ... 26 Z。时间限制大概十分钟只要求能跑。这类题在Java并发面试中出现频率非常高变体有两种一种是两个线程各打印一组字符交替输出另一种是两个线程轮流打印同一个自然数序列比如A打印奇数、B打印偶数。核心考察点是一致的线程间如何协作以及能不能用多种方式实现。我当时第一反应是直接用volatile变量做标志位加自旋因为代码量最小。但面试官说如果这个程序在CPU繁忙的服务器上跑自旋会很浪费资源你能不能用更合适的方案。我马上改成了synchronized加wait/notify写完后面试官又问除了synchronized有没有用Java并发包里的类的方案。于是又补了ReentrantLock加Condition的版本。5.2 第一版synchronized wait/notify 踩坑synchronized加wait/notify是我现场写的第一版思路是用一个共享锁对象线程A打印数字后唤醒线程B然后自己等待线程B打印字母后唤醒A自己等待。逻辑看起来很简单但我现场写的时候踩了一个经典的坑——忘记在while循环里检查标志位直接写了if判断。正确写法必须是while循环原因在于wait被唤醒后可能被虚假唤醒spurious wakeup也可能因为其他线程干扰而提前醒来。如果只用if判断线程醒来之后会继续往下执行而不是重新检查条件导致输出顺序错乱甚至重复打印。另一个坑是wait/notify必须放在synchronized块内否则会抛IllegalMonitorStateException。我在卷子上写的时候记得加锁但写完自己检查时差点在while循环外面加锁这种细节在面试高压下非常容易出错。给大家一个建议手写wait/notify之前先在脑子里过一遍wait释放锁、唤醒后重新获取锁、进入while循环再检查条件这一个流程再落笔。5.3 第二版ReentrantLock Condition 的clean方案可以用ReentrantLock加两个Condition来实现一个Condition给数字线程用一个给字母线程用。打印完数字之后signal字母线程的Condition然后自己await字母线程同理。这个方案的好处是不需要共享的flag——因为两个线程的条件天然不同一个等字母线程打印完一个等数字线程打印完用两个独立的Condition可以精准唤醒对应的线程不会出现notify随机唤醒的情况。这个版本的代码逻辑更清晰import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; public class AlternatePrint { private final ReentrantLock lock new ReentrantLock(); private final Condition digitCondition lock.newCondition(); private final Condition alphaCondition lock.newCondition(); private boolean digitTurn true; public void printDigit(int num) { lock.lock(); try { while (!digitTurn) { digitCondition.await(); } System.out.print(num ); digitTurn false; alphaCondition.signal(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { lock.unlock(); } } public void printAlpha(char ch) { lock.lock(); try { while (digitTurn) { alphaCondition.await(); } System.out.print(ch ); digitTurn true; digitCondition.signal(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { lock.unlock(); } } public static void main(String[] args) { AlternatePrint ap new AlternatePrint(); Thread t1 new Thread(() - { for (int i 1; i 26; i) { ap.printDigit(i); } }); Thread t2 new Thread(() - { for (char c A; c Z; c) { ap.printAlpha(c); } }); t1.start(); t2.start(); } }这里有一个必须注意的点while循环里用digitTurn作为条件判断不能在抛出InterruptedException后直接吞掉异常要重新设置中断标志位。面试现场我写的时候最后忘了finally里面的unlock后来检查时补上了。用ReentrantLock一定要养成unlock放finally的习惯这是和synchronized最大的不同——JVM不会帮你自动释放锁。5.4 面试官追问用volatile能做到吗现场推演写完之后面试官问刚才这个题用volatile标志位能实现吗有什么问题我说能实现。核心代码思路是定义了一个volatile boolean flag线程A判断flag为true则打印数字然后翻转flag线程B判断flag为false则打印字母然后翻转flag。这里要注意的是联合内存模型的问题volatile保证了可见性但不能保证原子性。如果两个线程同时读到flag为true这在自旋竞争时可能发生虽然volatile保证读到的都是最新值但两条线程在同一个瞬间看到的还是同一值理论上会让两个线程同时执行打印。实际上对这道题来说只要每次打印前后有一个线程翻转flag冲突的概率很低。但volatile实现本质上是忙等待busy-wait线程拿不到打印机会时会一直空转占用CPU。这也是面试官让我用synchronized/Lock的动机——让出CPU时间片避免无谓的自旋。volatile方案适合演示可见性但不适合真实生产环境下的线程协调。补充一个额外方案用Semaphore两个许可证也可以优雅地实现。维护两个SemaphoresemDigit初始为1semAlpha初始为0数字线程每次打印前acquire semDigit打印后release semAlpha字母线程正好反过来。这个方案思路极其清晰而且不需要判断标志位非常容易写对我当时没写出来事后复盘觉得这其实是最稳的解法。6. 复盘实习一面到底在筛选什么6.1 一面考察维度拆解百度Java后端实习一面问的东西从我自己的体验来看主要筛三个维度第一个是简历真实性。面试官会对你写在简历上的项目做深度压力测试。对账系统这种常见项目如果只是粗略看过别人文章而没实际做过问到幂等日切差异单流转这些细节时一定会露馅。所以实习简历上最安全的做法是只写自己真正参与并理解到可以现场画架构图的系统。第二个是知识纵深。AQS和Kafka的问题不算偏但问法克制。他们不问源码第几行做了什么而是问为什么这么设计换一种方案行不行。这要求你不能只背结论要理解每种技术的设计权衡。比如AQS为什么继承CLH又改造CLH、Kafka为什么选牺牲事务性换取吞吐量这些trade-off才是面试官真正在测试的点。第三个是代码功底和沟通能力。手写题难度并不高考的是在限时、约束、多轮修改的情况下能不能写出严谨的并发代码。我在写第一版wait/notify时暴露出的if条件判断问题说明对wait的语义还是没有形成肌肉记忆。这题不要求你写出最优解但要求你展示出思考过程。6.2 我给后续面试者的三条建议第一条准备项目时把项目的问题清单按正常流程、异常分支、性能瓶颈、扩展性四个维度各列出至少五个问题全部写在纸上一一作答。这个工作量大概需要一两天但比盲目刷题有价值得多。第二条并发和消息队列这两块是Java后端实习的绝对高频考点。尤其AQS、线程池的拒绝策略、Kafka的可靠性语义至少要能达到讲清一个方案再解释为什么选这个方案的程度。第三条手写代码前先在草稿纸上写清楚线程协作模型明确哪个线程负责唤醒对方自己等待的条件是什么用什么方式避免信号丢失。提前把wait/notify、Lock/Condition、Semaphore、volatile四种实现全部写一遍面试遇到同类题会非常从容。最后说点个人的体会面试被问到答不上来的问题并不可怕面试官在那一刻想看到的不是你会背多少而是面对不知道的时候能否诚实地说不清楚、再尝试用自己的知识推导。我这次一面虽然暴露了AQS和Kafka的不少盲区但最终拿到了下一轮的资格很大程度上是因为每一处卡壳之后我都没有放弃而是顺着已有知识继续把思路推到边界再把不确定的部分明确标记出来。这种知道自己哪里不知道的状态反而是实习面试里最需要展示给对方的东西。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑