当前位置: 首页 > news >正文

面试官与水货程序员的Battle:西安电子科技大学cjc同学的大厂面试奇遇记

面试官与水货程序员的Battle:西安电子科技大学cjc同学的大厂面试奇遇记

楔子

2024年的一个明媚下午,位于北京中关村某互联网大厂的会议室里,气氛略显紧张。今天要面试的是一位来自西安电子科技大学的应届生——cjc同学。面试官是一位经验丰富、技术功底深厚的老炮儿,面容严肃,眼神犀利。

而我们的主角cjc同学,穿着一身略显宽松的格子衬衫,背着一台充满贴纸的MacBook,信心满满地推开了会议室的门。

"请坐,我是今天的面试官,先简单介绍一下自己吧。"面试官推了推眼镜淡淡说道。

"面试官好,我叫cjc,来自西安电子科技大学,计算机科学与技术专业,喜欢写代码,尤其喜欢Java..."cjc同学顿了顿,"以及切图。"

面试官眉头微皱:"好了,我们开始吧。"

第一轮:Java基础与集合

面试官提问

"那我们先从基础开始。HashMap的底层数据结构是什么?"

cjc同学眼睛一亮:"这个我会!HashMap底层是数组+链表+红黑树,当链表长度超过8且数组长度大于64时,会转换成红黑树。"

"不错,那ArrayList和LinkedList的区别是什么呢?"

"ArrayList底层是数组,查询快,增删慢;LinkedList底层是双向链表,增删快,查询慢。"cjc对答如流。

"很好,HashMap是线程安全的吗?"

"不是,线程不安全。ConcurrentHashMap是线程安全的。"cjc回答得很溜。

面试官点了点头:"基础还可以,那我来问一下,HashMap在JDK1.7和JDK1.8有什么区别?"

cjc同学陷入了沉思:"这个...JDK1.7是用数组+链表,JDK1.8是...那个...加入了红黑树...然后...那个头插法改成了尾插法?"

"不错,还知道头插尾插,那你知道为什么改成尾插吗?"

cjc抓了抓头:"为了...解决并发问题?"

"主要是为了避免环形链表导致的死循环问题。好了,下一题,HashMap的扩容机制是怎样的?"

cjc开始结巴了:"就是...当元素数量超过负载因子乘以容量的时候...会扩容...扩容是2倍...然后重新计算索引...那个..."

"threshold了解吗?"

"threshold就是...临界值...大于这个值就扩容...等于负载因子乘以容量..."cjc勉强回答。

面试官没有继续为难:"行,基础部分过得去,我们继续。"

第二轮:JUC与多线程

面试官提问

"好,我们来聊聊多线程。创建线程的方式有哪几种?"

"继承Thread类,实现Runnable接口,实现Callable接口,还有使用线程池!"cjc回答得很积极。

"可以,那Thread和Runnable的区别是什么?"

"Thread是类,Runnable是接口。Thread继承了Runnable...不对,Thread实现了Runnable...实现Runnable更好,因为Java单继承...那个..."cjc有点混乱。

"Thread实现了Runnable接口,所以Thread本质上也是Runnable。那我问你,为什么有了Runnable还要有Callable?"

cjc想了想:"Callable可以有返回值,Runnable没有。Callable可以抛异常,Runnable不行。"

"对,那线程池的核心参数有哪些?"

" corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler!"cjc扳着手指头数。

"很好,线程池的执行流程是什么?"

cjc开始比划:"首先...任务来了...先看核心线程数有没有满...没满就创建核心线程...满了就加入队列...队列满了就创建临时线程...临时线程也满了就拒绝任务..."

"回答得不错。那我问你一个实际场景,如果你的系统需要执行大量短耗时任务,你会怎么选择线程池?"

cjc愣住了:"这个...用newFixedThreadPool?"

"短耗时任务建议使用CachedThreadPool或者自定义线程池,核心线程数可以设大一点。好了,volatile关键字了解吗?"

"volatile是轻量级同步机制,可以保证可见性和有序性,不能保证原子性。"cjc回答得很标准。

"那synchronized和Lock的区别是什么呢?"

cjc开始胡扯:"synchronized是Java内置的,Lock是API层面的...synchronized会自动释放锁,Lock需要手动释放...synchronized是阻塞式的...Lock可以非阻塞...那个..."

