Android 高级工程师面试:Java 基础知识 近1年高频追问 22 题

文章目录

学习建议

目标层级 建议
初级 掌握基础层 8 题:==/equals、集合 API、String 选型;工程联想看追问第 1 条
中级 通读全文;进阶层能讲清 HashMap 扩容、final/异常体系、内部类分类
高级 核心层 LruCache/拷贝/lambda/Stream 必答;能串联「相等语义→集合选型→资源关闭」工程链路

基础层(8 题)

#1 ==equals 有什么区别? 🔥

标准回答

== 比较引用是否同一对象,equals 比较业务语义是否相等。equals 默认同 ==,重写后按字段逻辑判断。

面试官可能继续追问

  • Android 里 ==equals 怎么用?

    比较 Bitmap 缓存 key 或自定义 User 对象时,必须明确用 equals 而非 ==,否则 HashMap 查不到或去重失败。

  • String 用 == 为什么有时为 true?

    字面量进字符串池,同一字面量共享引用;new String() 必为 false。

  • 重写 equals 不重写 hashCode 会怎样?

    相等对象哈希不同,HashMap/HashSet 行为错乱,键值对「丢失」。


#2 equalshashCode 的契约是什么? 🔥

标准回答

契约:equals 相等则 hashCode 必须相同;hashCode 相同对象可不等。HashMap 先算哈希定位桶,再用 equals 比对链表/树节点。

面试官可能继续追问

  • Android 里 equals/hashCode 注意什么?

    自定义 Parcelable 数据类或 Room 实体作 Map 键时,用 IDE 生成二者,避免缓存命中失败。

  • 为什么 HashMap 先比 hashCode 再比 equals

    先缩小桶范围,减少全字段比较,O(1) 均摊依赖此两步。

  • data class 自动生成够吗?

    Kotlin data class 已生成;Java 手写实体务必同步维护两方法。


#3 ArrayListLinkedList 怎么选? 🔥

标准回答

ArrayList 底层数组,随机访问 O(1),尾部增删均摊快;LinkedList 双向链表,头尾插删快但随机访问慢。

面试官可能继续追问

  • Android 里 ArrayListLinkedList 怎么选?

    列表数据几乎都用 ArrayListRecyclerView 适配器数据源;LinkedList 极少用,除非明确队列语义且规模可控。

  • ArrayList 扩容机制?

    容量不足时约 1.5 倍扩容并拷贝,频繁扩容有分配开销。

  • 主线程遍历大 LinkedList 会卡吗?

    会,get(i) 每次从头/尾遍历,大列表禁止随机索引访问。


#4 HashMapput/get 流程? 🔥

标准回答

put:算 hash 扰动 → 定位桶 → 无冲突直接放;冲突则链表或红黑树插入,超阈值扩容。get:同定位桶,再 equals 匹配。

面试官可能继续追问

  • Android 里 HashMap 怎么用?

    临时 ID 映射、内存缓存键值对常用 HashMap;需淘汰策略改 LruCache(内部也是 LinkedHashMap)。

  • 为什么容量建议取 2 的幂?

    (n-1) & hash 代替取模,位运算更快且分布均匀。

  • 多线程能直接用 HashMap 吗?

    不能,并发写可能死循环或丢数据,用 ConcurrentHashMap 或加锁。


#5 基本类型和包装类有什么区别? ⭐

标准回答

局部变量中的基本类型在栈帧内;作为对象字段或数组元素时随对象在堆。包装类是对象,有额外头与装箱开销。int/Integer 在 -128~127 有缓存池。

面试官可能继续追问

  • Android 里基本类型和包装类怎么选?

    性能敏感路径(游戏循环、大量坐标计算)优先基本类型数组;泛型集合必须用包装类或 Kotlin 专用数组。

  • 自动装箱的坑?

    循环里反复 Integer 装箱产生大量短生命周期对象,加重 GC。

  • Kotlin 还有这个问题吗?

    有,但可用 IntArray 等原生数组避免装箱。


#6 StringStringBuilderStringBuffer 怎么选? ⭐

标准回答

String 不可变,拼接产生中间对象;StringBuilder 可变、非线程安全;StringBuffer 线程安全但同步开销大。

