android
Android
Activity
- Activity的启动流程
启动流程详解
Activity启动过程详解
Android10.0应用进程创建过程以及Zygote的fork流程-[Android取经之路]
Android 源码解读-startActivity(含启动新应用) - onSaveInstanceState(),onRestoreInstanceState的掉用时机
调用时机 - activity的启动模式和使用场景
四种启动模式应用场景 - Activity A跳转Activity B,再按返回键,生命周期执行的顺序
生命周期执行的顺序 - 横竖屏切换,按home键,按返回键,锁屏与解锁屏幕,跳转透明Activity界面,启动一个 Theme 为 Dialog 的 Activity,弹出Dialog时Activity的生命周期
- onStart 和 onResume、onPause 和 onStop 的区别
区别 - Activity之间传递数据的方式Intent是否有大小限制,如果传递的数据量偏大,有哪些方案
使用 intent 传递数据时的大小限制 - scheme使用场景,协议格式,如何使用
Scheme协议的使用详解 - ANR 的四种场景
ANR问题总结分析 - onCreate和onRestoreInstance方法中恢复数据时的区别
- activty间传递数据的方式
- 跨App启动Activity的方式,注意事项
如何跨 App 启动 Activity? - 有哪些Activity常用的标记位Flags
Activity的标记位Flags - Activity的数据是怎么保存的,进程被Kill后,保存的数据怎么恢复的
Activity或进程重启时数据保存与恢复
Service
- service 的生命周期,两种启动方式的区别
service服务生命周期以及启动方式的区别 - Service启动流程
Android11源码分析:Service启动流程分析 - Service 的 onStartCommand 方法有几种返回值?各代表什么意思?
service的onStartCommand - bindService和startService混合使用的生命周期以及怎么关闭
startService与bindService混合使用对Service生命周期的影响
BroadcastReceiver
- 广播的分类和使用场景
广播的分类 - 广播的两种注册方式的区别
广播的两种注册方式 - 广播发送和接收的原理
广播发送和接收的原理 - 本地广播和全局广播的区别
本地广播与全局广播的区别
ContentProvider
-
什么是ContentProvider及其使用
-
ContentProvider,ContentResolver,ContentObserver之间的关系
之间的关系 -
ContentProvider的实现原理
-
Android系统为什么会设计ContentProvider,进程共享和线程安全问题
(1)提供一种跨进程数据共享的方式:
由系统来管理ContentProvider的创建、生命周期及访问的线程分配,简化我们在应用间共享数据(进程间通信)的方式。我们只管通过ContentResolver访问ContentProvider所提示的数据接口,而不需要担心它所在进程是启动还是未启动 。
(2)更好的数据访问权限管理:
ContentProvider可以对开发的数据进行权限设置,不同的URI可以对应不同的权限,只有符合权限要求的组件才能访问到ContentProvider的具体操作。
(3)不管Provider使用方是同一个进程的不同线程,还是不同的进程,Provider方实际上是同一个Provider对象实例。(当然,如果Provider方进程重启了,或者Provider本身或者Binder出现问题重启了,那另当别论,不是这里讨论的范围。)
(4)并发访问时,Provider方query()方法运行在不同的线程,实际上是运行在Provider方的进程的Binder线程池中。 -
ContentProvider的优点
-
Uri 是什么
Handler
- Handler的实现原理
- 子线程中能不能直接new一个Handler,为什么主线程可以
- 主线程的Looper第一次调用loop方法,什么时候,哪个类
- Handler导致的内存泄露原因及其解决方案
- 一个线程可以有几个Handler,几个Looper,几个MessageQueue对象
- Message对象创建的方式有哪些 & 区别?
- Message.obtain()怎么维护消息池的
- Handler 有哪些发送消息的方法
- Handler的post与sendMessage的区别和应用场景
- handler postDealy后消息队列有什么变化,假设先 postDelay 10s, 再postDelay 1s, 怎么处理这2条消息
- MessageQueue是什么数据结构
- Handler怎么做到的一个线程对应一个Looper,如何保证只有一个MessageQueue
- ThreadLocal在Handler机制中的作用
- HandlerThread是什么 & 好处 &原理 & 使用场景
- IdleHandler及其使用场景
原理分析和妙用 - 消息屏障,同步屏障机制
- 为什么Android系统不建议子线程访问UI
Android为什么不允许在子线程中访问UI - Android中为什么主线程不会因为Looper.loop()里的死循环卡死
- MessageQueue#next 在没有消息的时候会阻塞,如何恢复?
- Handler消息机制中,一个looper是如何区分多个Handler的
looper如何区分handler - 当Activity有多个Handler的时候,怎么样区分当前消息由哪个Handler处理
- 处理message的时候怎么知道是去哪个callback处理的
- Looper.quit/quitSafely的区别
- 通过Handler如何实现线程的切换
- Handler 如何与 Looper 关联的
- Looper 如何与 Thread 关联的
- Looper.loop()源码
- MessageQueue的enqueueMessage()方法如何进行线程同步的
- MessageQueue的next()方法内部原理
- 子线程中是否可以用MainLooper去创建Handler,Looper和Handler是否一定处于一个线程
- ANR和Handler的联系
View绘制
-
View绘制流程
一篇文章带你完全梳理自定义View工作流程! -
MeasureSpec是什么
MeasureSpac它是一个32位 int类型数值,高两位 SpacMode代表测量模式,低30位 SpacSize代表测量尺寸
UNSPECIFIED :父布局不会对子View做任何限制,例如我们常用的 ScrollView就是这种测量模式。
EXACTLY :精确数值,比如使用了 match_parent或者xxxdp,表示父布局已经决定了子 View的大小,通常在这种情况下 View的尺寸就是 SpacSize
AT_MOST :自适应,对应 wrap_content子View可以根据内容设置自己的大小,但前提是不能超出父 ViewGroup的宽高。 -
子View创建MeasureSpec创建规则是什么
-
自定义View Wrap_content不起作用的原因
-
在Activity中获取某个View的宽高有几种方法
-
为什么onCreate获取不到View的宽高
-
View#post与Handler#post的区别
-
Android绘制和屏幕刷新机制原理
动画系列(一)Android 屏幕刷新机制 -
Choreography原理
Android图形系统(十一)-Choreographer -
什么是双缓冲
-
为什么使用SurfaceView
-
什么是SurfaceView
-
SurfaceFlinger是什么东西
SurfaceFlinger是一个特殊进程,主要负责合成所有Surface到Framebuffer,然后屏幕去读取这个Framebuffer,然后显示给用户看。
SurfaceFlinger是什么东西
Android Systrace 基础知识 - SurfaceFlinger 解读 -
View和SurfaceView的区别
-
SurfaceView为什么可以直接子线程绘制
-
SurfaceView、TextureView、SurfaceTexture、GLSurfaceView
关于 SurfaceView 和 TextureView -
getWidth()方法和getMeasureWidth()区别
-
invalidate() 和 postInvalidate() 的区别
-
Requestlayout,onlayout,onDraw,DrawChild区别与联系
-
LinearLayout、FrameLayout 和 RelativeLayout 哪个效率高
-
LinearLayout的绘制流程
-
自定义 View 的流程和注意事项
-
自定义View如何考虑机型适配
-
自定义控件优化方案
-
invalidate怎么局部刷新
-
View加载流程(setContentView)
View事件分发
-
View事件分发机制
一个view事件分发,面试官6连问直击灵魂,我被虐的体无完肤 -
view的onTouchEvent,OnClickListerner和OnTouchListener的onTouch方法 三者优先级
onTouchListener优先级最高,如果onTouch方法返回 false ,那onTouchEvent就被调用了,返回true 就不会被调用。至于onClick 优先级最低 -
onTouch 和onTouchEvent 的区别
OnTouch和OnTouchEvent的区别 -
事件是先到DecorView还是先到Window
这一次,带你彻底弄懂 Android 事件分发机制(外/内层责任链) -
点击事件被拦截,但是想传到下面的View,如何操作
重写子类的requestDisallowInterceptTouchEvent()方法返回true就不会执行父类的onInterceptTouchEvent(),即可将点击事件传到下面的View
RecycleView
RecyclerView 面试题 | 滚动时表项是如何被填充或回收的?
- RecyclerView的多级缓存机制,每一级缓存具体作用是什么,分别在什么场景下会用到哪些缓存
- RecyclerView的滑动回收复用机制
- RecyclerView的刷新回收复用机制
- RecyclerView 为什么要预布局
- ListView 与 RecyclerView区别
- RecyclerView性能优化
Viewpager&Fragment
- Fragment的生命周期 & 结合Activity的生命周期
- Activity和Fragment的通信方式, Fragment之间如何进行通信
- 为什么使用Fragment.setArguments(Bundle)传递参数
- FragmentPageAdapter和FragmentStatePageAdapter区别及使用场景
- Fragment懒加载
- ViewPager2与ViewPager区别
- Fragment嵌套问题
WebView
- 如何提高WebView加载速度
- WebView与 js的交互
- WebView的漏洞
- JsBridge原理
动画
- 动画的类型
- 补间动画和属性动画的区别
- ObjectAnimator,ValueAnimator及其区别
- TimeInterpolator插值器,自定义插值器
- TypeEvaluator估值器
Bitmap
- Bitmap 内存占用的计算
- getByteCount() & getAllocationByteCount()的区别
- Bitmap的压缩方式
- LruCache & DiskLruCache原理
- 如何设计一个图片加载库
- 有一张非常大的图片,如何去加载这张大图片
- 如果把drawable-xxhdpi下的图片移动到drawable-xhdpi下,图片内存是如何变的。
- 如果在hdpi、xxhdpi下放置了图片,加载的优先级。如果是400800,10801920,加载的优先级。
Binder
-
Android中进程和线程的关系,区别
进程和线程的区别 -
为何需要进行IPC,多进程通信可能会出现什么问题
-
Android中IPC方式有几种、各种方式优缺点
Android系统中一个应用默认只有一个进程,每个进程都有自己独立的资源和内存空间,其它进程不能任意访问当前进程的内存和资源,系统给每个进程分配的内存会有限制。如果一个进程占用内存超过了这个内存限制,就会报OOM的问题多进程模式出现以下问题:
1、静态成员和单例模式完全失效
2、线程同步机制完全失效
3、SharedPreferences的可靠性下降
4、Application多次创建 -
为何新增Binder来作为主要的IPC方式
为什么 Android 要采用 Binder 作为 IPC 机制? -
什么是Binder
从机制、模型角度来说,Binder是Android为了满足系统对通信方式,传输性能和安全性的要求而建立的一套新的IPC机制。Binder基于Client-Server通信模式,传输过程只需一次拷贝(这是通过内存映射实现的),为发送方添加UID/PID身份,既支持实名Binder也支持匿名Binder,安全性高。
从模型的结构、组成来说,Binder是一种虚拟的物理设备驱动,连接着Service进程、Client进程和ServiceManager进程。
从Android代码角度来说,Binder是一个实现了IBinder接口的类,将Binder机制模型以代码的形式实现在Android中。 -
Binder的原理
Android通信方式篇(四)-Binder机制(开篇)
Android通信方式篇(五)-Binder机制(Kernel层)
Android通信方式篇(六)-Binder机制(Native层(上))
Android通信方式篇(七)-Binder机制(Native层(下))
Android通信方式篇(八)-Binder机制(Framework层) -
Binder Driver 如何在内核空间中做到一次拷贝的?
谈一谈Binder的原理和实现一次拷贝的流程 -
什么是AIDL
Android AIDL
你真的懂AIDL的oneway嘛? -
AIDL支持哪些数据类型
Java基本类型、String、List、Map(List,Map内的元素必须是AIDL支持,List接收方必须是ArrayList,Map接收方必须是HashMap)、其他AIDL定义的AIDL接口(传递的是引用)、实现Parcelable的类 -
AIDL的关键类,方法和工作流程
AIDL接口:继承IInterface。
Stub类:Binder的实现类,服务端通过这个类来提供服务。
Proxy类:服务器的本地代理,客户端通过这个类调用服务器的方法。
asInterface():客户端调用,将服务端的返回的Binder对象,转换成客户端所需要的AIDL接口类型对象。返回对象。
asBinder():根据当前调用情况返回代理Proxy的Binder对象。
onTransact():运行服务端的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理。
transact():运行在客户端,当客户端发起远程请求的同时将当前线程挂起。之后调用服务端的onTransact()直到远程请求返回,当前线程才继续执行。
若客户端和服务端位于同一进程,则直接返回Stub对象本身;
否则,返回的是系统封装后的Stub.proxy对象。 -
如何优化多模块都使用AIDL的情况
AIDL的本质是系统提供了一套可快速实现Binder的工具。关键类和方法:当有多个业务模块都需要AIDL来进行IPC,此时需要为每个模块创建特定的aidl文件,那么相应的Service就会很多。必然会出现系统资源耗费严重、应用过度重量级的问题。解决办法是建立Binder连接池,即将每个业务模块的Binder请求统一转发到一个远程Service中去执行,从而避免重复创建Service。 -
使用 Binder 传输数据的最大限制是多少,被占满后会导致什么问题
一次Binder通信最大可以传输是1MB-8KB(PS:8k是两个pagesize,一个pagesize是申请物理内存的最小单元)
一次Binder通信最大可以传输多大的数据 -
Binder 驱动加载过程中有哪些重要的步骤
Android10.0 Binder通信原理(五)-Binder驱动分析 -
Activity的bindService流程
由浅入深 学习 Android Binder(二)- bindService流程
Android四大组件系列6 bindService流程
内存泄漏&内存溢出
-
什么是OOM & 什么是内存泄漏以及原因
-
Thread是如何造成内存泄露的,如何解决?
匿名内部类 Thread 实例会长久运行,不会被系统 GC 回收。Threads 在 Java 中是 GC roots,意味着 Dalvik Virtual Machine (DVM) 会为所有活跃的 threads 在运行时系统中保持一个硬引用,这会导致 threads 一直处于运行状态,垃圾收集器将永远不可能回收它。
其次,非静态内部类会持有外部类的引用。Thread 会长久地持有 Activity 的引用,使得系统无法回收 Activity 和它所关联的资源和视图。 -
Handler导致的内存泄露的原因以及如何解决
同上面的做法一样 -
如何加载Bitmap防止内存溢出
bitmap引起的内存溢出OutOfMemory解决方案
性能优化
Android 性能优化必知必会
java中的垃圾回收机制,你理解了嘛?
强引用置为null,会不会被回收
图解 Java 垃圾回收机制
- 内存优化
Android内存优化大总结 - 启动优化
- 布局加载和绘制优化
- 卡顿优化
Android Systrace 基础知识 – 分析 Systrace 预备知识 - 网络优化
Window&WindowManager
- 什么是Window
Window全面解析 - 什么是WindowManager
带你彻底理解 Window 和 WindowManager
Window系列 (一) — WindowManager 详解 - 什么是ViewRootImpl
ViewRootImpl介绍
ViewRootImpl的独白,我不是一个View(布局篇) - 什么是DecorView
DecorView 一窥全貌(上)
DecorView 一窥全貌(下) - Activity,View,Window三者之间的关系
Activity 与 Window 与 View 之间的关系
Android图形系统(四)-Activity、Window、View关系总结 - DecorView什么时候被WindowManager添加到Window中
WMS
- 什么是WMS,WMS是如何管理Window的
Android WMS(一)-窗口管理
Android WMS(二)-Surface管理
Android WMS(三)- Input管理 - IWindowSession是什么,WindowSession的创建过程是怎样的
- WindowToken是什么
《深入理解Android 卷III》第四章 深入理解WindowManagerService - WindowState是什么
Android WMS(二)-Surface管理 - Android窗口大概分为几种?分组原理是什么
- Dialog的Context只能是Activity的Context,不能是Application的Context
Android中的Application真的不能弹出Dialog吗(Window全面解析) - App应用程序如何与SurfaceFlinger通信的
Android图形系统(七)-app请求SurfaceFlinger创建Surface过程
Android图形系统(八)-app与SurfaceFlinger共享UI元数据过程 - View 的绘制是如何把数据传递给 SurfaceFlinger 的
Android图形系统(九)-View、Canvas与Surface的关系 - 共享内存的具体实现是什么
- relayout是如何向SurfaceFlinger申请Surface
- 什么是Surface
Android图形系统(五)-Surface图形系统概览 - 什么是SurfaceFlinger
android的surfaceflinger原理学习
AMS
- ActivityManagerService是什么?什么时候初始化的?有什么作用?
AMS启动流程以及Hook该流程 - ActivityThread是什么?ApplicationThread是什么?他们的区别
ActivityThread在Android中它就代表了Android的主线程,但是并不是一个Thread类。
严格来说,UI主线程不是ActivityThread。ActivityThread类是Android APP进程的初始类,它的main函数是这个APP进程的入口。APP进程中UI事件的执行代码段都是由ActivityThread提供的。也就是说,Main Thread实例是存在的,只是创建它的代码我们不可见。ActivityThread的main函数就是在这个Main Thread里被执行的。
ApplicationThread是ActivityThread的内部类,也是一个Binder对象。在此处它是作为Activitythread和AMS通信的桥梁.
ActivityThread、ApplicationThread和ActivityClientRecord
- Instrumentation是什么?和ActivityThread是什么关系?
ActivityThread就是我们常说的主线程或UI线程,ActivityThread的main方法是整个APP的入口,本篇深入学习下ActivityThread,顺便了解下APP和Activity的启动过程。
ActivityThread的理解和APP的启动过程
Instrumentation的理解 - ActivityManagerService和zygote进程通信是如何实现的
App进程是AMS通过通过Socket通信通知Zygote孵化出来的
Zygote中Socket通信能否替换成Binder通信? - ActivityRecord、TaskRecord、ActivityStack,ActivityStackSupervisor,ProcessRecord
ActivityStackSupervisor,ActivityStack,ActivityRecord,TaskRecord的关系
ActivityRecord、TaskRecord、ActivityStack以及Activity启动模式详解
深入理解ActivityManagerService,你知道的不知道的全在这里! - ActivityManager、ActivityManagerService、ActivityManagerNative、
ActivityManagerProxy的关系
系统启动
Android系统启动(一)-开篇
Android系统启动(二)-Init篇
Android系统启动(三)-Zygote篇
Android系统启动(四)-SystemServer篇
Android是怎么启动的-[Android取经之路]
Android 10.0系统启动之init进程-[Android取经之路]
Android 10.0系统启动之Zygote进程-[Android取经之路]
Android 10.0系统启动之SystemServer进程-[Android取经之路]
Android 10.0系统服务之ActivityMnagerService-AMS启动流程-[Android取经之路]
Android 10.0系统启动之Launcher(桌面)启动流程-[Android取经之路]
- android系统启动流程
Android系统启动流程分析 - SystemServer,ServiceManager,SystemServiceManager的关系
- 孵化应用进程这种事为什么不交给SystemServer来做,而专门设计一个Zygote
我们知道,应用在启动的时候需要做很多准备工作,包括启动虚拟机,加载各类系统资源等等,这些都是非常耗时的,如果能在zygote里就给这些必要的初始化工作做好,子进程在fork的时候就能直接共享,那么这样的话效率就会非常高。这个就是zygote存在的价值,这一点呢SystemServer是替代不了的,主要是因为SystemServer里跑了一堆系统服务,这些是不能继承到应用进程的。而且我们应用进程在启动的时候,内存空间除了必要的资源外,最好是干干净净的,不要继承一堆乱七八糟的东西。所以呢,不如给SystemServer和应用进程里都要用到的资源抽出来单独放在一个进程里,也就是这的zygote进程,然后zygote进程再分别孵化出SystemServer进程和应用进程。孵化出来之后,SystemServer进程和应用进程就可以各干各的事了。 - Zygote的IPC通信机制为什么使用socket而不采用binder
第一个原因,我们可以设想一下采用binder调用的话该怎么做,首先zygote要启用binder机制,需要打开binder驱动,获得一个描述符,再通过mmap进行内存映射,还要注册binder线程,这还不够,还要创建一个binder对象注册到serviceManager,另外AMS要向zygote发起创建应用进程请求的话,要先从serviceManager查询zygote的binder对象,然后再发起binder调用,这来来回回好几趟非常繁琐,相比之下,zygote和SystemServer进程本来就是父子关系,对于简单的消息通信,用管道或者socket非常方便省事。第二个原因,如果zygote启用binder机制,再fork出SystemServer,那么SystemServer就会继承了zygote的描述符以及映射的内存,这两个进程在binder驱动层就会共用一套数据结构,这显然是不行的,所以还得先给原来的旧的描述符关掉,再重新启用一遍binder机制,这个就是自找麻烦了。
App启动&打包&安装
- 应用启动流程
进程是如何启动的? - apk组成和Android的打包流程
- Android的签名机制,签名如何实现的,v2相比于v1签名机制的改变
Android签名机制及原理
v1签名是对jar进行签名,V2签名是对整个apk签名:官方介绍就是:v2签名是在整个APK文件的二进制内容上计算和验证的,v1是在归档文件中解压缩文件内容。
二者签名所产生的结果:
v1:在v1中只对未压缩的文件内容进行了验证,所以在APK签名之后可以进行很多修改——文件可以移动,甚至可以重新压缩。即可以对签名后的文件在进行处理
v2:v2签名验证了归档中的所有字节,而不是单独的ZIP条目,如果您在构建过程中有任何定制任务,包括篡改或处理APK文件,请确保禁用它们,否则您可能会使v2签名失效,从而使您的APKs与Android 7.0和以上版本不兼容。 - APK的安装流程
应用安装(一) - 三方应用发起apk系统安装
应用安装(二) - PackageInstaller中转apk安装
应用安装(三)- 系统实现apk安装整体流程
序列化
- 什么是序列化
- 为什么需要使用序列化和反序列化
- 序列化的有哪些好处
- Serializable 和 Parcelable 的区别
- 什么是serialVersionUID
- 为什么还要显示指定serialVersionUID的值?
Art & Dalvik 及其区别
-
Art & Dalvik 及其区别
Android虚拟机ART和Dalvik区别
ART 是一种执行效率更高且更省电的运行机制,执行的是本地机器码,这些本地机器码是从dex字节码转换而来。ART采用的是AOT(Ahead-Of-Time)编译,应用在第一次安装的时候,字节码就会预先编译成机器码存储在本地。在App运行时,ART模式就较Dalvik模式少了解释字节码的过程,所以App的运行效率会有所提高,占用内存也会相应减少。谷哥在5.0以后的Android版本中默认了ART模式启动,就是希望Android能摆脱卡顿这个毛病。Dalvik 虚拟机采用的是JIT(Just-In-Time)编译模式,意思为即时编译,我们知道apk被安装到手机中时,对应目录会有dex或odex和apk文件,apk文件存储的是资源文件,而dex或odex(经过优化后的dex文件内部存储class文件)内部存储class文件,每次运行app时虚拟机会将dex文件解释翻译成机器码,这样才算是本地可执行代码,之后被系统运行。
Dalvik虚拟机可以看做是一个Java VM,他负责解释dex文件为机器码,如果我们不做处理的话,每次执行代码,都需要Dalvik将dex代码翻译为微处理器指令,然后交给系统处理,这样效率不高。为了解决这个问题,Google在2.2版本添加了JIT编译器,当App运行时,每当遇到一个新类,JIT编译器就会对这个类进行编译,经过编译后的代码,会被优化成相当精简的原生型指令码(即native code),这样在下次执行到相同逻辑的时候,速度就会更快。
模块化&组件化
- 什么是模块化
- 什么是组件化
- 组件化优点和方案
- 组件独立调试
- 组件间通信
- Aplication动态加载
- ARouter原理
热修复&插件化
- 插件化的定义
- 插件化的优势
- 插件化框架对比
- 插件化流程
- 插件化类加载原理
- 插件化资源加载原理
- 插件化Activity加载原理
- 热修复和插件化区别
- 热修复原理
AOP
- AOP是什么
- AOP的优点
- AOP的实现方式,APT,AspectJ,ASM,epic,hook
- jectpack
- Navigation
- DataBinding
- Viewmodel
- livedata
- liferecycle
开源框架
- Okhttp源码流程,线程池
Okhttp源码分析(流程和过滤器) - Okhttp拦截器,addInterceptor 和 addNetworkdInterceptor区别
- Okhttp责任链模式
- Okhttp缓存怎么处理
- Okhttp连接池和socket复用
- Glide怎么绑定生命周期
Glide 4.11.0源码走读
Glide缓存机制解析
Glide磁盘缓存研究
宏观剖析Glide4.8.0源码 - Glide缓存机制,内存缓存,磁盘缓存
- Glide与Picasso的区别
- LruCache原理
- Retrofit源码流程,动态代理
- LeakCanary弱引用,源码流程
- Eventbus
- Rxjava
java
HashMap
-
HashMap原理
HashMap实现原理
HashMap底层实现原理 -
HashMap1.7和1.8有哪些区别
1、jdk8中当链表长度大于8时会将链表转化成为红黑树
2、节点插入顺序不同,jdk7采用头插法,而jdk8采用尾插法
3、hash算法的简化
4、扩容不同,在jdk7中,发生扩容,它会把原来所有的元素重新计算hash。再插入到新的位置。而jdk8中则是直接copy过去,要么位置不变,要么位置更改为索引+原数组长度 -
解决hash冲突的时候,为什么用红黑树
如果冲突过多,会导致链表过长,降低查询性能,均匀的hash函数能有效的缓解冲突过多,但是并不能完全避免。所以HashMap加入了另一种解决方案,在往链表后追加节点时,如果发现链表长度达到8,就会将链表转为红黑树,以此提升查询的性能。 -
红黑树的效率高,为什么一开始不用红黑树存储
-
不用红黑树,用二叉查找树可以不
之所以选择红黑树是为了解决二叉查找树的缺陷,二叉查找树在特殊情况下会变成一条线性结构(这就跟原来使用链表结构一样了,造成很深的问题),遍历查找会非常慢。而红黑树在插入新数据后可能需要通过左旋,右旋、变色这些操作来保持平衡,引入红黑树就是为了查找数据快,解决链表查询深度的问题,我们知道红黑树属于平衡二叉树,但是为了保持“平衡”是需要付出代价的,但是该代价所损耗的资源要比遍历线性链表要少,所以当长度大于8的时候,会使用红黑树,如果链表长度很短的话,根本不需要引入红黑树,引入反而会慢。 -
为什么阀值是8才转为红黑树
TreeNodes占用空间是普通Nodes的两倍,所以只有当bin(bin就是bucket-桶,即HashMap中hashCode值一样的元素保存的地方)包含足够多的节点时才会转成TreeNodes,而是否足够多就是由TREEIFY_THRESHOLD的值决定的。当bin中节点数变少时,又会转成普通的bin。 -
为什么退化为链表的阈值是6
如果不设退化阀值,只以8来树化与退化:
那么8将成为一个临界值,时而树化,时而退化,此时会非常影响性能,因此,我们需要一个比8小的退化阀值;考虑到内存(树节点比普通节点内存大2倍,以及避免反复转化),所以,退化阀值最多为6。
-
HashMap在什么条件下扩容
新建的HashMap容量为DEFAULT_INITIAL_CAPACITY=16,系数DEFAULT_LOAD_FACTOR=0.75,
当第一次扩容时目前的元素数量>=DEFAULT_INITIAL_CAPACITY*DEFAULT_LOAD_FACTOR时,会将HashMap的容量扩大一倍(x2),一次类推每次加入数据时都会判断当前的元素数与容量的占比。
最大个数为Integer.MAX_VALUE即2147483648。
造成HashMap扩容的操作:putMapEntries():插入
putVal()
put()
treeifyBin()
merge():合并 -
HashMap中hash函数怎么实现的,还有哪些hash函数的实现方式
HashMap中hash函数怎么是实现的? -
链表的查找的时间复杂度是多少
单向链表要删除某一节点时,必须要先通过遍历的方式找到前驱节点(通过待删除节点序号或按值查找)。若仅仅知道待删除节点,是不能知道前驱节点的,故单链表的增删操作复杂度为O(n)。 -
红黑树
30张图带你彻底理解红黑树
Jvm
-
Jvm的内存模型,每个里面都保存的什么
jvm 内存模型
JVM 内存共分为虚拟机栈,堆,方法区,程序计数器,本地方法栈五个部分程序计数器(线程私有):
是当前线程锁执行字节码的行号治时期,每条线程都有一个独立的程序计数器,这类内存也称为“线程私有”的内存。正在执行java方法的话,计数器记录的是虚拟机字节码指令的地址(当前指令的地址)。如果是Native方法,则为空。java 虚拟机栈
也是线程私有的。
每个方法在执行的时候也会创建一个栈帧,存储了局部变量,操作数,动态链接,方法返回地址。
每个方法从调用到执行完毕,对应一个栈帧在虚拟机栈中的入栈和出栈。
通常所说的栈,一般是指在虚拟机栈中的局部变量部分。
局部变量所需内存在编译期间完成分配,
如果线程请求的栈深度大于虚拟机所允许的深度,则StackOverflowError。
如果虚拟机栈可以动态扩展,扩展到无法申请足够的内存,则OutOfMemoryError。本地方法栈(线程私有)
和虚拟机栈类似,主要为虚拟机使用到的Native方法服务。也会抛出StackOverflowError 和OutOfMemoryError。Java堆(线程共享)
被所有线程共享的一块内存区域,在虚拟机启动的时候创建,用于存放对象实例。
对可以按照可扩展来实现(通过-Xmx 和-Xms 来控制)
当队中没有内存可分配给实例,也无法再扩展时,则抛出OutOfMemoryError异常。方法区(线程共享)
被所有方法线程共享的一块内存区域。
用于存储已经被虚拟机加载的类信息,常量,静态变量等。
这个区域的内存回收目标主要针对常量池的回收和堆类型的卸载。 -
类加载机制的几个阶段加载、验证、准备、解析、初始化、使用、卸载
Java类加载机制,你理解了吗? -
对象实例化时的顺序
Java程序在执行过程中,类,对象以及它们成员加载、初始化的顺序如下:
1、首先加载要创建对象的类及其直接与间接父类。
2、在类被加载的同时会将静态成员进行加载,主要包括静态成员变量的初始化,静态语句块的执行,在加载时按代码的先后顺序进行。
3、需要的类加载完成后,开始创建对象,首先会加载非静态的成员,主要包括非静态成员变量的初始化,非静态语句块的执行,在加载时按代码的先后顺序进行。
4、最后执行构造器,构造器执行完毕,对象生成。java对象实例化时的顺序为:
1,父类的静态成员变量和静态代码块加载,加载时按代码的先后顺序进行。
2,子类的静态成员变量和静态代码块加载,加载时按代码的先后顺序进行。
3,父类成员变量和方法块加载,加载时按代码的先后顺序进行。
4,父类的构造函数加载
5,子类成员变量和方法块加载,加载时按代码的先后顺序进行。
6,子类的构造函数加载 -
类加载器,双亲委派及其优势
系统类加载器 (3种)
BootClassLoader Android 系统启动时会使用BootClassLoader来预加载常用类
PathClassLoader 用来加载系统类和应用程序的类
DexClassLoader 可以加载dex文件,也可以加载包含dex的压缩文件(apk 和jar文件)双亲委派模型是指当我们调用类加载器的loadClass()进行类加载时,该类加载器会首先请求它的父类加载器进行加载,依次递归.如果所有父类加载器都加载失败,则当前类加载器自己进行加载操作.
1-类加载器收到类加载的请求;
2-把这个请求委托给父加载器去完成,一直向上委托直到启动类加载器;
3-启动器加载器检查能不能加载(使用findClass()方法),能就加载(结束);否则,抛出异常,通知子加载器进行加载.
4-重复步骤三优点:
1:安全,可避免用户自己编写的类动态替换Java的核心类,如java.lang.String
2:避免全限定命名的类重复加载(使用了findLoadClass()判断当前类是否已加载)
这种设计有个好处是,如果有人想替换系统级别的类:String.在双亲委派机制下这些系统类已经被Bootstrap classLoader加载过了,不会再去加载,从一定程度上防止了危险代码的植入.
多线程
-
Java中创建线程的方式,Callable,Runnable,Future,FutureTask
Java中创建线程到底有几种方式?
FutureTask详解 -
线程的几种状态
线程的5种状态详解
线程从创建、运行到结束总是处于下面五个状态之一:新建状态、就绪状态、运行状态、阻塞状态及死亡状态。
1.新建状态(New):
当用new操作符创建一个线程时, 例如new Thread®,线程还没有开始运行,此时线程处在新建状态。 当一个线程处于新生状态时,程序还没有开始运行线程中的代码2.就绪状态(Runnable)
一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。当线程对象调用start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态。3.运行状态(Running)
当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法.4.阻塞状态(Blocked)
线程运行过程中,可能由于各种原因进入阻塞状态:
1>线程通过调用sleep方法进入睡眠状态;
2>线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;
3>线程试图得到一个锁,而该锁正被其他线程持有;
4>线程在等待某个触发条件;5.死亡状态(Dead)
有两个原因会导致线程死亡:
1) run方法正常退出而自然死亡,
2) 一个未捕获的异常终止了run方法而使线程猝死。
为了确定线程在当前是否存活着(就是要么是可运行的,要么是被阻塞了),需要使用isAlive方法。如果是可运行或被阻塞,这个方法返回true; 如果线程仍旧是new状态且不是可运行的, 或者线程死亡了,则返回false. -
synchronized和Lock的使用、区别,原理;
详解synchronized与Lock的区别与使用
Syschronized的底层实现原理以及各种锁的理解 -
synchronized和volatile的区别?为何不用volatile替代synchronized?
1、volatile只能作用于变量,使用范围较小。synchronized可以用在变量、方法、类、同步代码块等,使用范围比较广。
2、volatile只能保证可见性和有序性,不能保证原子性。而可见性、有序性、原子性synchronized都可以包证。
3、volatile不会造成线程阻塞。synchronized可能会造成线程阻塞。 -
锁的分类,锁的几种状态,CAS原理
Java中的锁分类与使用
Java 常见的锁分类及其特点 -
Java多线程通信
JAVA多线程之线程间的通信方式 -
Java中的线程池参数,共有几种
Java线程池使用和常用参数 -
线程安全的 List 集合有什么
CopyOnWriteArrayList、SynchronizedList -
CopyOnWriteArrayList 的特点以及使用场景?
CopyOnWriteArrayList 的写时复制
反射
- 什么是反射
- 反射机制的相关类
- 反射中如何获取Class类的实例
- 如何获取一个类的属性对象 & 构造器对象 & 方法对象
- Class.getField和getDeclaredField的区别,getDeclaredMethod和getMethod的区别
- 反射机制的优缺点