面经总结
操作系统/计网
进程和线程? 线程的轻量级的体现在哪里?
: 一个进程可以有多个线程, 线程是CPU调度的最小单位, 进程是资源分配的最小单位. 轻量级的话, 进程是静态的只用来分配资源的, 线程是动态的用来执行指令的.
线程crash进程会不会crash
: 在Java语言的话, 线程crash并不会导致进程crash, 不过进程内不同线程之间的内存是共享的, 一个线程的crash可能导致其他线程用到的内存访问到不正确的数据, 然后导致其他线程出现crash
死锁了解吗? 说一下死锁产生的四个必要条件?有什么算法能避免死锁吗
(银行家算法): 四个必要条件: 互斥(一个资源一次只能分配给一个线程使用), 不可剥夺(一个线程不能强行占有其他线程已经占有的), 请求和保持(一个线程在等待其他进程释放资源的同时,继续占有已经拥有的资源), 循环等待条件(A等B等C等A)
操作系统中cpu调度算法有哪些? 你觉得其中最好的一种是?
: 首先是分为抢占和非抢占, 具体有:先进先执行FCFS/最短优先SJF/优先权法/轮转法(隔一段时间被迫交出CPU)…
操作系统IO模型
: 同步阻塞/同步非阻塞(轮询:隔一会儿瞄一眼进度条/IO多路复用:poll&epoll机制:Handler?)/异步非阻塞(回调式)/异步阻塞(Kotlin协程suspend底层原理利用了Continuation实现了CPS转换,回调地狱应该算异步阻塞)
- 同步阻塞: 从开始阻塞到结束.
- 同步非阻塞: 开始到资源就绪轮询,资源拷贝阻塞.
- IO多路复用: 开始到资源阻塞, 资源拷贝阻塞, 两段分别阻塞.
进程通信方式
: 管道(只能在有亲缘关系进程间单向流动), 有名管道(允许无亲缘关系进程间单向流动), 消息队列, 共享内存, 信号量(控制多个进程对共享资源的访问,常用于文件锁), 套接字(可用于网络), 信号(通知进程某个事件已经发生)
解释性语言
: Java编译成class, 在不同平台使用不同虚拟机解释执行. 编译型语言: c语言编译成二进制文件, 不同平台可执行的二进制文件不同.
架构? MVVM/MVP 区别
:(MVC: View触发事件传递给Controller通知Model数据变化刷新View. MVP: MV彻底解耦, Presenter持有两者的接口进行操作. MVVM: MV彻底解耦, VM实现数据双向绑定, 比如Google的Databinding, 这时我们其实还需要写BindAdapter)
https与http的区别, 数字签名?
: 多了一层 TLS/SSL, 数字签名将服务器发来的密文用签发机构的公钥解密, 然后和服务器的信息的hash值进行对比
DNS解析怎么做的? DNS劫持如何避免
: DNS解析就是根据指定的域名, 记录类型(A,AAAA,CNAME,MX,NS), 记录值找到指定的服务器或跳转到其他域名. 避免DNS劫持可以在OkHttpClient中自定义自己的dns
http状态码
: 100/102continue, 206分段内容, 301永久/301临时重定向/304 NotModified, 401未授权/403Forbidden服务器拒绝请求/404NotFound, 500服务器内部错误/502网关错误/503服务暂时不可用
http2多路复用
: 基于二进制分帧层, HTTP消息被分解为独立的帧并行发送出去, 最后在另一端根据流ID重新组合
http几个版本的不同
: 1.1: 多条连接承载多个请求, 2.0: 单条连接多路复用, 3.0: 基于QUIC实现快速可靠的UDP连接
get与post请求的区别? get与post携带数据的区别
: get往往使用FormUrlEncoded这种方式将请求参数编码在url中, post往往会在body中包含数据. 且get请求一般带有缓存, post没有. get发送一次, post会先发送一次header, 服务端响应100 continue, 然后再发送真实数据, 响应 200
浏览器输入url发生了什么
: 构建请求/DNS解析/发起TCP连接/发送HTTP请求/服务端响应HTTP请求/客户端处理请求
TCP原理
, 如何实现可靠传输, 除了三次握手和四次挥手还有哪些机制?
: ACK(服务端返回收到的最后一次正确的seq+1), 流量控制, 拥塞控制
为什么要三次握手/四次挥手
:
- 三次握手: SYN->SYN+ACK->ACK, 如果变成两次握手, 如果第一次SYN包发送出去但是暂未成功建立连接, 而在客户端重试之后原来失效的连接又被成功发到了服务器使得服务器又使用原来的SYN包建立起了连接, 不符合预期.
- 四次挥手: FIN->ACK,FIN->ACK, 其中一方通知关闭之后另一方回应收到, 然后等待成功关闭后回应FIN包, 然后另一端回应ACK, 连接结束.
tcp长连接和http长连接区别
: HTTP header(Connection: keep-alive), 指的是一个连接会保持一段时间, 而不是传输完数据就直接断开. TCP长连接指的是TCP保活计时器, HTTP的长连接本质上是TCP的长连接, TCP的长连接通过心跳包建立.
UDP如何实现可靠传输
: QUIC: 基于UDP,减少了握手时间/避免队头阻塞的多路复用
怎么优化首页加载速度(从数据传输方面)
:(使用http3.0, 加入缓存机制, protobuf, 预加载机制, 服务端优化查询策略, dns解析存储ip, 跳过查找过程服务端进行压缩, 数据分页传递…)
说一下你熟悉的设计模式?
: Factory/Builder/Adapter/Observer/Proxy/Singleton…
设计模式六大原则
:开闭(扩展开放,修改关闭), 里氏代换(基类可以出现的地方,子类一定可以替换), 依赖反转(面向切面编程而不是具体的实现), 接口隔离(使用多个隔离接口解耦), 最少知道(尽可能少的与其他实体相互作用,系统功能模块相对独立), 合成复用(使用组合而不是继承)
Java/Kotlin
多态能解决什么问题, 多态的底层原理
: invokespecial(init及私有方法)/invokevirtual(虚方法,会检查父类)
泛型是什么
(在同一份代码上操作多种类型), 泛型上界下界?
(上:extends,out/下:super,in) 如何反射获取泛型类型
: Field.genericType
Java内部类和静态内部类的区别
: 是否可以访问外部类的属性
接口和抽象类的区别?抽象类和接口的使用侧重点知道吗?
: 抽象类是对整个类的抽象, 接口是对局部行为的抽象.
容器类了解哪些?说说copyonwrite容器的应用场景
: set,map,list,deque. copyonwrite:并发读多写少的场景
ArrayList扩容机制
: 默认10, 每次扩1.5倍(oldCap+oldCap»1)
HashMap底层数据结构
:数组+链表(+红黑树 1.8), 不线程安全, 扩容:乘2, 负载因子:默认0.75…其余八股略
Hashmap的Concurrentmodification异常怎么产生的
: HashMap迭代时添加或删除元素
HashMap put时的流程是怎么样的
: 高低16位异或得到hash, 空间不够的话进行resize, 之后通过hash&(容量-1)异或得到储存位置, 如果当前位置没元素或者keyObject相同, 那么直接新建或者修改他们的value, 如果hash冲突那么就使用链表查找或者添加, 如果hash冲突严重就会使用红黑树进行查找或者添加.
红黑树如何自平衡? 为什么树化的阈值为8?
(泊松分布命中概率太低了)
为什么hashmap的长度必须是2的指数?
因为长度是2的指数的时候hash&(n-1)=hash%n, hashmap中的tabSizeFor方法也会找到刚刚比需要容量大的2的n次方的容量
LinkedHashMap底层
: 在hashmap的node的基础上给它增加了当前元素的前节点和后节点.
ConcurrentHashMap底层
三个关键字什么区别
: final
(对象关键字,代表不可变. 类关键字)、finally
(异常处理,不管有无异常均执行)与finalize
(对象被回收时回调,但并不能确保,建议使用PhantomReference)
死锁
(写一个) 悲观锁
乐观锁
volatile与synchronized都是什么? 他们的区别
: volatile保证了内存的可见性, 对变量的读写会直接刷新到主存. synchronized既可以保证可见也可以保证原子
synchronized代码块原理:
我们在字节码里面可以看到, 是在语句之前插入了一个monitorenter, 如果线程进入数为0就将进入数设为1, 如果当前线程已经持有了monitor, 那么就继续+1(可重入), 语句之后插入了一个monitorexit, 每次会将进入数-1, 进入数为0可以理解为释放掉了锁.
wait 和 sleep 的区别
: wait是让出对象锁, sleep就是阻塞线程一段时间后继续执行
java开启一个线程的方式
: 线程池, new Thread
Thread.start()与直接调用run()的区别
: start是启动新线程执行内部的runnable, run方法是Thread继承Runnable, 实现了run方法, 直接执行runnable#run
threadlocal原理
: 每个线程都拥有一个属于它们的ThreadLocalMap, 相当于一个精简的Hashmap, ThreadLocal的get就是从这些map中获取指定的键值对
Java默认提供了几种线程池
, 有哪些参数ThreadPoolExecutor(核心线程数corePoolSize, 最大并发数maximumPoolSize, 空闲回收超时keepAliveTime, 阻塞队列 workQueue, threadFactory, 超出maxPoolSize+queue大小时, 任务交给RejectedExecutionHandler处理)
有哪些常用线程池?
:newFixed(指定线程池大小,全部是核心线程,空闲立即回收,阻塞队列无限), newCached(无核心线程,默认60s回收,无空间阻塞队列), newSingle/Scheduled…
线程池4种拒绝策略:
丢弃,抛出异常/丢弃,不抛出异常/丢弃最早的任务,重新提交新的/由调用线程处理这个任务
synchronized与reentrantlock内部实现方式的区别
(性能? synchronized具体做了哪些优化): synchronized是在JVM层面, 不能主动释放锁, 非公平锁. reentrantlock是Java代码层面, 可以主动释放锁, 也可以是公平锁.
Reentrantlock 底层实现通过AQS实现, AQS内部通过CAS调用Unsafe类的park/unpark方法实现线程的阻塞和继续运行
synchronized是什么锁?
偏向锁向轻量CAS到重量锁的过程
java注解用法
kotlin扩展函数原理
JVM运行时内存区
: 数据(方法区:类信息,字段信息,常量池/堆内存), 指令(程序计数器/虚拟机栈:基本数据类型,对象引用/Native栈)
如何判断什么样的对象需要回收?GC ROOTS有哪些
: 主要有虚拟机栈以及Native方法区中引用的对象
Python如何实现的断开循环引用
分代收集使用不同算法的原因
方法执行之前发生了什么
: 首先加载class文件, 验证准备解析, 然后执行父类构造, 子类构造, 成员变量初始化, 然后执行方法
System.out.println(“a”)将字符输出到屏幕发生了什么
: 一个Synchronized的方法将字符串输出到System.out.textOut中然后flushBuffer
成员变量与局部变量的区别? 局部变量的值一定是存放在堆中吗
AQS原理
jvm类加载的时机
: 调用静态方法/new对象/反射调用/.class
根类加载器加载什么类
:jre包下的类rt.jar(BootstrapClassLoader)
jvm类加载过程
: 加载/验证准备解析/初始化使用卸载
双亲委托机制是什么
双亲委托机制什么时候会被破坏
: 通过Thread#setContextClassLoader给线程设置一个自定义ClassLoader/ClassLoader.getSystemClassLoader().getParent()得到AppClassLoader的父类, 然后加载我们需要的Class
Class.forName和ClassLoader.loadclass的区别
: Class.forName会进行初始化, loadclass在完成解析之后便会结束
遇到过的异常有哪些?
空指针,数组越界,文件未找到,IOException,NosuchMethod,OOMError,StackOverflowError
CAS
, 自旋锁
四种引用
Android
handler原理
: handler的post实际上最终是向MessageQueue中通过enqueueMessage插入了Message, 并会通过when参数保证消息的顺序, 同时在插入完成后会通过nativeWake唤醒等待的消息队列. Looper中的loop是阻塞的, 会阻塞在message的next方法中, 这个阻塞是通过nativePollOnce实现的, 并会传递超时时间, 若超时或者被nativeWake唤醒则继续消息队列的循环.
handler的内存泄露了解吗?如何避免?
: handler.removeCallbacksAndMessages/静态内部类持有外部activity的弱引用, 不过当消息队列为空自然会释放, 但是, 比如说activity的window已经detach了还去操作布局就会出现问题, 建议使用生命周期感知刷新界面(livedata?)
postDelay源码
两个Message相隔5秒, 这5秒内线程会一直阻塞吗
view绘制流程, 怎样优化
include, viewStub, merge 的使用和区别
怎么获取view的宽高, 如何确定值是准确的
: onWindowFocusChanged/view.post()/viewTreeObserver.OnGlobalLayoutListener
view.post和handler.post的区别?
: View.post如果还没有attach到window上面会先把消息插入到Queue,在ViewRootImpl#performTraversal后执行
requestLayout 和 invalidate 的区别
: 重新调用onMeasure,onLayout/重新onDraw
知道消息是怎么插入到消息队列的吗?
广播注册有哪些方式?
: <receiver <intent-filter <action / registerReceiver(receiver, intentFilter)
启动流程
、绘制流程
、事件分发
遇到过滑动冲突吗?滑动冲突的解决方法
: 父View是否intercept,是则父View#onTouch,否则子View#dispatch
自定义View如何特殊处理wrap_content, padding的实现
: 给子View也传AT_MOST过去让子View都测量一遍,然后计算出我这个View实际尺寸.
属性动画和补间动画的区别
: 属性动画反射setter方法调用,真实改变view的属性,可以作用于任意对象,不只是View,且而补间动画可能只会改变view的translationXY,只能作用与View.
点击桌面上的 App 启动发生了什么
: Launcher通过Intent启动Activity,这里会调用AT(task)MS,之后通知AMS创建应用进程,之后ActivityThread.main(),之后创建出第一个Activity,执行各种生命周期,onCreate#setContentView的方法会让activity创建的PhoneWindow创建ViewRootImpl,里面会有一个decroView,decroView中的content就是activity的布局
Android四大组件
Service优先级
: 前台进程, 可见进程, 服务进程, 后台进程, 空进程
Service使用场景, 如何绑定服务
: startService/startForegroundService/bindService
Service的两种启动方式和生命周期
: create->(un)bind/onStartCommand->destory,onStartCommand在每次startService都会调用, onBind只在第一次bindService调用
广播分类
: 普通广播(sendBroadcast)、有序广播(sendOrderedBroadcast)、异步(粘性)广播(已经弃用,发出广播会一直等待Receiver). 重写BroadcastReceiver#onReceive
Activity的启动模式及其应用场景
: standard(打开后可以返回原app)/singleTop(创建Activity加入栈顶,如已有则拿到栈顶onNewIntent)/singleTask/singleInstance(调用支付宝支付)
onSaveInstance 调用的场景
: 语言字号, 屏幕尺寸方向发生改变
Activity taskAffinity了解吗
, 横竖屏转换发生了什么
, onNewIntent有什么用
Activity生命周期, 每个生命周期事件代表了什么样的含义
: create正在被创建/restart重新启动/onstart即将启动/onResume用户可交互/onPause/onDestory销毁
启动一个活动A, 接着在A中启动活动B, 各自的生命周期变化?如果B活动是透明的呢?如果此时再启动第三个活动C, 三个活动的生命周期变化?
fragment的生命周期了解吗?
: onAttach:与Activity建立关联,onCreateView:创建视图时使用,onViewCreated,onDestroyView,onDetach
RecyclerView和ListView区别, RecyclerView缓存机制
: RecyclerView四级缓存:mAttachedScrap(预布局时储存不变的VH)+mChangedScrap(变了的VH,用来执行动画)/mCacheViews(默认缓存两个)/mViewCacheExtension/RecycledViewPool(SparseArray,不同itemType使用不同缓存List<ViewHolder>). ListView离屏缓存View,从mScrapViews根据pos获取相应缓存,getView后进行bindView.
IPC有哪些? ContentProvider细讲?
Binder,AIDL有了解过吗
: 服务端onBind实现AIDL接口Stub对象,客户端.Stub.asInterface(iBinder)
如何自己做ANR监测, 设计一下
为什么会发生OOM?
: 图片视频,循环中创建大量对象(包括onDraw),内存泄漏(Handler?)
Android热修复有哪几种方案?
SharedPreference commit和apply方法的区别以及它们的缓存机制?
SharedPreference要多进程怎么办
Parcelable轻量的原理是什么
适配浏览器跳转到 APP
, intent-filiter
SparseArray
三方库
Dagger原理
ViewModel怎么实现保存恢复数据, 如何创建一个viewModel
: 在横竖屏切换时会调用onRetainNonConfigurationInstance(onSaveInstanceState之后), ComponentActivity重写了这个东西, 在其中ViewModelStore用来保存了状态, 恢复数据的话, 就从getLastNonConfigurationInstance里面看看有没有上次保存的配置. 至于这个保存在ActivityClientRecord里面, 储存在Activity#mActivities:ArrayMap<IBinder,ActivityClientRecord> 中.
LiveData的onChanged回调时机
: setValue(postValue实际也调)时通知每个观察者onChanged, 每次set版本号++, 因此即使set值相同也回调.
LiveData与RxJava的区别
LiveData生命周期感知如何实现, LiveData原理
:setValue时会分发(dispatchValue), 考虑通知(considerNotify)这里就会根据ObserverWrapper(LifecycleBoundObserver) 根据生命周期决定是否分发.
介绍RxJava
内存泄漏是什么
: 有内存无法回收
内存泄漏的场景
: 静态对象(比如静态集合), 单例模式, 数据库/网络/文件IO等连接不释放, 内部类持有外部类引用
内存泄漏检测的工具
LeakCanary原理, 用了个什么队列
: 通过ActivityLifecycleCallbacks绑定生命周期, 在Activity销毁后触发就去检查, 检查就是构建了一个WeakReference并向其提供了一个ReferenceQueue, 之后触发gc, 然后看看引用队列里面有没有元素.
OkHttp底层, 对网络请求做了啥优化
:RetryAndFollowUpInterceptor:请求,出错重试,重定向, BridgeInterceptor加各种header(cookie), CacheInterceptor:通过Cache/有无网络综合判断是否使用缓存, ConnectInterceptor:打开连接,也会通过连接池复用healthConnection,创建okio IO流, CallServerInterceptor:发起真正的请求,从IO流读取内容.
OkHttp异步流程中准备队列的进入条件
: 是一个ThreadPoolExecutor,maxRequests:64,maxRequestsPerHost:5
Retrofit原理
Retrofit返回的是什么数据类型
: Call<ResponseBody>, 不过可以自己写converter就可以实现
Navigation的优缺点
如何实现的自定义navigate方法切换fragment, 如何让被覆盖的fragment感知到生命周期
图片框架就是使用Glide吗, 有自己思考吗
: with创建RequestManager, 创建fragment绑定生命周期(Retriever), 如果传入application就全局生命周期. load使用RequestManager构建RequestBuilder, 指定asBitmap/Drawable/Gif什么的, into到一个target, ImageView会变成一个ViewTarget, 然后engine.load依次从三个缓存里面查找, 找不到就网络请求.
Glide缓存有什么优化?
: 内存缓存(ResourceWeakReference,LRUCache), 磁盘缓存(DATA/NONE/RESOURCE/ALL/AUTOMATIC). with传入Activity/Fragment什么的,用来创建空Fragment绑定生命周期
开放性问题
- 项目中有没有遇到问题?怎么解决的
- 如果排期只有一周的任务, 但是以我的能力需要两周才能完成, 这个时候你会怎么办.
- 自己是如何学习Android的, 非科班的学习过程
- 实习过程中做的需求说一下
- 实习过程中的难点说一个
- 实习过程中和哪些人接触的比较多
- 如何和别人沟通的
- 实习的收获
- 挑一个项目说一下
- 学习方法, 路线
- 手写生产者消费者模式, 并说一下有哪些解决策略
- 最有成就感的一件事
- 如何总结提升/排解压力
算法
- LC25-k个一组反转链表
- LC445-两个链表相加, 最高位在前, 要求时间O(n), 空间O(1)
- LCJZ77-链表排序
- LC114,JZ36-树变链表
- JZ32-3-二叉树z字形打印
- JZ33-给一个数组, 判断是否符合二叉搜索树的后序遍历
- JZ7-前中数组构造二叉树
- 根据前序遍历和中序遍历输出后序遍历
- LC199-二叉树右视图
- LC102-树的层序遍历
- 判断A是否是B的子树
- 排序算法有哪些? 几个排序算法的场景: 稳定/不稳定:冒泡,插排(数据量不大使用,因为空间O(1)),归并(链表,稳定)/选择,快排,堆排,计数(数据大小范围不大)
- 汉字表示的数字转成阿拉伯表示
- LRU
- LC47-字符串的全排列(要求去重)
- LC20-判定括号合法
- 数组 链表 二叉树 哈希表的读写时间复杂度
- 手写单例双重检测, 在什么情况下使用单例
- 36进制加法
- LC611-有效三角形的个数
- NC126-换钱的最少货币数
- LC22/NC26-括号生成, 可不可以用二叉树解?
- LC564-最近回文数
- ipv4地址转32位int输出
- LC718 最长公共字串
- 二叉树查找, 将这条路上的结点存储