"好了,synchronized是隐式锁,Lock是显式锁,这是最主要的区别。"

cjc如释重负:"对对对,就是这样。"

第三轮:框架与分布式

面试官提问

"好,我们聊聊Spring和SpringBoot。SpringBoot的自动配置原理是什么?"

cjc绞尽脑汁:"就是...@SpringBootApplication注解...里面包含@EnableAutoConfiguration...然后...那个...SpringFactoriesLoader...加载META-INF/spring.factories..."

"还有呢?"

"还有...@Conditional...条件装配...根据环境判断..."cjc勉强挤出几句。

"基本正确,那Spring的事务传播行为有哪些?"

"REQUIRED、REQUIRES_NEW、SUPPORTS、NOT_SUPPORTED...还有MANDATORY、NEVER、NESTED..."cjc背得很熟。

"OK,那MyBatis的#{}和${}有什么区别?"

"#{}是预编译SQL,防止SQL注入,${}是直接拼接SQL,有SQL注入风险。"cjc回答得很快。

"不错,那Redis的数据类型有哪些?"

"String、Hash、List、Set、ZSet,还有HyperLogLog、Bitmap、Geospatial..."cjc扳着指头数。

"了解很全面,那Redis的持久化机制呢?"

"RDB和AOF,RDB是快照,AOF是追加日志..."cjc回答得很溜。

"好,那我来问一下实际的,如果Redis某个key过期了,但是内存还没释放,是什么原因?"

cjc开始瞎猜:"那个...可能是内存碎片?或者...那个...淘汰策略没触发?"

"这种情况通常是因为Redis的淘汰策略设置为no-eviction,或者内存分配器有问题。好了,MySQL的索引结构是什么?"

"B+树!"cjc毫不犹豫。

"那为什么用B+树而不是B树?"

cjc又卡住了:"因为...B+树更适合范围查询...所有数据都在叶子节点...非叶子节点只存索引...树更矮...查询更快..."

"回答得不错,那MySQL的事务隔离级别有哪些?"

"READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ、SERIALIZABLE。默认是REPEATABLE READ。"

"好,那幻读是什么?是怎么解决的?"

cjc开始胡说:"幻读就是...就是读取的时候出现了幻觉...是MVCC和Next-Key Lock解决的...不对...是间隙锁...就是那个..."

"行了,大概知道就行。最后一个问题,你了解DDD领域驱动设计吗?"

cjc彻底懵了:"DDD...就是...领域驱动设计...那个...有实体、值对象、聚合根、领域服务...分层架构...基础设施层...应用层...领域层..."

面试官抬手看了看表:"好了,今天的面试就到这儿。你先回去等通知吧。"

cjc同学收拾东西:"面试官,那个...我表现怎么样?"

"还可以,基础挺扎实的,后面部分还需要加强。回去等通知吧。"

"好的,谢谢面试官!"cjc同学起身离开,心里默默祈祷能收到offer。

技术答案详解

第一轮答案

1. HashMap的底层数据结构

JDK1.7:数组 + 链表(头插法)

JDK1.8:数组 + 链表 + 红黑树(尾插法)

  • 当链表长度超过8且数组长度大于64时,链表转换为红黑树
  • 红黑树节点数小于等于6时,退化为链表
  • 使用尾插法是为了避免并发情况下形成环形链表导致死循环
2. ArrayList vs LinkedList

| 特性 | ArrayList | LinkedList | |------|-----------|------------| | 底层结构 | 动态数组 | 双向链表 | | 随机访问 | O(1) | O(n) | | 头部插入/删除 | O(n) | O(1) | | 尾部插入/删除 | 均摊O(1) | O(1) | | 内存占用 | 小,仅存数据 | 大,需存前后指针 |

3. HashMap线程不安全的原因
  • JDK1.7:并发扩容时使用头插法,可能形成环形链表,导致死循环
  • JDK1.7:put操作时可能导致数据覆盖
  • JDK1.8:并发put可能导致数据覆盖,但解决了环形链表问题
  • 建议使用ConcurrentHashMap
4. HashMap扩容机制
  • 负载因子默认为0.75
  • 当size >= threshold(capacity * loadFactor)时触发扩容
  • 扩容为原来容量的2倍
  • 重新计算每个元素的索引位置(hash and (newCap - 1))
  • threshold也会翻倍

