小米Java基础专项面试:小米Android开发覆盖MIUI系统定制、IoT设备互联。技术栈偏底层,面试官喜欢问系统级原理和性能数据。技术方向偏向MIUI定制、IoT互联。特色专项包括MIUI系统定制与IoT设备开发。今天8道题覆盖小米Java基础专项面试核心考点。
Q1:GC算法和垃圾收集器对比?
基础算法:标记-清除(有碎片)、标记-复制(浪费一半空间但无碎片)、标记-整理(无碎片但慢)。分代策略:新生代用复制算法,老年代用标记-整理。收集器演进:Serial→Parallel→CMS(低停顿)→G1(Region化管理)→ZGC(<10ms停顿)。
深度解析:GC 目标是回收不可达对象。判断可达性用 GC Roots 引用链。Android ART 默认分代、并发、部分压缩,尽量缩短 Stop-The-World。容易踩的坑是:频繁创建临时对象会引发频繁 Minor GC,造成内存抖动和卡顿。;面试官常追问:追问:finalize 为什么不推荐?不确定性高且拖慢回收。。
追问:G1的Mixed GC是什么?同时回收新生代和部分标记为可回收的老年代Region,避免Full GC。
Q2:synchronized和ReentrantLock的区别?
synchronized是JVM关键字,自动释放锁;ReentrantLock是API类,必须手动unlock放finally里。ReentrantLock多了公平锁、可中断、tryLock超时、多Condition等高级特性。JDK6后synchronized大优化——偏向锁→轻量级锁→重量级锁的升级链让性能差距不大了。
val lock = ReentrantLock(true)
lock.lockInterruptibly()
try { /* critical section */ }
finally { lock.unlock() }
深度解析:这道题属于Java 基础的基础盘,面试官想确认你不是只会调 API。回答时先一句话给定义,再结合小米的澎湃 OS / HyperOS 与跨端体验。业务场景举例,最后补一个你做过的案例或踩过的坑,比背结论更得分。
追问:synchronized锁升级过程?无锁→偏向锁(记录线程ID)→轻量级锁(CAS自旋)→重量级锁(OS互斥量),不可逆降级。
Q3:动态代理的实现原理?
JDK动态代理通过Proxy.newProxyInstance在运行时生成代理类字节码,代理类继承Proxy并实现目标接口,方法调用转发到InvocationHandler.invoke()。只能代理接口。CGLIB通过继承目标类生成子类来代理,不能代理final类。
// Retrofit.create() 底层就是动态代理
val api: MyApi = retrofit.create(MyApi::class.java)
深度解析:这道题属于Java 基础的基础盘,面试官想确认你不是只会调 API。回答时先一句话给定义,再结合小米的澎湃 OS / HyperOS 与跨端体验。业务场景举例,最后补一个你做过的案例或踩过的坑,比背结论更得分。
追问:Retrofit怎么利用动态代理的?create()返回代理对象,调用接口方法时invoke()解析注解、构建Request、发起网络调用。
Q4:类加载机制和双亲委派?
五步流程:加载→验证→准备→解析→初始化。双亲委派:加载请求先委托父加载器,父找不到才自己加载。层级:Bootstrap→Ext→AppClassLoader。目的是防止核心类被篡改、避免重复加载。
class MyClassLoader : ClassLoader() {
override fun loadClass(name: String): Class<*> =
if (name.startsWith("com.myapp")) findClass(name)
else super.loadClass(name) }
深度解析:这道题属于Java 基础的基础盘,面试官想确认你不是只会调 API。回答时先一句话给定义,再结合小米的澎湃 OS / HyperOS 与跨端体验。业务场景举例,最后补一个你做过的案例或踩过的坑,比背结论更得分。
追问:Tomcat为什么要打破双亲委派?不同Web应用可能有同名类的不同版本,需要隔离加载。
Q5:JVM调优实战:OOM排查和GC调优?
OOM排查流程:1)jmap -dump:format=b,file=heap.bin dump堆;2)MAT打开分析Leak Suspects报告;3)找大对象——Dominator Tree看Retained Heap最大的对象;4)看引用链——GC Root到泄漏对象的路径。GC调优: Young GC频繁→增大新生代(-Xmn);Full GC频繁→检查老年代是否有大对象或内存泄漏;STW过长→从CMS换G1或ZGC。关键参数:-Xms=-Xmx(避免动态扩容)、-XX:+UseG1GC、-XX:MaxGCPauseMillis=200。
# 常用JVM诊断命令
jps -l # 查看Java进程
jstat -gcutil <pid> 1000 10 # 每秒打印GC统计10次
jmap -histo <pid> | head -20 # 对象直方图Top20
# 线上诊断:arthas boot <pid>
追问:ZGC为什么能做到<10ms停顿?染色指针+读屏障——在对象引用中嵌入GC标记信息,不需要修改对象头,读屏障在引用读取时做并发整理。
Q6:泛型擦除和桥接方法?
Java泛型是编译期特性——编译后泛型类型信息被擦除,List和List在运行时都是List。擦除导致的问题:运行时无法获取泛型类型(反射getGenericType可以)、不能new T()、不能new泛型数组。桥接方法是编译器为解决擦除后多态冲突自动生成的——子类重写泛型方法时参数类型被擦除,编译器生成一个参数为Object的桥接方法调用真正的方法。
// 编译后实际生成的方法
// bridge: public void set(Object val) { set((String) val); }
public void set(String val) { ... }
追问:Kotlin的reified泛型为什么能做到运行时获取类型?配合inline函数——编译时把泛型类型内联到调用处,类型信息在编译期就确定了。
Q7:volatile关键字的原理和作用?
volatile保证可见性和有序性,不保证原子性。底层靠内存屏障——写前加StoreStore屏障,写后加StoreLoad屏障。JMM规定:volatile变量写后强制刷新到主内存,读时强制从主内存加载,禁止指令重排序。
@Volatile private var instance: Singleton? = null
追问:为什么i++不能靠volatile保证?i++是读-改-写三步操作,volatile只管各自步骤的可见性,不管原子性。
Q8:Stream API的惰性求值和短路操作?
Stream操作分中间操作和终端操作。中间操作(filter/map等)是惰性的——只有终端操作(forEach/collect/count等)触发时才真正执行。短路操作:anyMatch找到匹配立即返回、limit截断流、findFirst找到第一个就停。这意味着stream.filter(...).limit(5)在找到5个后就不再处理后续元素——不是遍历整个集合再取5个。
val result = list.parallelStream()
.filter { it > 0 }
.map { it * 2 }
.limit(10)
.collect(Collectors.toList())
追问:parallelStream什么时候该用?数据量大(>1万)且操作CPU密集时用。小数据集线程切换开销大于收益。注意线程安全。
面试备战建议
Java 基础是底线,集合、并发、JVM、GC 必问且会追到源码。
HashMap/ConcurrentHashMap、synchronized/volatile、GC 算法要滚瓜烂熟。
准备 JVM 调优或 OOM 排查的真实案例。
下一篇进入小米Kotlin专项——data class自动生成了什么、Kotlin空安全、Kotlin value class。
觉得有用的同学扣1,你面试被追问最狠的是哪道题?
本系列连载中,关注不迷路,下一篇:小米Kotlin专项面经:data class自动生成了什么、Kotlin空安全、Kotlin value class
系列简介:Android大厂面经连载,覆盖字节跳动、腾讯、阿里、美团等40+企业,从初级到架构师全岗位覆盖。每篇文章包含真实面试题+详细答案+代码示例,帮你拿到大厂Offer。