Java基础
-
String是什么数据类型?(Java中数据类型)
Java中数据类型分为两种:基本数据类型和引用数据类型。
根据下图可以看出String为引用类型。
-
String、StringBuilder、StringBuffer的区别
(1)String是字符串常量,一旦创建后就不可改变,String的拼接会创建新的字符串常量对象并回收之前的对象,频繁的String拼接对内存消耗较大,并且耗时。
(2)StringBuilder就是为了解决频繁String拼接造成的问题,StringBuilder是可变长度的,频繁的拼接直接对对象进行修改,无需重新创建和销毁对象。
(3)StringBuffer和StringBuilder类似,他俩的区别就是StringBuffer是线程安全的,无需考虑多个线程间数据同步问题,但效率会比StringBuilder慢一些。
(4)总结下来就是String适合少量字符串操作,StringBuilder适合单线程内大量字符串操作,StringBuffer适合多线程中字符操作。 -
堆栈内存的区别(全局变量存放在哪里?)
-
synchronized和volatile的区别
(1)volatile仅能作用于变量,synchronized可以作用于变量、方法和类上
(2)volatile仅能实现对变量的修改可见性,不能保证原子性,synchronized可以保证 原子性和可见性
(3)volatile不会造成线程阻塞,synchronized可能会造成线程阻塞
(4)volatile修饰的变量不会被编译器优化(在不同线程中不会出现副本),synchronized修饰的可以被编译器优化(但能保证原子性)。 -
Java中锁的分类(synchronized用的是公平锁还是非公平锁)
synchronized使用的是非公平锁,允许插队,提高性能。
Java中锁的分类:参见该文章 -
抽象类和接口的区别
(1)抽象类中可以定义普通成员变量,接口中只能定义static final变量,并且需要进行初始化
(2)抽象类可以有构造方法,接口没有
(3)抽象类中可以定义普通方法,接口中所有的方法都是抽象的
(4)抽象类中的抽象方法可以定义为public或protected,接口中只能是public
(5)一个类只能继承一个抽象类,但是可以实现多个接口
(6)抽象类中可以包含静态方法,接口中不能有静态方法 -
什么时候一个对象会被GC(引用计数法、可达性分析)
一个对象被GC,也就是对象无法被任何地方访问,那么如何进行判断呢,有两种常用方法。- 引用计数法:引用计数法就是给对象添加一个引用计数器,当对象被强引用的时候,计数+1,否则-1,如果对象的引用计数器为0,那么对象就没有被使用。该方法的优势就是高效,简单,但无法解决对象之间互相引用造成的问题。
- 可达性分析:可达性分析就是通过一系列的被称为“GC Roots”的对象,从这些节点向下进行搜索,搜索走过的路径被称为引用链,当一个对象到GC Roots没有任何引用链时,证明这个对象是不可用的。JVM采用的就是这种方法。
-
Java中集合相关问题
-
Java中集合都有哪些类型