第二轮答案

1. 创建线程的方式
  • 继承Thread类
  • 实现Runnable接口
  • 实现Callable接口(配合FutureTask或线程池)
  • 使用线程池(Executors或ThreadPoolExecutor)
2. Thread vs Runnable
  • Thread是类,Runnable是接口
  • Thread实现了Runnable接口
  • Runnable更适合资源共享,避免单继承限制
  • 两种方式都需要通过start()启动线程
3. Runnable vs Callable

| 特性 | Runnable | Callable | |------|-----------|----------| | 返回值 | void | V | | 异常 | 不可抛异常 | 可抛异常 | | 执行方法 | run() | call() |

4. 线程池核心参数
  • corePoolSize:核心线程数
  • maximumPoolSize:最大线程数
  • keepAliveTime:空闲线程存活时间
  • unit:时间单位
  • workQueue:任务队列
  • threadFactory:线程工厂
  • handler:拒绝策略
5. 线程池执行流程
  1. 提交任务,检查核心线程数是否已满
  2. 未满,创建核心线程执行任务
  3. 已满,将任务加入队列
  4. 队列满,检查最大线程数是否已满
  5. 未满,创建临时线程执行任务
  6. 已满,执行拒绝策略
6. volatile关键字
  • 可见性:一个线程修改volatile变量,其他线程立即可见
  • 有序性:禁止指令重排序
  • 不保证原子性:如i++操作
  • 底层实现:Lock前缀指令 + MESI缓存一致性协议
7. synchronized vs Lock

| 特性 | synchronized | Lock | |------|--------------|------| | 级别 | JVM内置 | API | | 释放 | 自动释放 | 手动释放 | | 阻塞 | 阻塞式 | 可tryLock非阻塞 | | 公平 | 非公平 | 可设置公平/非公平 | | 条件变量 | 无 | 可多个wait/notify |

第三轮答案

1. SpringBoot自动配置原理
  • SpringBootApplication = SpringBootConfiguration + EnableAutoConfiguration + ComponentScan
  • EnableAutoConfiguration = AutoConfigurationPackage + Import(AutoConfigurationImportSelector.class)
  • SpringBoot启动时通过SpringFactoriesLoader加载META-INF/spring.factories中的自动配置类
  • 通过Conditional系列注解进行条件装配
  • 按需加载,自动配置类不会全部加载
2. Spring事务传播行为
  • REQUIRED:如果当前有事务,加入;否则创建新事务(默认)
  • REQUIRES_NEW:总是创建新事务,挂起当前事务
  • SUPPORTS:如果当前有事务,加入;否则非事务执行
  • NOT_SUPPORTED:非事务执行,挂起当前事务
  • MANDATORY:必须在事务中执行,否则抛异常
  • NEVER:必须不在事务中执行,否则抛异常
  • NESTED:嵌套事务,使用Savepoint
3. MyBatis #{} vs ${}
  • #{}:预编译SQL参数占位,防止SQL注入
  • ${}:直接字符串拼接,存在SQL注入风险
  • #{}会加上单引号,${}不会
  • 动态表名、排序字段等需要使用${}
4. Redis数据类型
  • String:字符串、计数器、缓存
  • Hash:对象存储
  • List:消息队列、列表
  • Set:去重、标签、好友关系
  • ZSet:排行榜、延迟队列
  • HyperLogLog:基数统计
  • Bitmap:位图、签到
  • Geospatial:地理位置
5. Redis持久化机制

RDB(快照)

  • 定时生成数据快照
  • 适合备份和灾难恢复
  • 可能会丢失最近数据

AOF(追加文件)

  • 记录所有写操作命令
  • 可配置同步策略(always/everysec/no)
  • 文件较大,恢复慢

建议:同时开启RDB和AOF

6. MySQL索引结构

B+树

  • 所有数据都在叶子节点
  • 叶子节点之间用双向链表连接
  • 适合范围查询
  • 树高通常为2-4层

为什么不用B树?

  • B+树非叶子节点不存数据,同样数据量下树更矮
  • B+树范围查询更高效,只需遍历叶子节点链表
  • B+树查询更稳定,必须到叶子节点
