高频题
Java
- List和ArrayList区别、原理、扩容
List是接口,ArrayList是其实现类。ArrayList使用动态数组的数据结构。add时会检查扩容,get时会根据索引查找。java8扩容时会创建旧容量1.5倍的数组复制过去,java17会检查最小增长量和1.5中最大的,一步到位。
- HashMap、LinkedHashMap、ConcurrentHashMap区别、原理、扩容
HashMap数组 + 链表/红黑树。LinkedHashMap数组 + 链表/红黑树 + 双向链表。ConcurrentHashMap数组 + 链表/红黑树。HashMap容量超过容量 × 负载因子时扩容,容量翻倍(2的幂次),利用 (hash & oldCapacity) == 0 判断,将链表拆分成两个,减少重新计算。
- 线程池创建方式、原理
ThreadPoolExecutor:核心线程数、最大线程数、空闲线程存活时间、时间单位、工作队列、线程工厂、拒绝策略Executors工厂方法:Executors.newFixedThreadPool、Executors.newCachedThreadPool、Executors.newSingleThreadExecutor、Executors.newScheduledThreadPool
自定义线程工厂ScheduledThreadPoolExecutorJDK21虚拟线程
- 安卓中锁的种类、使用
volatilesynchronizedReentrantLockAtomic类Handler
- 重载里可以使用泛型吗?泛型擦除了解吗?
重载可以使用泛型,但是需要参数列表个数不一样。泛型擦除是为了兼容旧版本没有泛型而设计的,在运行时会将泛型擦除为Object或第一个泛型上界。泛型是编译期类型安全检查。需要读取数据时 → 使用 ? extends T(上界)需要写入数据时 → 使用 ? super T(下界)既读又写时 → 不要使用通配符,直接用 <T>
- final关键字有什么作用?
修饰类禁止继承。修饰方法禁止重写。修饰变量禁止重新赋值(引用类型是禁止改变引用)。保证线程安全 - 安全发布不可变对象。
- 深拷贝和浅拷贝
深拷贝和浅拷贝是对象复制的两种不同方式,主要区别在于对引用类型字段的处理。复制对象本身,深拷贝同时递归复制对象内部所有引用类型字段指向的对象
- 实现线程安全的方法有哪些?
不可变对象:同步机制:volatile、synchronized、ReentrantLock、并发集合类、不可变集合、原子变量类、ThreadLocal、读写锁、StampedLock。
- sleep和wait有什么区别?
sleep是Thread类的静态方法。wait是Object类的方法。sleep不会释放锁。wait需要释放锁,需要手动唤醒。
- 有三个耗时任务,A和B并发,C拿到A和B的结果再执行,用线程怎么实现?java中有什么API可以实现?用kotlin怎么做?
JVM
- GC流程
GC主要分三步:标记:从GC Roots(如线程栈、静态变量)出发,标记所有存活对象。清理:回收未被标记的垃圾对象内存。整理(可选):移动存活对象,消除内存碎片。关键算法:新生代用"复制",老年代用"标记-整理"或"标记-清除"。
新对象 → Eden区
↓
Eden满 → Minor GC
↓
存活对象 → Survivor区 (反复复制)
↓
存活达阈值 → 晋升老年代
↓
老年代满 → Full GC (全局回收)Android
架构模式:MVC、MVP、MVVM、MVI。
MVC:Model负责数据和业务逻辑。View负责UI的展示,通常是Activit/Fragment。Controller负责处理用户输入和更新Model/View。通常是Activity/Fragment。缺点:1.Activity/Fragment 过于臃肿,承担了太多职责,被称为“上帝对象”。2.View和Controller紧密耦合,难以测试。MVP:Model负责数据和业务逻辑。View由一个接口(如IView)定义,由Activity/Fragment实现。它只负责展示UI和接收用户输入。Presenter是业务逻辑的处理中心。持有View的接口引用和Model的引用,它从Model获取数据,然后调用View接口的方法来更新UI。缺点:1.需要为每个View编写大量的接口,有一定模板代码。2.Presenter持有View的引用,容易发生内存泄漏,需要在onDestroy中解绑。MVVM:Model负责数据和业务逻辑。View几乎只负责监听ViewModel的数据变化并更新UI,以及处理生命周期。ViewModel与Presenter类似,是业务逻辑的处理中心。但关键区别是,它不持有View的引用。它通过可观察的数据(如LiveData, StateFlow)来暴露UI状态。核心思想是数据驱动,利用数据绑定技术减少胶水代码。缺点:1.数据绑定的调试可能更复杂。2.如果滥用,可能会在XML中写入过多逻辑,违背了关注点分离的初衷。MVI:Model代表UI的状态。View渲染State,并发送Intent。Intent代表用户的意图描述一个动作。MVI是一种响应式、函数式的架构模式,强调单向数据流和唯一数据源。缺点:1.学习成本。2.对于简单页面,显得有些重。
(工程/项目)架构:模块化、组件化。
模块化:关注代码组织,解决的是依赖关系。组件化:关注功能拆分,解决的是独立运行与组合。
App启动流程
启动入口:由启动器通过startActivity向AMS发送Intent。AMS任务栈检查,若不存在则创建进程。创建进程:由Zygote执行fork子进程并加载应用入口ActivityThread。子进程调用ActivityThread.main()创建主线程(UI 线程)Looper并向AMS发送attachApplication注册 ApplicationThread Binder 接口,供 AMS 回调。AMS 收到 attachApplication 后,完成与进程的双向通信绑定。触发 Instrumentation.bindApplication,后续通过 scheduleTransaction 驱动组件生命周期。应用初始化:ContentProvider初始化:AMS 在主线程调用 ActivityThread.installProvider,每个 Provider 执行 onCreate。Application 创建:调用 LoadedApk.makeApplication → 执行 Application.attach、onCreate。Activity 启动事务:AMS 发出 LaunchActivityItem 事务。ActivityThread 解析事务,利用 Instrumentation.newActivity 反射实例化 Activity。执行 Activity.attach、onCreate、onStart、onResume,间穿插 Fragment 生命周期。
Activity生命周期、保存机制、启动模式、任务栈
自定义View
View的绘制流程
主线程handler.post和View.post有什么区别
onMeasure会传递两个参数,会用到这两个参数做什么事情?
onLayout摆放的逻辑是什么,参数怎么用的?
事件分发机制、滑动冲突
事件分发机制:dispatchTouchEvent:首先,如果存在 OnTouchListener 且 onTouch 返回 true,则消费掉。否则,会调用 onTouchEvent 方法。对于 ViewGroup调用 onInterceptTouchEvent。onInterceptTouchEvent:事件拦截。onTouchEvent:事件处理。若消费则独占。解决滑动冲突:在 父容器 的 onInterceptTouchEvent 方法中做拦截判断。在 子View 的 dispatchTouchEvent 方法中requestDisallowInterceptTouchEvent。可能不生效原因:1.在ACTION_DOWN申请。2.父布局在onInterceptTouchEvent的ACTION_DOWN直接拦截。
Handler机制、休眠唤醒机制、idleHandler机制
线程间通信机制
1.Handler + Looper + MessageQueue2.LiveData3.协程 + Flow
进程间通信机制
1.Binder2.AIDL(基于Binder)3.Messenger(基于AIDL)4.ContentProvider(基于Binder)5.BroadcastReceiver6.文件 & Socket
内存泄露
1.静态引用:生命周期不匹配2.非静态内部类/匿名类:隐式持有其外部类的引用3.资源性对象未关闭:它们使用了 native 内存4.监听器/广播未反注册:全局发布者就会一直持有引用5.WebView:会使用native内存。可以使用独立进程。
崩溃捕获
Thread.setDefaultUncaughtExceptionHandler
ANR检测
1.监控/data/anr/traces.txt(需要root)2.WatchDog:在主线程每秒发送消息,观察消息是否超时(5s)ANR,搜集堆栈信息/上报。
常用设计模式:工厂模式、建造者模式、单例模式、都解决什么问题的?
工厂模式:不知道要创建哪个具体类的实例,或者不希望直接依赖于具体类简单工厂:一个具体的类,包含一个创建产品的方法。通常通过 if-else 或 switch 来判断创建哪种产品。工厂方法:定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类。抽象工厂:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
建造者模式:将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。它主要用于解决构造方法参数过多、参数可选性多以及构造过程复杂的问题。单例模式:管理全局的、共享的资源或状态。
装饰者模式和建造者模式的作用和使用场景,什么时候用build模式,什么时候用set方法
手写观察者模式
OkHttp原理、Retrofit原理、Glide原理、Bugly原理、Lifecycle原理、ViewModel原理、RecyclerView原理
性能优化:内存优化、启动优化、卡顿优化、编译优化、包优化
RecycleView是怎么实现高效缓存的,DiffUtil和AsyncListDiffer
Crash检测
ANR检测
SparseArray 和 ArrayMap了解吗
Android 中如何实现屏幕适配?
一个图片的内存是怎么计算的?
用ViewGroup去实现纵向的列表?
约束布局有什么缺点?
mmkv比sp有什么优势?
如何判断当前线程是不是主线程?
如何优化 RecyclerView 的性能?
如何处理 WebView 的内存泄漏?
OOM有那几种异常类型?
Java 中 Object 的 clone()方法如何实现深拷贝?描述其步骤并实现。
怎么做的应用质量保障?开发阶段?测试阶段?上线之后?
流畅度的评价指标是什么?
如何检测应用性能好不好?
kotlin切换线程和Java切换线程有什么区别?
俩个View都有背景,是过度绘制吗
DataBinding是怎么实现双向绑定的
java里普通内部类和静态内部类的区别
一个类里的静态私有属性可以通过反射获取吗,和获取普通属性的区别是什么
Kotlin
- 作用域函数let、run、also、apply、with
- 类委托、属性委托
- 扩展函数、扩展函数的局限性
- 集合、可变集合
- 主要构造函数、次要构造函数
- 伴生对象
- 为什么协程开销更小
- 协程中如何捕获异常?父子协程如何捕获异常?
- 一个父协程里有两个子协程,父协程取消了,子协程会取消吗?
- 一个协程里做了循环的io操作,协程取消了,io操作会取消吗?
- 为什么使用协程,开发中有什么便利点?
- 协程中,哪些场景是CPU计算密集型,哪些是IO密集型
- 挂起函数的挂起是指什么,是怎么恢复的
- flow原理,协程能做的东西,flow也能做,为什么要有flow?
- kotlin中,inline和noinline
Compose
- remember 、mutableStateOf、rememberSaveable
- 重组是什么、原理
- LaunchedEffect、SideEffect