1. 面试背景与公司概况
爱奇创新是一家位于广州的互联网科技企业,主要业务方向为数字内容平台开发。作为典型的"小而美"技术团队,他们的技术栈集中在Java生态体系,对实习生的考察重点放在基础知识的扎实程度和实际应用能力上。我应聘的是Java后端开发实习岗位,整个面试流程包括笔试、技术面两轮和HR面,全程约3小时。
这类中小型互联网公司的面试特点非常鲜明:不会过分追求高并发、分布式等"大厂标配"问题,但会对Java核心机制、常用框架原理和基础组件的实际应用进行深度挖掘。下面我就从笔试开始,完整复盘这次技术考察的全过程。
2. 笔试环节重点题目解析
2.1 线程池参数动态调整
题目要求实现一个可动态修改核心参数的线程池,并说明在Web服务中如何安全应用。这题考察的是对ThreadPoolExecutor源码的理解程度。
public class DynamicThreadPool extends ThreadPoolExecutor { // 构造函数使用标准参数 public DynamicThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); } // 动态修改核心线程数 public void setCorePoolSize(int corePoolSize) { super.setCorePoolSize(corePoolSize); } // 动态修改最大线程数 public void setMaximumPoolSize(int maximumPoolSize) { super.setMaximumPoolSize(maximumPoolSize); } }关键实现点:
- 继承ThreadPoolExecutor重写setter方法
- 修改时需考虑当前活跃线程数
- 生产环境建议配合监控指标使用
注意:直接在线程池运行期间调整参数可能导致任务拒绝,实际项目中应该先暂停新任务提交,等待现有任务完成后再修改。
2.2 装饰器模式实践题
要求用装饰器模式实现一个具有缓存能力的文件读取器。这个题目很好地考察了设计模式的实际应用能力。
public interface FileReader { String read(String path); } public class BasicFileReader implements FileReader { @Override public String read(String path) { // 基础文件读取实现 } } public class CachedFileReader implements FileReader { private final FileReader delegate; private final Map<String, String> cache = new ConcurrentHashMap<>(); public CachedFileReader(FileReader delegate) { this.delegate = delegate; } @Override public String read(String path) { return cache.computeIfAbsent(path, delegate::read); } }装饰器模式的核心优势在于:
- 符合开闭原则,无需修改原有类
- 可以多层嵌套添加功能
- 比继承更灵活
3. 技术一面深度复盘
3.1 Spring IOC容器工作原理
面试官从@Autowired注解开始,逐步深入到IOC容器的实现原理。这里我画出了关键流程:
BeanDefinition加载阶段
- 配置源解析(XML/注解/JavaConfig)
- Bean定义注册到DefaultListableBeanFactory
依赖注入阶段
- 构造器解析(构造器注入优先)
- 字段注入(通过反射实现)
- 方法注入(@Bean方式)
生命周期回调
- Aware接口回调
- BeanPostProcessor处理
- InitializingBean/init-method
实际工程经验:在Spring Boot项目中,自动配置是通过@Conditional系列注解配合spring.factories实现的,这是IOC容器的高级应用场景。
3.2 Redis签到系统设计
设计一个支持百万级用户的连续签到系统,主要考察Redis数据结构的灵活运用。
// 使用BitMap实现签到记录 public class SignInService { private final Jedis jedis; public void signIn(long userId) { String key = "sign:" + LocalDate.now().getMonthValue(); jedis.setbit(key, userId, true); // 连续签到计算 String continueKey = "continue:" + userId; long continueDays = jedis.incr(continueKey); jedis.expire(continueKey, 48*60*60); // 两天有效期 } }优化方案:
- 按月分片存储签到数据
- 使用HyperLogLog统计UV
- 对于连续签到采用滑动过期策略
4. 技术二面项目深度考察
4.1 ES分词器定制开发
面试官对我在校项目中的ES分词器改造很感兴趣,这里涉及到几个关键技术点:
自定义Analyzer组成
- Character Filter(处理HTML标签等)
- Tokenizer(核心分词逻辑)
- Token Filter(停用词处理等)
热更新词典实现
public class DynamicDict implements Reloadable { private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); public void init() { executor.scheduleAtFixedRate(this::reload, 0, 30, TimeUnit.MINUTES); } @Override public void reload() { // 从数据库加载最新词典 } }- 性能优化要点
- 采用双缓冲机制避免 reload 阻塞查询
- 对核心词典使用ConcurrentHashMap存储
- 限制单term最大长度防止OOM
4.2 线程池使用不当案例
面试官给出一个生产环境案例:某接口突然出现大量超时,要求分析可能原因。这实际上考察的是线程池参数的合理配置。
问题现象:
- 接口平均响应时间从50ms飙升到2000ms
- 监控显示线程池活跃度100%
- 队列堆积超过1000个任务
根本原因分析:
- 核心线程数设置过小(4核机器只配了2个核心线程)
- 使用无界队列导致OOM风险
- 没有合理的拒绝策略
解决方案:
new ThreadPoolExecutor( Runtime.getRuntime().availableProcessors(), // 核心线程数 Runtime.getRuntime().availableProcessors() * 2, // 最大线程数 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100), // 有界队列 new ThreadPoolExecutor.CallerRunsPolicy() // 饱和策略 );5. HR面中的技术沟通
5.1 技术学习路径规划
虽然HR面以软性问题为主,但技术型HR还是会关注:
- 如何保持技术更新(我分享了每周精读2篇源码的心得)
- 遇到技术瓶颈时的解决方式(调试技巧+社区交流)
- 对新技术的学习方法论(官方文档->示例代码->原理剖析)
5.2 团队协作能力考察
通过以下方面展示技术协作能力:
- Git使用规范(feature分支+rebase操作)
- Code Review要点(重点检查异常处理和日志)
- 技术文档写作技巧(API文档的版本管理)
6. 面试准备建议
6.1 知识体系构建
针对中小厂Java面试,建议重点准备:
Java基础
- 集合源码(HashMap扩容机制)
- 并发工具(AQS实现原理)
- JVM内存模型
框架原理
- Spring循环依赖解决
- MyBatis缓存体系
- Tomcat连接池配置
中间件
- Redis持久化策略
- Kafka消息可靠性
- ES写入优化
6.2 实战项目提炼
准备2-3个技术亮点:
- 性能优化案例(如接口从500ms优化到50ms)
- 疑难问题排查(如内存泄漏定位)
- 技术创新点(如自定义注解开发)
6.3 模拟面试训练
建议进行:
- 白板编程练习(手写LRU缓存)
- 系统设计演练(短链服务设计)
- 行为面试准备(STAR法则运用)
7. 面试后的技术反思
通过这次面试,我总结了几个需要改进的技术点:
对Spring事件机制的理解不够深入
- 需要补ApplicationEventPublisher的实现原理
- 研究@EventListener的线程模型
Redis集群方案掌握不全面
- Codis与原生Cluster的区别
- 跨机房同步方案
ES聚合查询性能优化
- 使用composite aggregation替代terms
- 合理设置fielddata
最后给Java实习求职者的建议是:中小厂面试更看重基础知识的深度和解决实际问题的能力,不需要盲目追求高并发的场景,但要确保每个技术点都能讲清楚实现原理和应用场景。我的准备方法是每天精读一个JDK类的源码,并思考其在项目中的实际应用场景。