7. MySQL事务隔离级别

| 隔离级别 | 脏读 | 不可重复读 | 幻读 | |----------|------|------------|------| | READ UNCOMMITTED | 是 | 是 | 是 | | READ COMMITTED | 否 | 是 | 是 | | REPEATABLE READ | 否 | 否 | 是 | | SERIALIZABLE | 否 | 否 | 否 |

默认级别:REPEATABLE READ

8. 幻读及解决

幻读:同一事务中,两次查询结果不一致,第二次读到了新插入的行

解决方案:

  • MVCC:多版本并发控制,通过版本号控制
  • Next-Key Lock:行锁 + 间隙锁,锁定范围包括记录和间隙
  • SERIALIZABLE:串行化,完全避免幻读

写在最后

cjc同学的面试之旅虽然有些地方回答得磕磕巴巴,但整体表现还算中规中矩。面试官那句"回去等通知",既是常规话术,也给了cjc一丝希望。

技术面试就是这样,会的问题要答准,不会的问题也要尽量扯一扯,态度诚恳最重要。各位正在找工作的小伙伴们,基础知识一定要扎实,项目经验要能讲清楚,面试官一般不会为难你的。

最后,祝cjc同学能顺利拿到offer,也祝各位读者面试顺利!

代码改变世界,面试改变命运,且面且珍惜。

本文纯属娱乐虚构,如有雷同纯属巧合。

http://www.cnnetsun.cn/news/1298737.html

相关文章:

  • weixin229学生资助在线管理软件开发微信小程序ssm(文档+源码)_kaic
  • 利用LLM优化亚马逊产品列表的技术实践
  • 专科生也能用!千笔,倍受青睐的AI论文写作软件
  • AI写论文大比拼!4款AI论文生成软件,哪款适合写期刊论文?
  • 低查重AI教材生成攻略,从选题到完稿,AI工具全程助力!
  • 第0011课 - 把画坦克也装进魔法盒子
  • 基于N32G430的USB功率监测与RGB状态指示嵌入式系统
  • Flutter鸿蒙开发:跨平台技术栈融合下的机遇与挑战
  • 拼多多的便宜2元/斤的五常大米能买吗?——关于拼多多平台销售的大米是否建议“尽量少吃”,需要结合实际情况理性分析。以下是一些可能的原因和相关建议:
  • Whisper语音识别镜像部署指南:GPU加速,响应快,小白友好
  • PCB三维叠层文化硬件设计:醒狮瑞象嵌入式装置
  • CLIP-GmP-ViT-L-14算力适配指南:不同显存配置下的参数调优
  • 专用语音IC驱动的电子圣诞树硬件设计
  • DeepSeek-R1-Distill-Qwen-1.5B上线只需5步:生产环境部署详细步骤
  • mPLUG-Owl3-2B多模态工具:Anaconda环境配置指南
  • Cosmos-Reason1-7B效果展示:手术视频中识别器械操作规范性与组织损伤风险
  • SmolVLA跨语言代码翻译效果:Java与Python互转示例
  • AudioSeal Pixel Studio效果展示:高清保真水印嵌入前后频谱对比图集
  • M2LOrder模型Keil5开发STM32入门:工程创建与调试全流程
  • 造相-Z-Image问题解决:RTX 4090运行全黑图、显存不足的排查方法
  • RGB智能风压式笔电散热器设计与实现
  • Qwen3-ForcedAligner-0.6B开源镜像详解:1.7GB显存占用,纯本地CTC对齐方案
  • LiuJuan Z-Image高性能部署:4090D显卡上BF16算力利用率提升30%实践
  • 千问3.5-27B生产环境部署:日志分级+端口监听+服务健康检查
  • SiameseAOE模型Java开发集成指南:SpringBoot微服务中的信息抽取应用
  • Lychee-Rerank-MM惊艳效果:同一商品图在不同指令(推荐/比价/评测)下的重排序差异
  • SecGPT-14B效果展示:对Cisco ASA日志进行NAT转换链路与ACL策略冲突分析
  • 小白也能懂:Z-Image-ComfyUI工作原理与快速出图技巧
  • Asian Beauty Z-Image Turbo 在Ubuntu服务器上的生产环境部署
  • 学术党必备!深求·墨鉴精准识别公式表格保姆级教程