Skip to content

高频题

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
    • 自定义线程工厂
    • ScheduledThreadPoolExecutor
    • JDK21虚拟线程
  • 安卓中锁的种类、使用
    • volatile
    • synchronized
    • ReentrantLock
    • Atomic类
    • 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 + MessageQueue
    • 2.LiveData
    • 3.协程 + Flow
  • 进程间通信机制

    • 1.Binder
    • 2.AIDL(基于Binder)
    • 3.Messenger(基于AIDL)
    • 4.ContentProvider(基于Binder)
    • 5.BroadcastReceiver
    • 6.文件 & 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