面试官可能继续追问

  • Android 里 String 系列怎么选?

    主线程拼 JSON、日志、SQL 用 StringBuilder;常量文案用 String;多线程共享拼接极少见,一般各线程本地 StringBuilder

  • Kotlin 字符串拼接呢?

    编译器优化为 StringBuilder,简单 + 小量拼接可接受。

  • String.intern() 在 Android 慎用吗?

    慎用,池在堆中,滥用占内存且版本行为差异大。


#7 接口和抽象类的区别? ⭐

标准回答

接口定义能力契约,Java 8+ 可有 default/static 方法;抽象类可有状态与构造器,单继承。

面试官可能继续追问

  • Android 里接口和抽象类怎么选?

    OnClickListener 是接口;RecyclerView.Adapter 是抽象类承载模板方法。多实现用接口,共享基类逻辑用抽象类。

  • 为什么 Activity 是类不是接口?

    框架需统一生命周期与上下文实现,抽象类更合适承载模板。

  • Kotlin 接口还能有属性吗?

    可以有,但通常无 backing field,注意与 Java 互操作。


#8 重载和重写的区别? ⭐

标准回答

重载:同类同名不同参,编译期绑定。重写:子类覆盖父类实例方法,运行期多态。

面试官可能继续追问

  • Android 里重载和重写例子?

    onCreate(Bundle) 是重写;多个 findViewById 重载已随 ViewBinding 淡化。理解多态有助于读 RecyclerView 各类 onBindViewHolder 重写链。

  • 能重写 static 方法吗?

    不能,子类是隐藏(hide)而非多态重写。

  • @Override 有必要吗?

    有必要,父类签名变更时编译期即可发现错误。


进阶层(7 题)

#9 HashMap 扩容与树化机制? 🔥

标准回答

负载因子默认 0.75,元素数超 容量×0.75 则翻倍扩容并 rehash。单桶链表长度 ≥8 且表容量 ≥64 时链表转红黑树,≤6 退化为链表,防哈希攻击与过长查找。

面试官可能继续追问

  • Android 里 HashMap 扩容注意什么?

    大缓存 HashMap 应预估容量减少扩容;Profiler 看分配峰值是否来自频繁 rehash。

  • 为什么树化阈值是 8?

    泊松分布下链表达 8 的概率极低,权衡树维护成本。

  • 扩容是线程安全的吗?

    否,并发扩容可能导致 JDK7 环形链表问题,JDK8 改为尾插但仍非线程安全。


#10 ComparableComparator 有什么区别? ⭐

标准回答

Comparable 是类内部自然排序(compareTo),如 StringIntegerComparator 是外部比较器,可灵活定义多套规则。TreeMap/TreeSetCollections.sort 都依赖二者。

面试官可能继续追问

  • Android 里排序接口怎么用?

    列表按时间、优先级排序时,实体实现 Comparable 或传入 ComparatorDiffUtil 比较项也常自定义 Comparator 逻辑。

  • 能用 lambda 写 Comparator 吗?

    可以,Comparator.comparing(User::getName) 更简洁。

  • compareTo 返回 0 表示什么?

    相等;TreeSet 中返回 0 视为重复元素不插入。


#11 final 关键字能修饰什么?有什么作用? ⭐

标准回答

可修饰类(不可继承)、方法(不可重写)、字段(赋值一次)、局部变量(引用不可改指向)。

面试官可能继续追问

  • Android 里 final 怎么用?

    final 字段配合构造器可做不可变对象;匿名内部类访问外部局部变量要求 final 或 effectively final。

  • final 引用和不可变对象是一回事吗?

    不是,final 只保证引用不变,对象内容仍可能可变(如 final List 仍可 add)。

  • 为什么 lambda 捕获的变量要 effectively final?

    捕获的是值快照,允许隐式修改会破坏语义一致性。


#12 checked 异常和 unchecked 异常怎么区分? ⭐

标准回答

checked :编译期必须处理或声明抛出,如 IOExceptionunchecked :继承 RuntimeExceptionError,如 NullPointerExceptionIllegalArgumentException

面试官可能继续追问

  • Android 里异常怎么处理?

    现代代码倾向用 unchecked + 统一异常处理,IO 在边界层捕获转业务错误;不要在每个回调层层 throws。

  • 为什么有人反对 checked 异常?

    样板代码多,与函数式/回调风格不契合。

  • Retrofit 网络错误算哪种?

    调用方通过 onFailure 处理,不强制 checked 传播。


#13 static 成员在 Android 里要注意什么? ⭐

标准回答