-
ArrayList和LinkedList的实现和区别
ArrayList是由数组实现的,数组结构插入和删除速度慢,访问速度快。
LinkedList是由链表实现的,链表结构插入和删除速度快,访问速度慢。 -
ArrayList初始长度、负载因子、增加倍数(同理其他的也有可能问到)
ArrayList的初始长度为10,负载因子是1,扩容增量为0.5 -
HashMap的底层实现
HashMap的实现原理是数组加链表或者红黑树来实现,当我们调用put方法时,先去获取key的hash值,然后根据hash值经过算法来确定bucket数组中的下标,如果该下标处没有值,则将node放置在这里,如果对应位置上有链表,则调用key的equals方法和链表上的每一个key进行比较,如果有重复的,那么对应的key位置将会被替换掉,如果没有,则添加到链表末尾。
同理,调用put方法的时候,先调用key的hash值找到bucket对应下标,然后从链表中调用equals方法来获取我们需要找的value。HashMap中数组中某个下标处的链表大小超过8个时,将改为红黑树结构,小于8个时,重新改为链表结构。
-
HashMap中bocket的下标到底是怎么算的
对传入的key取hashcode,然后对hashCode以及hashCode右移16位做异或运算static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }然后在put操作的时候,再与0xFF做&操作
if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); else {最终确定下标值
-
HashMap的初始长度、负载因子、增加倍数
Hashmap的初始容量为16,默认负载因子为0.75,超过后进行扩容,每次扩大一倍。 -
HashMap是如何判断需要扩容的
new一个两倍大小的数组,遍历旧数组,将item变成游离态,重新计算数组下标,放入新的数组中 -
Hash碰撞的原因
-
链表和红黑树相关问题
当同一个下标中的链表数量过多时,就会导致失衡,查找需要遍历链表,速度比较慢,所以引入了红黑树
红黑树搜索非常快,是一种近似平衡的二叉查找树,能够确保任何一个节点的左右子树差不会超过而这种较低那个的一倍红黑树的特性:
每个节点要么是黑色,要么是红色
根节点是黑色
如果节点是红色,那么它的子节点必须是黑色,反之不一定需要成立
从根节点到叶节点或空子节点的每条路径,都包含相同数目的黑色节点
-
-
设计模式(单例、工厂、观察者、代理、适配器、建造者等常用模式)
- 单例的几种实现方式,为什么要使用单例
- 单例和全局变量的区别
- 动态代理和静态代理的区别
- 什么时候用观察者模式
- Android中你都知道用到了哪些设计模式,在哪里
-
线程相关
- 线程的几种创建方式
- 线程池
- 为什么要用到线程池(线程创建消耗大,减少重复创建线程带来的消耗)
- 线程池的工作原理
- 多线程间线程优先级调度
-
你对Java多态的理解
-
Java异常体系和处理机制
- Java异常的分类(exception(运行时异常和非运行时异常)、error)
- 异常处理机制(抛出异常、处理异常)
- try-catch-finally代码块都是什么时候被执行的,什么情况下finally代码块不会被执行
-
Java中四种引用的区别和使用场景
Android部分
-
基础部分
-
activity、fragment、service的生命周期
-
两个activity跳转时的生命周期
-
activity横竖屏切换时生命周期
-
activity中包含fragment时生命周期调用顺序
activity onCreate -> fragment onAttch -> fragment onCreate -> fragment onCreateView -> fragment onActivityCreated -> activity onStart -> fragment onStart -> activity onResume -> fragment onResume -> fragment onPause -> activity onPause -> fragment onStop -> activity onStop -> fragment onDestoryView -> fragment onDestory -> fragment onDetach -> activity onDestoryFragment添加到activity后,创建的时候是先执行activity的生命周期,然后执行fragment的相同生命周期,销毁的时候则是相反,先执行fragment的生命周期,然后执行activity的。
-
按home键后fragment状态错乱的原因,怎么解决
-
surfaceView、textureView和view的区别
-
ListView和RecyclerView的优化
-
-
Biemap所占的内存
-
OOM和ANR的分析定位及如何避免
-
Handler
- Handler的基本原理
- Handler是如何做到不阻塞主线程的
- Handler是如何发送延迟消息的、如何在队列中调度消息(保证消息的先后顺序)
- 消息队列为空时,Handler是如何阻塞线程的
- Handler阻塞时候,新消息到来是如何唤醒的
- Handler是如何向其他线程发送消息的(handler的线程切换机制)
-
Android中线程通信机制(handler、全局变量等)
-
进程间通信机制
-
AIDL

-
Messenger
-
Bundle
-
ContentPovider
-
Broadcast Receiver
-
文件共享
-
Socket
-
-
你对Binder机制的理解
IPC原理:
IPC指的是进程间通信或者是跨进程通信,指两个进程间进行数据交换的过程。
每个Android进程,只能运行在自己进程所拥有的虚拟地址空间。虚拟地址空间对应两个部分,用户空间和内核空间,不同进程的用户空间是非共享的,但是内核空间是共享的,进程间的通信就是利用了不同进程之间可共享的内核空间来完成的。Binder原理:
Binder机制从组件角度来讲,包含Client、Server、ServiceManager以及Binder驱动。
Client:使用服务的进程。
Server:提供服务的进程。
ServiceManager:服务管理进程,作用是使Client进程可以通过Binder名称获得对应的Server中的Binder引用。
Binder驱动:负责进程间Binder通信的建立、传递、计数和管理等工作。 -
事件分发机制
-
基本原理
-
一个事件的完成分发流程
-
如何拦截和消费一个事件
-
什么是事件队列
-
滑动冲突怎么解决
View滑动冲突分为两种:外层和内层滑动方向不一致,外层和内层滑动方向一致。解决方案分为两种,外部处理和内部处理。外部拦截法:指点击事件都先经过父容器的拦截处理,父容器需要此事件,就拦截,外部拦截法需要重写父容器的onInterceptTouchEvent方法进行拦截
内部拦截法:内部拦截法指父容器不拦截任何事件,所有事件都传递到子元素,如果子元素需要此事件就直接消耗掉,否则就交给父容器进行处理,内部拦截法需要重写子元素的dispatchTouchEvent方法,配合requestDisallowInterceptTouchEvent方法进行使用。
-
-
自定义View
-
都有哪些自定义View(继承现有view、拼接现有view、完全自定义)
-
自定义View有哪些注意事项
-
View的绘制流程(mesure、layout、draw)
(1) onMesure():测量视图大小,从顶层父View到子View递归调用mesure方法。
(2) onLayout():确定View位置,进行页面布局。从顶层父View向子View递归调用view.layout方法的过程,即父View根据上一步mesure子View所得到的布局大小和布局参数,将子View放在核实的位置上。
(3) onDraw():绘制视图,ViewRoot创建一个Canvas对象,然后调用onDraw()(绘制视图的背景、保存画布的图层、绘制view的内容、绘制view子视图,如果有的话、还原图层、绘制滚动条) -
invalidate、postinvalidate和requestlayout的区别
-
-
应用中如何进行数据缓存和更新
-
如何进行数据库迁移
-
Android版本适配
-
App或Activity的启动流程
-
你对插件化、组件化和热修复都是怎么理解的
-
你用过热修复框架吗,热修复的原理是什么
-
你对MVC、MVP、MVVM的理解
-
三方框架相关问题
-
ButterKnife(内部原理、APT流程,反射和APT技术的比较)
-
Okhttp拦截器是怎么实现的
-
如何使用okhttp缓存网络请求数据
-
Okhttp和volley的区别,你是怎么选择的
-
Glide和Picasso的区别
Glide和Picasso的使用方法基本相同,glide的包体积比较大,在缓存方面,picasso是将原图进行缓存,然后根据具体控件的大小resize,而glide则是将图片下载之后,根据控件大小进行resize,然后缓存,也就是说当同一张图片设置到不同控件的时候,picasso只会缓存一张图片,而glide则会缓存多张,内存使用上,glide比picasso要小一些,加载图片的时间对比,当第一次从网络上下载图片的时候,picasso的速度比较快,因为glide下载好之后需要进行resize然后放入缓存,而当从内存中加载图片的时候,glide比较快,因为picasso需要对图片进行resize,同时glide支持gif图片。 -
Glide缓存图片的key是怎么形成的(图片改变时glide是怎么更新图片的)
-
Glide是怎么感知应用的生命周期的(如何确定什么时候加载图片、什么时候停止加载)
-
LruCache的基本原理(怎么控制缓存大小、怎么实现最近最少原则)
LruCache的主要原理就是把最近使用的对象用强引用存储在LinkedHashMap中,当缓存满的时候,把最近最少使用的对象从内存中移除。LruCache提供了get和put方法来完成缓存的获取和添加
LruCache的核心思想就是维护一个缓存对象的列表,列表的排列方式是按照访问顺序来进行排列的,就是将一直没有访问的对象放在队尾,当缓存区满时最先淘汰,将最近访问的对象放在队头,最后淘汰。而LinkedHashMap刚好可以实现这个功能,LinkedHashMap的构造函数中的accessOrder设置为true表示用访问顺序来排序,false表用用插入顺序来排序。
当LruCache调用put方法的时候,就会在集合中添加元素,并调用trimToSize方法判断缓存是否已满,如果满了就调用LinkedHashMap的迭代器删除近期最少访问的元素。当调用get方法的时候,会调用LinkedHashMap的get方法获取集合中的元素,同时通过删除原有元素,更新该元素到队头。 -
RxJava的基本原理
-
RxJava是如何将操作符串起来的

-
RxJava是如何切换线程的

-
RxJava背压模式
-
Dagger2的作用和原理
-
数据库框架使用的是哪个?选择他的原因
-
如何进行数据库迁移
-
如何动态请求权限,使用的是什么框架
-
自己如何去封装一个网络请求、图片加载框架,有哪些需要注意的
-
- 稳定性(内存优化、崩溃分析)
- Memory Monitor、MAT、LeakCanary等工具分析内存泄漏并优化
- LeakCanary的原理(基于MAT,在ondestory方法调用后获取activity对象,如果不能被系统回收,并对他进行分析,查找内存泄漏原因)
- 流畅性(渲染优化、冷启动优化、减少主线程数据处理)
- 布局优化(减少嵌套、使用ConstraintLayout、使用include、merger、viewStub等标签、减少wrapcontent使用、删除无用属性)
- 冷启动优化(放置窗口背景图、异步或延后加载非必要数据)
- 动画优化(启动硬件加速、使用vector动画、使用motionlayout、使用lottie等)
- bolckCanary的原理(监测Main Lopper分发message耗时情况)
- 损耗性(流量、电量)
- 使用battery monitor进行电量分析,监测充电状态,在充电状态下做一些耗电操作
- 图片压缩(不同网络环境下请求不同清晰度的图片)
- 压缩网络请求数据,减少流量消耗
- 包瘦身
- 图片压缩(大图压缩、使用webP格式)
- 代码混淆、对齐(zip align)
- 资源优化(删除无用资源、三方库版本统一)
- 插件化处理(将部分模块放置在服务器中)
- 稳定性(内存优化、崩溃分析)
Http基础知识
-
TCP的三次握手和四次挥手
-
第一次握手:客服端发送syn包到服务器,并进入SYN_SENT状态,等待服务器确认
第二次握手:服务器收到syn包,确认客户端的syn,同时自己也发送一个syn包,服务器进入SYN_RECV状态
第三次握手:客户端收到服务器发来的syn包后,再向服务器发送确认包,发送完成之后,完成三次握手,服务器和客户端开始进行数据传输 -
第一次挥手:主动关闭方发送一个FIN,用来关闭主动方到被动方的数据传输,此时主动关闭方还可以接收数据
第二次挥手:被动关闭方收到FIN包后,发送一个确认包给主动关闭方。
第三次挥手:被动关闭方发送一个FIN包,用来关闭被动关闭方到主动关闭方的数据传输。
第四次挥手:主动关闭方收到FIN包后,发送一个确认包给被动关闭方,此时四次挥手完成。
-
-
TCP和UDP的区别及应用
- TCP面向连接,UDP是无连接的,即发送数据之前不需要建立连接
- TCP提供可靠服务(传输的数据无差错,不丢失,不重复且按序到达),UDP尽最大努力交付(不保证可靠交付)
- TCP面向字节流,UDP面向报文
- TCP连接是点对点的,UDP支持一对一,一对多,多对一和多对多交互
- TCP适合需要可靠数据的传输,UDP则适合对通信可靠性要求不是那么高,同时要求传输速度尽可能的快(例如语音通话等)
-
HTTP1.0和2.0的区别
HTTP1.0:浏览器每次请求都需要与服务器简历一个TCP连接,服务器处理完成之后立即断开TCP连接,服务器也不跟踪每个客户端也不记录过去的请求,即无连接无状态
HTTP2.0:HTTP2.0引入了二进制数据帧和流的概念,其中帧对数据进行顺序标识,这样服务器就可以并行的传输数据,浏览器接收到数据之后可以按照序列对数据进行合并,而不会出现数据错乱的情况。 -
HTTP与HTTPS的区别及HTTPS是如何实现安全性的
区别:
- HTTP是明文传输,传输内容容易被篡改或者窃取,HTTPS是密文传输
- HTTP在网络请求效率上会低于HTTP,因为采用了不同的请求协议和更复杂的安全验证
- HTTPS需要申请CA证书
HTTPS主要通过非对称加密+对称加密+CA证书来保证请求安全
HTTPS协议中使用了SSL协议加密,SSL的加密即用到了对称加密也用到了非对称加 密,对称加密速度快,非对称加密更加安全。 -
Socket和WebSocket的区别
WebSocket相对于HTTP的优势是支持长连接,WebSocket的整个通信过程是建立在一次连接中的,避免了HTTP的无状态性,服务端会一直知道你的信息,直到连接关闭。Socket处于抽象层,是HTTP协议的底层协议,而WebSocket是类似于HTTP协议的Http应用层协议。
HTTP协议的通信只能从客户端发起,服务器无法主动向客户端推送消息。WebSocket协议服务器可以主动向客户端推送消息,客户端也可以主动向服务器发送消息。 -
使用WebSocket时需要注意什么
算法
-
基础排序算法(冒泡、选择、插入、快速排序等),冒泡排序必须会手写,可以手写快速排序更好
手写冒泡排序Int a[] = {23,89,67,48,28,59,86}; Int temp = 0; for(int i = 0;i<a.length-1;i++){ for(int j = 0; j<a.length-1-i;j++){ if(a[j] > a[j+1]){ temp = a[j]; a[j] = a[j+1]; a[j+1] = temp; } } }快速排序
public static void quickSort(int[] arr, int left, int right) { //如果left大于等于right 退出 if (left >= right) { return; } //定义变量i指向最左边 int i = left; //定义变量j指向最右边 int j = right; //定义最左边的元素为基准元素 int base = arr[left]; //当i和j没有指向同一个元素的时候,交换他们的位置 while (i != j) { //从右向左寻找第一个小于基准元素的数 while (arr[j] >= base && i < j) { j--; } //从左向右寻找第一个大于基准元素的数 while (arr[i] <= base && i < j) { i++; } //交换i和j指向的元素 int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } //循环完成之后 表示i和j指向了同一元素,此时将其和基准元素进行交换 arr[left] = arr[i]; arr[i] = base; //将基准左侧数组进行排序 quickSort(arr, left, i - 1); //将基准右侧数据进行排序 quickSort(arr, i + 1, right); }
kotlin、flutter相关
- kotlin对比Java的优势
- 如何理解kotlin中的协程
- jetpack相关
- 协程的原理
- 你是如何看待flutter的
其他问题
- 项目中的技术亮点
- 工作中遇到最难解决的问题
- 作为leader的工作协调分配管理等问题,当有人对分配结果不满意时你是如何处理的
341



被折叠的 条评论
为什么被折叠?



