文章目录
- 学习建议
- [基础层(8 题)](#基础层(8 题))
-
- [#1 `==` 和 `equals` 有什么区别? 🔥](#1
==和equals有什么区别? 🔥) - [#2 `equals` 和 `hashCode` 的契约是什么? 🔥](#2
equals和hashCode的契约是什么? 🔥) - [#3 `ArrayList` 和 `LinkedList` 怎么选? 🔥](#3
ArrayList和LinkedList怎么选? 🔥) - [#4 `HashMap` 的 `put`/`get` 流程? 🔥](#4
HashMap的put/get流程? 🔥) - [#5 基本类型和包装类有什么区别? ⭐](#5 基本类型和包装类有什么区别? ⭐)
- [#6 `String`、`StringBuilder`、`StringBuffer` 怎么选? ⭐](#6
String、StringBuilder、StringBuffer怎么选? ⭐) - [#7 接口和抽象类的区别? ⭐](#7 接口和抽象类的区别? ⭐)
- [#8 重载和重写的区别? ⭐](#8 重载和重写的区别? ⭐)
- [#1 `==` 和 `equals` 有什么区别? 🔥](#1
- [进阶层(7 题)](#进阶层(7 题))
-
- [#9 `HashMap` 扩容与树化机制? 🔥](#9
HashMap扩容与树化机制? 🔥) - [#10 `Comparable` 和 `Comparator` 有什么区别? ⭐](#10
Comparable和Comparator有什么区别? ⭐) - [#11 `final` 关键字能修饰什么?有什么作用? ⭐](#11
final关键字能修饰什么?有什么作用? ⭐) - [#12 checked 异常和 unchecked 异常怎么区分? ⭐](#12 checked 异常和 unchecked 异常怎么区分? ⭐)
- [#13 `static` 成员在 Android 里要注意什么? ⭐](#13
static成员在 Android 里要注意什么? ⭐) - [#14 Java 有哪几种内部类?各有什么特点? ⭐](#14 Java 有哪几种内部类?各有什么特点? ⭐)
- [#15 `SparseArray` 和 `HashMap` 怎么选? ⭐](#15
SparseArray和HashMap怎么选? ⭐)
- [#9 `HashMap` 扩容与树化机制? 🔥](#9
- [核心层(7 题)](#核心层(7 题))
-
- [#16 `LruCache` 和 `HashMap` 怎么选? 🔥](#16
LruCache和HashMap怎么选? 🔥) - [#17 深拷贝和浅拷贝的区别? ⭐](#17 深拷贝和浅拷贝的区别? ⭐)
- [#18 BIO 和 NIO 在 Android 网络中的视角? ⭐](#18 BIO 和 NIO 在 Android 网络中的视角? ⭐)
- [#19 Java 8 lambda 在 Android 中如何工作? 💡](#19 Java 8 lambda 在 Android 中如何工作? 💡)
- [#20 `try-with-resources` 和 `AutoCloseable` 是什么? 💡](#20
try-with-resources和AutoCloseable是什么? 💡) - [#21 Java 是值传递还是引用传递? 💡](#21 Java 是值传递还是引用传递? 💡)
- [#22 Java 8 `Stream` 在 Android 里能用吗? 💡](#22 Java 8
Stream在 Android 里能用吗? 💡) - 面试策略速查
- 完整链路一句通
- 相关推荐
- [#16 `LruCache` 和 `HashMap` 怎么选? 🔥](#16
学习建议
| 目标层级 | 建议 |
|---|---|
| 初级 | 掌握基础层 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 equals 和 hashCode 的契约是什么? 🔥
标准回答
契约:equals 相等则 hashCode 必须相同;hashCode 相同对象可不等。HashMap 先算哈希定位桶,再用 equals 比对链表/树节点。
面试官可能继续追问
-
Android 里
equals/hashCode注意什么?自定义
Parcelable数据类或 Room 实体作Map键时,用 IDE 生成二者,避免缓存命中失败。 -
为什么
HashMap先比hashCode再比equals?先缩小桶范围,减少全字段比较,O(1) 均摊依赖此两步。
-
data class 自动生成够吗?
Kotlin
data class已生成;Java 手写实体务必同步维护两方法。
#3 ArrayList 和 LinkedList 怎么选? 🔥
标准回答
ArrayList 底层数组,随机访问 O(1),尾部增删均摊快;LinkedList 双向链表,头尾插删快但随机访问慢。
面试官可能继续追问
-
Android 里
ArrayList和LinkedList怎么选?列表数据几乎都用
ArrayList或RecyclerView适配器数据源;LinkedList极少用,除非明确队列语义且规模可控。 -
ArrayList扩容机制?容量不足时约 1.5 倍扩容并拷贝,频繁扩容有分配开销。
-
主线程遍历大
LinkedList会卡吗?会,
get(i)每次从头/尾遍历,大列表禁止随机索引访问。
#4 HashMap 的 put/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 String、StringBuilder、StringBuffer 怎么选? ⭐
标准回答
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 Comparable 和 Comparator 有什么区别? ⭐
标准回答
Comparable 是类内部自然排序(compareTo),如 String、Integer;Comparator 是外部比较器,可灵活定义多套规则。TreeMap/TreeSet、Collections.sort 都依赖二者。
面试官可能继续追问
-
Android 里排序接口怎么用?
列表按时间、优先级排序时,实体实现
Comparable或传入Comparator;DiffUtil比较项也常自定义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 :编译期必须处理或声明抛出,如 IOException;unchecked :继承 RuntimeException 或 Error,如 NullPointerException、IllegalArgumentException。
面试官可能继续追问
-
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 SparseArray 和 HashMap 怎么选? ⭐
标准回答
SparseArray 用两个数组存 int key 与 value,无装箱、内存更省,key 必须 int;HashMap 通用但 Integer 键有装箱开销。
面试官可能继续追问
-
Android 里
SparseArray和HashMap怎么选?SDK 大量用
SparseArray/LongSparseArray替代Map<Integer, ?>,如View的 tag、监听器缓存;通用对象键仍用HashMap。 -
查找复杂度?
SparseArray二分查找 O(log n);小数据常比HashMap更省。 -
ArrayMap又是什么?Android 对少量映射的数组实现,适合小集合。
核心层(7 题)
#16 LruCache 和 HashMap 怎么选? 🔥
标准回答
LruCache 基于 LinkedHashMap 访问顺序,超容量自动淘汰最久未用条目,并可重写 sizeOf 按字节计;HashMap 无淘汰。
面试官可能继续追问
-
Android 里
LruCache怎么用?图片内存缓存、解析结果缓存用
LruCache;临时请求映射用HashMap。Glide 内部有更复杂分层缓存,思路同源。 -
sizeOf返回什么?条目权重,Bitmap 缓存常返回像素字节数。
-
线程安全吗?
LruCache本身非线程安全,多线程需外部同步或每线程实例。
#17 深拷贝和浅拷贝的区别? ⭐
标准回答
浅拷贝共享引用字段;深拷贝递归复制引用对象。Object.clone() 默认浅拷贝。
面试官可能继续追问
-
Android 里深/浅拷贝注意什么?
传递可变
List或Bitmap配置时,浅拷贝可能导致一处修改处处可见;需要隔离时用拷贝构造、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-resources 和 AutoCloseable 是什么? 💡
标准回答
Java 7+ 语法糖:在 try 括号内声明 AutoCloseable 资源,块结束自动 close(),异常时先抑制关闭异常再抛主异常。
面试官可能继续追问
-
Android 里
try-with-resources怎么用?读
InputStream、数据库Cursor、文件流应优先使用;Kotlin 对应use {}。避免手动finally漏关导致文件描述符泄漏。 -
close抛异常怎么办?try-with-resources会 addSuppressed 保留两个异常信息。 -
哪些 Android API 实现了
AutoCloseable?ParcelFileDescriptor、Cursor等,用完即关。
#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 LinkedList;HashMap put/get、扩容树化;SparseArray/LruCache 选型;final/异常体系。
高频追问
HashMap 为何容量 2 的幂;值传递 vs 引用传递;非静态内部类为何易泄漏;try-with-resources 关闭顺序。
加分项(💡)
lambda desugar;深/浅拷贝;BIO/NIO 与 OkHttp;Java 8 Stream 在 Android 的边界。
容易踩坑
重写 equals 忘 hashCode;static 持 Activity;主线程网络/大列表;HashMap 多线程写;final List 误以为内容不可变。
完整链路一句通
Java 基础在 Android 里的主线 :用正确的相等语义与 hashCode 驱动 HashMap/LruCache/SparseArray 选型 → 掌握 final、异常、内部类与值传递等 OOP 规则 → 资源用 try-with-resources 关闭 → 网络与拼接遵守「阻塞不放主线程、少在热路径分配」。
相关推荐
Android 16(API Level 36)Activity 启动流程源码级解析