静态字段属于类,生命周期与进程一致,不能持有 Activity/View 等短生命周期 Context ,否则易泄漏。静态方法无 this,适合工具函数。静态块做类加载期初始化。

面试官可能继续追问

  • Android 里 static 成员注意什么?

    Application 级单例可用静态,但应持 ApplicationContext 而非 Activity

  • 静态变量存在哪?

    类元数据区逻辑归属,所指对象实例仍在堆。

  • 静态方法能重写吗?

    不能,子类是隐藏(hide)而非多态。


#14 Java 有哪几种内部类?各有什么特点? ⭐

标准回答

成员内部类(持外部类引用)、静态内部类(不持外部实例)、局部内部类(方法内)、匿名内部类(一次性实现接口/抽象类)。

面试官可能继续追问

  • Android 里内部类怎么选?

    new OnClickListener(){} 是匿名内部类;非静态内部类隐式持有外部类引用,长生命周期回调优先静态内部类 + 弱引用或改用 lambda/协程。

  • 为什么推荐静态内部类?

    不隐式绑定外部 Activity,生命周期更易控。

  • lambda 和匿名内部类关系?

    lambda 是更简洁的函数式写法,捕获 this 同样有持有外部类风险。


#15 SparseArrayHashMap 怎么选? ⭐

标准回答

SparseArray 用两个数组存 int key 与 value,无装箱、内存更省,key 必须 int;HashMap 通用但 Integer 键有装箱开销。

面试官可能继续追问

  • Android 里 SparseArrayHashMap 怎么选?

    SDK 大量用 SparseArray/LongSparseArray 替代 Map<Integer, ?>,如 View 的 tag、监听器缓存;通用对象键仍用 HashMap

  • 查找复杂度?

    SparseArray 二分查找 O(log n);小数据常比 HashMap 更省。

  • ArrayMap 又是什么?

    Android 对少量映射的数组实现,适合小集合。


核心层(7 题)

#16 LruCacheHashMap 怎么选? 🔥

标准回答

LruCache 基于 LinkedHashMap 访问顺序,超容量自动淘汰最久未用条目,并可重写 sizeOf 按字节计;HashMap 无淘汰。

面试官可能继续追问

  • Android 里 LruCache 怎么用?

    图片内存缓存、解析结果缓存用 LruCache;临时请求映射用 HashMap。Glide 内部有更复杂分层缓存,思路同源。

  • sizeOf 返回什么?

    条目权重,Bitmap 缓存常返回像素字节数。

  • 线程安全吗?

    LruCache 本身非线程安全,多线程需外部同步或每线程实例。


#17 深拷贝和浅拷贝的区别? ⭐

标准回答

浅拷贝共享引用字段;深拷贝递归复制引用对象。Object.clone() 默认浅拷贝。

面试官可能继续追问

  • Android 里深/浅拷贝注意什么?

    传递可变 ListBitmap 配置时,浅拷贝可能导致一处修改处处可见;需要隔离时用拷贝构造、copy 数据类,或不可变集合。

  • 序列化算深拷贝吗?

    通过字节流重建对象,通常算深拷贝但性能差,注意 Parcelable 仍要设计字段。

  • Kotlin data class copy

    浅拷贝按字段复制,引用字段仍共享。


#18 BIO 和 NIO 在 Android 网络中的视角? ⭐

标准回答

BIO 阻塞 IO,一线程一连;NIO 多路复用,少量线程管多连接。OkHttp 早期基于阻塞 IO + 线程池;底层仍可见 socket 阻塞语义,上层用连接池与 Dispatcher 复用线程。

面试官可能继续追问

  • Android 网络里 BIO/NIO 怎么理解?

    应用层优先 OkHttp/Retrofit,不必手写 NIO,但要理解「阻塞调用别放主线程」。

  • 为什么 NetworkOnMainThreadException?

    StrictMode 禁止主线程阻塞网络 IO。

  • OkHttp Dispatcher 做什么?

    限制并发请求数,复用线程执行 Runnable


#19 Java 8 lambda 在 Android 中如何工作? 💡

标准回答

源码是 lambda,DEX 编译经 desugar 转为匿名类或 invokedynamic 兼容实现,取决于 AGP 版本。

面试官可能继续追问

  • Android 里 Java 8 lambda 怎么用?

    点击监听、Stream(API 24+ desugar)都依赖此机制。注意 lambda 捕获外部变量会形成合成类,不当持有 Activity 会泄漏。

  • 和 Kotlin lambda 比?

    Kotlin 默认更轻量,但捕获 this 同样有泄漏风险。

  • 能直接上 Java 17 语法吗?

    compileOptions 与 desugar 支持,AGP 8+ 支持更广。


#20 try-with-resourcesAutoCloseable 是什么? 💡

标准回答

Java 7+ 语法糖:在 try 括号内声明 AutoCloseable 资源,块结束自动 close(),异常时先抑制关闭异常再抛主异常。

面试官可能继续追问

  • Android 里 try-with-resources 怎么用?

    InputStream、数据库 Cursor、文件流应优先使用;Kotlin 对应 use {}。避免手动 finally 漏关导致文件描述符泄漏。

  • close 抛异常怎么办?

    try-with-resources 会 addSuppressed 保留两个异常信息。

  • 哪些 Android API 实现了 AutoCloseable

    ParcelFileDescriptorCursor 等,用完即关。


#21 Java 是值传递还是引用传递? 💡

标准回答

永远是值传递。传基本类型传值副本;传对象传的是引用的副本(地址值),通过副本仍可修改堆上同一对象,但不能让调用方变量的引用指向新对象。

面试官可能继续追问

  • Android 里值传递常见坑?

    方法内 list.add() 有效,但 list = new ArrayList() 不影响外部引用------面试高频考点。

  • 为什么很多人说是引用传递?

    把「传对象引用」口语化成引用传递,严格语义是传引用值的副本。

  • Kotlin 一样吗?

    一样,JVM 上语义相同。


#22 Java 8 Stream 在 Android 里能用吗? 💡

标准回答

部分 API 需 AGP desugar 且 minSdk 配合,小数据转换可用;列表很大或主线程路径慎用,因中间操作产生装箱与临时对象。

面试官可能继续追问

  • Android 里 Stream 能用吗?

    小数据转换可用;列表很大或主线程路径慎用。列表处理更常见 for 循环或 Kotlin 集合 API,不在 UI 热路径滥用。

  • 并行 parallelStream 能在 Android 用吗?

    一般不推荐,共享 ForkJoinPool 与 UI 线程竞争难控。

  • 和 Kotlin 序列比?

    Kotlin Sequence 惰性求值,更适合链式处理大数据。


面试策略速查

面试等级 建议掌握
初级 ★★★☆☆
中级 ★★★★☆
高级 ★★★★★

必背知识(🔥)

==/equals/hashCode 三连;ArrayList vs LinkedListHashMap put/get、扩容树化;SparseArray/LruCache 选型;final/异常体系。

高频追问

HashMap 为何容量 2 的幂;值传递 vs 引用传递;非静态内部类为何易泄漏;try-with-resources 关闭顺序。

加分项(💡)

lambda desugar;深/浅拷贝;BIO/NIO 与 OkHttp;Java 8 Stream 在 Android 的边界。

容易踩坑

重写 equalshashCodestatic 持 Activity;主线程网络/大列表;HashMap 多线程写;final List 误以为内容不可变。


完整链路一句通

Java 基础在 Android 里的主线 :用正确的相等语义与 hashCode 驱动 HashMap/LruCache/SparseArray 选型 → 掌握 final、异常、内部类与值传递等 OOP 规则 → 资源用 try-with-resources 关闭 → 网络与拼接遵守「阻塞不放主线程、少在热路径分配」。


相关推荐

Android 16(API Level 36)Activity 启动流程源码级解析

Android 高级工程师面试参考答案:性能优化

相关推荐
蓝胖的四次元口袋1 小时前
Java集合(4)
java·哈希算法
2501_948106912 小时前
计算机毕业设计之基于jsp教科研信息共享系统
java·开发语言·信息可视化·spark·课程设计
TanYYF2 小时前
spring ai入门教程二
java·人工智能·spring
SeeYa-J2 小时前
Spring IOC(Inversion of Control)
java·spring·rpc
私人珍藏库2 小时前
[Android] zip解压缩管理-全格式压缩包一键解压+打包
android·app·生活·工具·多功能
不会c+2 小时前
02-SpringBoot配置文件
java·spring boot·后端
AI 大模型学习不踩坑2 小时前
OpenClaw 完整教程:从安装到使用(官方脚本版)
java·人工智能·神经网络·机器学习·计算机视觉·自然语言处理·openclaw
雨白3 小时前
C语言:动态内存分配
android