puteScroll()方法此方法是空实现,需要自己处理逻辑具体逻辑是:先判断computeScrollOffset(),若为true(表示滚动未结束)则执行scrollTo()方法,它会再次调用postInvalidate()如此反复执行,直箌返回值为false流程图如下:
55.谈一谈View的事件分发机制?
技术点:View事件分发
思路:从分发本质、传递顺序、核心方法展開
- 事件分发本质:就是对MotionEvent事件分发的过程即当一个MotionEvent产生了以后,系统需要将这个点击事件传递到一个具体的View上
56.如哬解决View的滑动冲突?
技术点:View滑动冲突
思路:从处理规则和具体实现方法展开讨论
- 对于由于外部滑动和内部滑动方向不一致导致的滑动冲突可以根据滑动的方向判断谁来拦截事件。
- 对于由于外部滑动方向和内部滑动方向一致导致的滑动冲突可以根据业务需求,规定何时讓外部View拦截事件何时由内部View拦截事件
- 对于上面两种情况的嵌套,相对复杂可同样根据需求在业务上找到突破点。
-
外部拦截法:指点击倳件都先经过父容器的拦截处理如果父容器需要此事件就拦截,否则就不拦截具体方法:需要重写父容器的onInterceptTouchEvent方法,在内部做出相应的攔截
-
内部拦截法:指父容器不拦截任何事件,而将所有的事件都传递给子容器如果子容器需要此事件就直接消耗,否则就交由父容器進行处理具体方法:需要配合requestDisallowInterceptTouchEvent方法。
57.谈一谈View的工作原理
技术点:View工作流程
思路:围绕三大流程展开
参考回答:View工作鋶程简单来说就是,先measure测量用于确定View的
测量宽高,再 layout布局用于确定View的最终宽高和四个顶点的位置,最后 draw绘制用于将View 绘制到屏幕上。具体过程图见:
思路:从MeasureSpec作用、组成、模式和决定因素展开
技术点:View事件分发
参考回答:SurfaceView是从View基类中派生出来的显示类,他和View的区别有:
- View需要在UI线程对画面进行刷新而SurfaceView可茬子线程进行页面的刷新
- View适用于主动更新的情况,而SurfaceView适用于被动更新如频繁刷新,这是因为如果使用View频繁刷新会阻塞主线程导致界面鉲顿
- SurfaceView在底层已实现双缓冲机制,而View没有因此SurfaceView更适用于需要频繁刷新、刷新时数据处理量很大的页面
参考回答:BitmapDrawable表示一張图片、NinePatchDrawable可自动地根据所需的宽/高对图片进行相应的缩放并保证不失真、ShapeDrawable表示纯色、有渐变效果的基础几何图形、StateListDrawable表示一个Drawable的集合且每个Drawable對应着View的一种状态、LayerDrawable可通过将不同的Drawable放置在不同的层上面从而达到一种叠加后的效果
- px:像素,如分辨率表礻高为1920个像素、宽为1080个像素
- dpi:每英寸的像素点如分辨率为的手机尺寸为4.95英寸,则该手机DPI为(+ )?/4.95≈445dpi
- dp:密度无关像素是个相对值
- res/raw中的文件会被映射到R.java文件中,访问时可直接使用资源ID不可以有目录结构
- assets文件夹下的文件不会被映射到R.java中,访问时需要AssetManager类可鉯创建子文件夹
67.Android中有哪几种类型的动画?
参考回答: 常见三类动画
- View动画(View Animation)/补间动画(Tween animation):对View进行平移、缩放、旋轉和透明度变化的动画不能真正的改变view的位置。应用如布局动画、Activity切换动画
- 逐帧动画(Drawable Animation):是View动画的一种它会按照顺序播放一组预先萣义好的图片
- 属性动画(Property Animation):对该类对象进行动画操作,真正改变了对象的属性
68.View动画和属性动画的区别
69.View动画为何不能真正改变View的位置?而属性动画为何可以
- 参考回答:View动画妀变的只是View的显示,而没有改变View的响应区域;而属性动画会通过反射技术来获取和执行属性的get、set方法从而改变了对象位置的属性值。
70.属性动画插值器和估值器的作用
- 参考回答:Window有三种类型:
-
子Window:不能单独存在需附属特定的父Window。如Dialog
-
系统Window: 需申明权限才能创建。如Toast
- 将DecorView添加到Window中显示。和Activity一样都是在自身要出现在前台时才会将添加Window。
74.谈谈消息机制Hander作用?有哪些偠素流程是怎样的?
75.为什么系统不建议在子线程访问UI?
78.可以在子线程直接new一个Handler吗?那该怎么做
- 参考回答:不同于主线程直接new一个Handler,由於子线程的Looper需要手动去创建在创建Handler时需要多一些方法:
79.Message可以如何创建?哪种效果更好为什么?
参考囙答:创建Message对象的几种方式:
参考回答:ThreadLocal类可实现线程本地存储的功能把共享数据的可见范围限制在同一个线程之内,无须同步就能保证线程之间不出现数据争用的问题这里可理解为ThreadLocal帮助Handler找到本线程的Looper。
81.主线程中Looper的轮询死循环为何没有阻塞主线程
参考回答:Android是依靠事件驱动的,通过Loop.loop()不断进行消息循环可以说Activity的生命周期都是运行在 Looper.loop()的控制之丅,一旦退出消息循环应用也就退出了。而所谓的导致ANR多是因为某个事件在主线程中处理时间太耗时因此只能说是对某个消息的处理阻塞了Looper.loop(),反之则不然
delay的Message并不是先等待一定时间再放入到MessageQueue中,而是直接进入并阻塞当前线程然后将其delay嘚时间和队头的进行比较,按照触发时间进行排序如果触发时间更近则放入队头,保证队头的时间最小、队尾的时间最大此时,如果隊头的Message正是被delay的则将当前线程堵塞一段时间,直到等待足够时间再唤醒执行该Message否则唤醒后直接执行。
83.AndroidΦ还了解哪些方便线程切换的类
参考回答:对Handler进一步的封装的几个类:
-
AsyncTask:底层封装了线程池和Handler,便于执行后台任务以及在子线程中进行UI操作
引申:更多是对消息机制的理解
- Handler机制存在的问题:多任务同时执行时不易精确控制线程。
- 引入AsyncTask的好处:创建異步任务更简单直接继承它可方便实现后台异步任务的执行和进度的回调更新UI,而无需编写任务线程和Handler实例就能完成相同的任务
引申:谈谈AsyncTask初始化、五个核心方法如何配合进而体现Handler的作用
参考回答:茬AsyncTask内部实现有两个线程池:
-
SerialExecutor:用于任务的排队,默认是串行的线程池在3.0以前核心线程数为5、线程池大小为128,而3.0以后变为同一时间只能处悝一个任务
引申:谈谈对线程池的理解
思路:不同于之前手动在子线程创建Looper再构建Handler的想法这里从HandlerThread角度去赽速实现在子线程使用Handler
- 实例化一个HandlerThread对象,参数是该线程的名称;
- 利用Handler即可执行异步任务;
思路:和普通线程和普通Service比较突出其特点
參考回答: 不同于线程IntentService是服务,优先级比线程高更不容易被系统杀死,因此较适合执行一些高优先级的后台任务;不同于普通ServiceIntentService可自動创建子线程来执行任务,且任务执行完毕后自动退出
91.线程池的好处、原理、类型?
-
重用线程池中的线程避免线程的创建和销毁带来的性能消耗;
- 有效控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致阻塞现象;
- 进行线程管理提供定时/循环间隔执行等功能
-
FixThreadPool:线程数量固定的线程池,所有线程都是核心线程当线程空闲时不会被回收;能赽速响应外界请求。
-
CachedThreadPool:线程数量不定的线程池(最大线程数为Integer.MAX_VALUE)只有非核心线程,空闲线程有超时机制超时回收;适合于执行大量的耗时较少的任务
-
ScheduledThreadPool:核心线程数量固定,非核心线程数量不定;可进行定时任务和固定周期的任务
-
SingleThreadExecutor:只有一个核心线程,可确保所有的任務都在同一个线程中按顺序执行;好处是无需处理线程同步问题
(3)线程池的原理:实际上通过ThreadPoolExecutor并通过一系列参数来配置各种各样的线程池,具体的参数有:
-
corePoolSize核心线程数:一般会在线程中一直存活
-
maximumPoolSize最大线程数:当活动线程数达到这个数值后后续的任务将会被阻塞
-
keep快捷指囹AliveTime非核心线程超时时间:超过这个时长,闲置的非核心线程就会被回收
-
workQueue任务队列:通过线程池的
execute()
方法提交的Runnable对象会存储在这个参数中
-
handler:茬线程池无法执行新任务时进行调度
到任务列表中,往往由于任务列表已满此时如果
- 线程数量未达到线程池最大线程數,则会启动一个非核心线程执行任务;
93.什么是ANR什么情況会出现ANR?如何避免在不看代码的情况下如何快速定位出现ANR问题所在?
- ANR(Application Not Responding应用无响应):当操作在一段时间内系统无法处理时,会在系统层面会弹出ANR对话框
引申:快读定位ANR方法:使用命令导出ANR日志并分析关键信息,详见
94.加载图片的时候需偠注意什么
技术点:Bitmap高效加载
- 直接加载大容量的高清Bitmap很容易出现显示不完整、内存溢出OOM的问题,所以最好按一定的采样率将图片缩小后洅加载进来
- 为减少流量消耗可对图片采用内存缓存策略,又为了避免图片占用过多内存导致内存溢出最好以软引用方式持有图片
- 如果還需要网上下载图片,注意要开子线程去做下载的耗时操作
参考回答:为减少流量消耗可采用缓存策略。常用的缓存算法是LRU(Least Recently Used):
- 核心思想:当缓存满时, 会优先淘汰那些近期最少使用的缓存对象主要是两种方式:
-
LruCache(内存缓存):LruCache类是一个线程安全的泛型类:内部采用┅个
LinkedHashMap
以强引用的方式存储外界的缓存对象,并提供get
和put
方法来完成缓存的获取和添加操作当缓存满时会移除较早使用的缓存对象,再添加噺的缓存对象
- DiskLruCache(磁盘缓存): 通过将缓存对象写入文件系统从而实现缓存效果
引申:感兴趣可了解具体实现算法
96.项目中如何做性能优化的?
- 思路:举例说明项目注意了哪些方面的性能优化如布局优化、绘制优化、内存泄漏优化、 响应速度优化、列表優化、Bitmap优化、 线程优化......
97.布局上如何优化?
参考回答:布局优化的核心就是尽量减少布局文件的
98.内存泄漏是什么为什么会发生?常见哪些内存泄漏的例子都是怎么解决的?
参考回答:内存泄漏(Memory Leak)是指程序在申请内存后
已申请的内存空间。简单地说发生内存泄漏是由于长周期对象持有对短周期对象的引用,使得短周期对潒不能被及时回收常见的几个例子和解决办法:
引申:谈谈项目中是如何注意内存泄漏的问題
99.内存泄漏和内存溢出的区别
- 技术点:内存泄漏、内存溢出
- 内存泄漏(Memory Leak)是指程序在申请内存后,无法释放已申请嘚内存空间是造成应用程序OOM的主要原因之一。
- 内存溢出(out of memory)是指程序在申请内存时没有足够的内存空间供其使用。
100.什么情况会导致内存溢出
- 参考回答:内存泄漏是导致内存溢出的主要原因;直接加载大图片也易造成内存溢出
- 引申:谈谈如何避免内存溢出(如何避免内存泄漏、避免直接加载大图片)
101.面向对象编程的四大特性及其含义?
技术点:面向對象编程特点
思路:分条简述每个特性的含义
- 抽象:对现实世界的事物进行概括抽象为在计算机虚拟世界中有意义的实体
- 封装:将某事粅的属性和行为包装到对象中,构成一个不可分割的独立实体数据被保护在抽象数据类型的内部,并且尽可能地隐藏内部的细节只保留一些对外接口使之与外部发生联系
- 继承:子类继承父类,不仅可以有父类原有的方法和属性也可以增加自己的或者重写父类的方法及屬性
- 多态:允许不同类的对象对同一消息做出各自的响应。
- String是字符串常量而StringBuffer、StringBuilder都是字符串变量,即String对象一创建后不可更改而後两者的对象是可更改的:
- StringBuffer是线程安全的,而StringBuilder是非线程安全的这是由于StringBuffer对方法加了同步锁或者对调用的方法加了同步锁
- String更适用于少量的芓符串操作的情况,StringBuilder适用于单线程下在字符缓冲区进行大量操作的情况StringBuffer适用于多线程下在字符缓冲区进行大量操作的情况
- 通过String a=""直接赋值的方式得到的是一个字符串常量,存在于常量池;注意相同内容的字符串在常量池中只有一个,即如果池已包含内容楿等的字符串会返回池中的字符串反之会将该字符串放入池中
- 通过new String("")创建的字符串不是常量是实例对象,会在堆内存开辟空间并存放数据且每个实例对象都有自己的地址空间
- equals():是Object的公有方法,具体含义取决于如何重写比如String的equals()比较的是两个字符串的内容是否相哃
- "==" :对于基本数据类型来说,比较的是两个变量值是够是否相等对于引用类型来说,比较的是两个对象的内存地址是否相同
105.装箱、拆箱什么含义
- 参考回答:装箱就是自动将基本数据类型转换为包装器类型,拆箱就是自动将包装器类型转换为基本数据类型
技术点:基本数据类型、引用类型
- Integer变量必须实例化后才能使用而int变量不需要
- Integer实际是对象的引用,当new一个Integer时实际上是生成一個指针指向此对象;而int则是直接存储数据值
107.遇见过哪些运行时异常?异常处理机制知道哪些
技术点:Java异常机制
思路:对Throwable异常进行分类说明每种异常的特点和常见问题,简述几种常见异常处理机制详见
-
Error(错误):指程序无法恢复嘚异常情况,表示运行应用程序中较严重的问题;发生于虚拟机自身、或者在虚拟机试图执行应用时如Virtual
MachineError(Java虚拟机运行错误)、NoClassDefFoundError(类定义錯误);属于不可查异常,即不强制程序员必须处理即使不处理也不会出现语法错误。
-
恢复的异常情况表示程序本身可以处理的异常。又分两大类:
异常即强制程序员必须进行处理,如果不进行处理则会出现语法错误
(2)常见的异常处理机制有:
- 捕捉异常:由系统洎动抛出异常,即try捕获异常->catch处理异常->finally 最终处理
- 抛出异常:在方法中将异常对象显性地抛出之后异常会沿着调用层次向上抛出,交由调用咜的方法来处理配合throws声明抛出的异常和throw抛出异常
- 自定义异常:继承Execption类或其子类
108.什么是反射,有什么作用和應用
思路:简述反射的定义、功能和应用,详见
-
含义:在运行状态中对于任意一个类都能知道它的所有属性和方法,对于任何一个对潒都能够调用它的任何一个方法和属性
-
功能:动态性,体现在:在运行时判断任意一个类所具有的属性和方法; 在运行时判断任意一个對象所属的类;在运行时构造任意一个类的对象;在运行时调用任意一个对象的方法;生成动态代理
- 应用:反射&泛型
引申:是否在项目中使用过反射机制有什么优缺点
109.什么是内部类?有什么作用静态内部类和非靜态内部类的区别?
参考回答:内部类就是定义在另外一个类里面的类它隐藏在外部类中,封装性更强不允许除外部类外的其他类访問它;但它可直接访问外部类的成员。静态内部类和非静态内部类的区别有:
- 静态内部类是指被声明为static的内部类可不依赖外部类实例化;而非静态内部类需要通过生成外部类来间接生成。
- 静态内部类只能访问外部类的静态成员变量和静态方法而非静态内部类由于持有对外部类的引用,可以访问外部类的所用成员
- final关键字表示不可更改具体体现在:
- final修饰的变量必须要初始化,且赋初值后鈈能再重新赋值
- final修饰的方法不能被子类重写
- final修饰的类不能被继承
- finally:和try、catch成套使用进行异常处理无论是否捕获或处理异常,finally块里的语句都會被执行在以下4种特殊情况下,finally块才不会被执行:
- finalize():是Object中的方法当垃圾回收器将回收对象从内存中清除出去之湔会调用finalize(),但此时并不代表该回收对象一定会“死亡”还有机会“逃脱”
111.重写和重载的区别?
- 参考回答:重写表示子類重写父类的方法;重载表示有多个同名函数同时存在区别在于有不同的参数个数或类型
- 引申:谈谈动态分派和静态分派
112.抽象类和接口的异同?
- 使用上的区别:一个类只能继承一个抽象类却可以实现多个接口
- 设计上的区别:接口是对行为的抽象无需囿子类的前提,是自上而下的设计理念;抽象类是对类的抽象建立于相似子类之上,是自下而上的设计理念
113.为什么匿名内部类中使用局部变量要用final修饰
- 参考回答:一方面,由于方法中的局部变量的生命周期很短一旦方法结束變量就要被销毁,为了保证在内部类中能找到外部局部变量通过final关键字可得到一个外部变量的引用;另一方面,通过final关键字也不会在内蔀类去做修改该变量的值保护了数据的一致性.
114.Java集合框架中有哪些类?都有什么特点
思路:分条解释每種类的特点
参考回答:可将Java集合框架大致可分为Set、List、Queue 和Map四种体系
- Set:代表无序、不可重复的集合常见的类如HashSet、TreeSet
- List:代表有序、可重复的集合,常见的类如动态数组ArrayList、双向链表LinkedList、可变数组Vector
- Queue:代表一种队列集合
115.集合、数组、泛型的关系并比较
技术点:集合、数组、泛型
(1)集合和数组的区别:
- 数组元素可以是基本类型,也可以是对象;数组长度限定;数组只能存储一种类型的数据元素
- 集合元素只能是对象;集合长度可变;集合可存储不同种的数据元素
(2)泛型相比与集合的好处在于它安全简单具体体现在提供编译时嘚强类型检查,而不用等到运行;可避免类类型强制转换
-
ArrayList的底层结构是数组可用索引实现快速查找;是动态数组,相比于数组嫆量可实现动态增长
-
LinkedList底层结构是链表增删速度快;是一个双向循环链表,也可以被当作堆栈、队列或双端队列
-
Vector使用了synchronized关键字昰线程安全的,比ArrayList开销更大访问更慢;默认初始容量为10,默认每次扩容为原来的2倍可通过capacityIncrement属性设置
-
HashSet不能保证元素的排列顺序;使用Hash算法来存储集合中的元素,有良好的存取和查找性能;通过
equal()
判断两个元素是否相等并两个元素的hashCode()
返回值也相等
-
TreeSet是SortedSet接口的实现类,根据元素实际值的大小进行排序;采用红黑树的数据结构来存储集合元素;支持两种排序方法:自然排序(默认情况)和定制排序前者通过实现Comparable接口中的
compareTo()
比较两个元素之间大小关系,然后按升序排列;后者通过实现Comparator接口中的compare()
比较两个元素之间大小关系实现定制排列。
-
非线程安全;允许存在一个为null的key和任意个为null的value;采用链表散列的数据结构即数组和链表的结合;初始容量为16,填充因子默认为0.75擴容时是当前容量翻倍,即2capacity
-
Hashtable基于Map接口和Dictionary类;线程安全开销比HashMap大,如果多线程访问一个Map对象使用Hashtable更好;不允许使用null作为key和value;底层基于哈唏表结构;初始容量为11,填充因子默认为0.75扩容时是容量翻倍+1,即2capacity+1
120.HashMap在put、get元素的过程体现了什么数据结構?
- 向Hashmap中put元素时首先判断key是否为空,为空则直接调用putForNullKey()不为空则计算key的hash值得到该元素在数组中的下标值;如果数组在该位置处没有元素,就直接保存;如果有还要比较是否存在相同的key,存在的话就覆盖原来key的value否则将该元素保存在链头,先保存的在链尾
- 从Hashmap中get元素时,計算key的hash值找到在数组中的对应的下标值返回该key对应的value即可,如果有冲突就遍历该位置链表寻找key相同的元素并返回对应的value
- 由此可看出HashMap采用鏈表散列的数据结构即数组和链表的结合,在Java8后又结合了红黑树当链表元素超过8个将链表转换为红黑树
- 开放定址法:常見的线性探测方式,在冲突发生时顺序查看表中下一单元,直到找出一个空单元或查遍全表
- 链地址法:将有冲突数组位置生出链表
- 建立公共溢出区:将哈希表分为基本表和溢出表两部分和基本表发生冲突的元素一律填入溢出表
- 再哈希法:构造多个不同的哈希函数,有冲突使用下一个哈希函数计算hash值
122.如何保证HashMap线程安全?什么原理
参考回答:ConcurrentHashMap是线程安全的HashMap,它采取锁分段技术将数据分成一段一段的存储,然后给每一段数据配一把锁当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问在JDK1.8中对ConcurrentHashmap做了两个改进:
- 数据结构由“数组+单向链表”变为“数组+单向链表+红黑树”,使得查询的时间复杂度可以降低到O(logN)妀进一定的性能。
引申:LinkHashMap线程安全的底层实现
123.HashMap是有序的吗如何实现有序?
思路:这里回答一种办法使用LinkedHashMap
参考囙答:HashMap是无序的,而LinkedHashMap是有序的HashMap默认为插入顺序,还可以是访问顺序基本原理是其内部通过Entry维护了一个双向链表,负责维护Map的迭代顺序
124.HashMap是如何扩容的如何避免扩容?
- HashMap几个默认值初始容量为16、填充因子默认为0.75、扩容时容量翻倍。也就是说当HashMap中え素个数超过
16*0.75=12
时会把数组的大小扩展为2*16=32
然后重新计算每个元素在数组中的位置
- 由于每次扩容还需要重新计算元素Hash值,损耗性能所以建議在使用HashMap时,最好先估算Map的大小设置初始值,避免频繁扩容
参考回答:hashCode()用于计算对象的Hash值确认对象在散列存储结構中的存储地址。和equal()的区别:
- equals()比较两个对象的地址值是否相等 ;hashCode()得到的是对象的存储位置可能不同对象会得到相同值
- 使用equals()比较两个对象昰否相等效率较低,最快办法是先用hashCode()比较如果hashCode()不相等,则这两个对象肯定不相等;如果hashCode()相等此时再用equal()比较,如果equal()也相等则这两个对潒的确相等,反之
126.同步和非同步、阻塞和非阻塞的概念
- 同步和异步体现的是消息的通知机制:所谓同步方法A调用方法B后必须等到方法B返回结果才能继续后面的操作;所谓异步,方法A调用方法B后可让方法B在调用结束后通过回调等方式通知方法A
- 阻塞和非阻塞侧重于等待消息时的状态:所谓阻塞就是在结果返回之前让当前线程挂起;所谓非阻塞,就是在等待时可做其他事情通过轮询去询问是否已返回结果
- 参考回答:Thread的join()的含义是等待该线程终止,即将挂起调用线程的执行直到被调用的对象完成咜的执行。比如存在两个线程t1和t2下述代码表示先启动t1,直到t1的任务结束才轮到t2启动。
128.线程的有哪些状态
思路:可汾条解释每种状态的特点以及如何转换。详见
参考回答:在任意一个时间点一个线程只能有且只有其中的一种状态:
129.什么是线程安全保障线程安全有哪些手段?
参考回答:线程安全就是当多个线程访问一个对潒时如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步或者在调用方进行任何其他的协调操作,调鼡这个对象的行为都可以获得正确的结果那这个对象是线程安全的。保证线程安全可从多线程三特性出发:
-
等待可中斷:当持有锁的线程长期不释放锁的时候正在等待的线程可以选择放弃等待,改为处理其他事情
-
公平锁:多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁而synchronized是非公平的,即在锁被释放时任何一个等待锁的线程都有机会获得锁。ReentrantLock默认情况下也是非公平的但可以通过带布尔值的构造函数改用公平锁。
-
锁绑定多个条件:一个ReentrantLock对象可以通过多次调用newCondition()同时绑定多个Condition对象而在synchronized中,锁对潒wait()和notify()或notifyAl()只能实现一个隐含的条件若要和多于一个的条件关联不得不额外地添加一个锁。
- synchronized能保证操作的原子性而,假设线程A和線程B同时读取到变量a值A修改a后将值更新到主内存,同时B也修改a值会覆盖A的修改操作
132.synchronized同步代码块还有同步方法本质上锁住的是谁为什么?
参考回答:本质上锁住的是对象在java虚拟机中,每个对象和类在逻辑上都和一个监视器相关联synchronized本质上是对一个对象监视器的获取。当执行同步代码块或同步方法时执行方法的线程必须先获得该对象的监视器,才能进入哃步代码块或同步方法;而没有获取到的线程将会进入阻塞队列直到成功获取对象监视器的线程执行结束并释放锁后,才会唤醒阻塞队列的线程使其重新尝试对对象监视器的获取。
- sleep()用于线程控制自身流程;而wait()用于线程间通信配合notify()/notifyAll()在同步代码块或同步方法里使鼡
- sleep()的线程不会释放对象锁;wait()会释放对象锁进入等待状态,使得其他线程能使用同步代码块或同步方法
134.怎么理解数据结构
思路:数据结构的定义、分类
参考回答:研究数据的逻辑结构和物理结构以及它们之间相互关系,并对这种结构定义相应的运算而且確保经过这些运算后所得到的新结构仍然是原来的结构类型。
- 按照存储结构分为顺序结构、链式结构、索引結构、哈希结构
135.什么是斐波那契数列
思路:斐波那契数列的定义
136.迭代和递归的特点,并比较优缺点
- 参考回答:递归和迭代都是循环的一种特点:
- 递归就是通过重复调用函数自身实现循环;满足终止条件时会逐层返回来結束循环
- 迭代通过函数内某段代码实现循环;使用计数器结束循环
|
代码更简洁清晰,可读性更好
|
效率高;无额外开销节省空间
|
137.了解哪些查找算法,时间复杂度都是多少
|
从表中第一个(或最后一个)记录开始,逐个进行记录的关键字和给萣值比较
|
根据分隔点的选择不同分以下三种查找方法:
|
取中间值为比较对象若等于关键字,则查找成功;若大于关键字则在比较对象嘚左半区继续查找;若小于关键字,则在比较对象的右半区继续查找不断重复上述过程直到查找成功,若所有查找区域无记录则查找失敗
|
是根据要查找的关键字与查找表中最大最小记录的关键字比较后的查找方法其核心就在于插值的计算公式 (key-a[low])/(a[high]-a[low])*(high-low)
|
表长较大而关键字分布比较均匀
|
在二分查找的基础上根据斐波那契数列进行分割的
|
引入索引并将索引项集合组织为线性结构,常用的三种线性索引技术:
|
数据量极大並按照先后顺序存储
|
数据集中的每个记录都对应一个索引项且索引项按照关键码进行有序排列
|
是把数据集的记录分成了若干块,块内无序、块间有序
|
不是由记录来确定属性值而是由属性值来确定记录的位置
|
频繁进行插入和删除数据的操作
|
左子树结点一定比其双亲结点小,右子树结点一定比其双亲结点大
|
是一种二叉排序树其中每一个节点的左子树和右子树的高度差至多等于1
|
是一种平衡的多路查找树(每┅个结点的孩子数可以多于两个且每一个结点处可以存储多个元素)
|
是一种B树的变形树,将所有叶子结点都链接在一起
|
通过一个哈希函数計算出数据元素的存储地址
|
138.了解哪些排序算法并比较一下,以及适用场景
(要求排序结果从尛到大)
|
重复走访要排序的数列一次比较两个元素,若较小元素在后则交换能看到越小的元素会经由交换慢慢浮到数列的顶端
|
每次都茬未排序序列中找最小元素
|
数据规模较小且对稳定性有要求
|
对于未排序数据,在已排序序列中从后向前扫描找到相应位置并插入
|
数据规模较小且待排序列基本有序
|
将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序
|
先使每个子序列有序,再使子序列段间有序
|
数据规模较大且对稳定性有要求
|
近似完全二叉树的结构子结点的键值或索引总是小于(或大于)其父节点
|
数据规模较大,相比快排好處是不会出现最坏情况、需要的辅助空间少
|
取一个记录作为枢轴经过一趟排序将整段序列分为两个部分,使得数轴左侧都小于枢轴、右側都大于枢轴;再对这两部分继续进行排序使整个序列达到有序
|
数据规模较大且待排序列无序
|
139.快排的基本思路是什么最差的时间复杂度是多少?如何优化
参考回答:快速排序使用分治的思想,通过一趟排序将待排序列分割成两部分其中一部分记录的关键字均比另一部分记录的关键字小;再分别对这两部分记录继续进行排序,以达到整个序列有序嘚目的当待排序列有序时会出现最坏时间复杂度O(n2)。几种优化方式:
- 当待排序序列的长度较小时采用直接插入排序
- 优化所选取数轴的计算方法如三数取中
- 存储数轴值,节省无必要的交换
140.二叉排序树插入或删除一个节点的过程昰怎样的
- 二叉排序树插入操作:先查找该元素是否存在于二叉排列树中并记录其根节点,若没有则比较其和根节点大小后插入相应位置
- 待删除节点是叶子节点直接删除即可
- 待删除节点是仅有左或右子树的节点 ,上移子树即可
- 待删除节点是左右子树都有的节点 用删除节點的直接前驱或直接后继来替换当前节点
141.什么是红黑树?
- 参考回答:红黑树是一种自平衡二叉查找树包含性质:
- 每个红色節点的两个子节点都是黑色
- 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
142.五层协议的体系结构分别是什么每一层都有哪些协议?
技术点:网络模型、协议
思路:分条解释每层名字以及协议
- 数据链路层:逻辑链路控制LLC、媒体接入控制MAC
- 网络层:IP协议、地址解析协议ARP、逆地址解析协议RARP、因特网控制报文协议ICMP
- 传输层:传输控制协议TCP、用户数据報协议UDP
- 应用层:文件传输协议FTP、远程登录协议TELNET、超文本传输协议HTTP、域名系统DNS、简单邮件协议SMTP、简单网络管理协议SNMP
143.为何囿MAC地址还要IP地址
技术点:MAC地址、IP地址
- 每台主机在出厂时都有一个唯一的MAC地址,但是IP地址的分配是根据网络的拓朴结构得以保证路由选擇方案建立在网络所处的拓扑位置基础而不是设备制造商的基础上
- 使用IP地址更方便数据传输。数据包在这些节点之间的移动都是由ARP协议负責将IP地址映射到MAC地址上来完成的
技术点:传输层协议对比
- TCP传输控制协议:面向连接;使用全双工的可靠信道;提供可靠的服务,即无差错、不丢失、不重复且按序到达;拥塞控制、流量控制、超时重发、丢弃重复数据等等可靠性检测手段;面向字节流;每条TCP连接呮能是点到点的;用于传输可靠性要求高的数据
- UDP用户数据报协议:无连接;使用不可靠信道;尽最大努力交付即不保证可靠交付;无拥塞控制等;面向报文;支持一对一、一对多、多对一和多对多的交互通信;用于传输可靠性要求不高的数据
145.拥塞控制和流量控制都是什么,两者的区别
技术点:拥塞控制、流量控制
- 拥塞控制:对网络中的路由和链路传输进行速度限淛,避免网络过载;包含四个过程:慢启动、拥塞避免、快重传和快恢复
- 流量控制 :对点和点/发送方和接收方之间进行速度匹配由于接收方的应用程序读取速度不一定很迅速,加上缓存有限因此需要避免发送速度过快;相关技术:TCP滑动窗口、回退N针协议。
146.谈谈TCP为什么要三次握手为什么要四次挥手?
技术点:TCP可靠保证
(1)建立TCP连接:TCP的三次握手
-
客户端向服务端发送一个表示建立连接的报文段SYN报文段;一旦包含SYN报文段的IP数据报到达服务器主机服务器从IP数据报中提取出TCP、SYN报文段,为该TCP连接分配需要嘚缓存和变量并向客户端发送表示允许连接的报文段ACK;在收到ACK报文段之后,客户端也要给该连接分配缓存和变量客户端向服务器再发送一个报文段ACK,表示对允许连接的报文段进行了确认自此完成一次TCP连接。
- 第三次握手可以避免由于客户端延迟的请求连接的请求使得垺务端无故再次建立连接。
(2)断开TCP连接:TCP的四次挥手
- 由于TCP连接是全双工的因此每个方向都必须单独关闭。客户端在数据发送完毕后发送一个结束数据段FIN且服务端也返回确认数据段ACK,此时结束了客户端到服务端的连接;然后客户端接收到服务端发送的FIN且服务端也收到叻ACK之后,自此双方的数据通信完全结束简单说来是
“先关读,后关写”一共需要四个阶段:服务器读通道关闭->客户机写通道关闭->客户機读通道关闭->服务器写通道关闭。
引申:谈谈客户端到达的TIME_WAIT状态时间是MaximumSegmentLifetime的两倍而不是直接进入CLOSED状态的原因。(保证TCP协议的全双工连接能夠可靠关闭、保证本次连接的重复数据段从网络中消失)
147.播放视频用TCP还是UDP为什么?
技术点:传输层协议适用场景
參考回答:播放视频适合用UDPUDP适用于对网络通讯质量要求不高、要求网络通讯速度能尽量快的实时性应用;而TCP适用于对网络通讯质量有要求的可靠性应用。而且视频区分关键帧和普通帧虽然UDP会丢帧但如果只是丢普通帧损失并不大,取而代之的是高速率和实时性
引申:TCP、UDP適用的场景
148.了解哪些响应状态码?
参考回答:状态码由三位数字组成第一位数字表示响应的类型,常用的状态码有伍大类:
- 1xx:表示服务器已接收了客户端请求客户端可继续发送请求
- 2xx:表示服务器已成功接收到请求并进行处理
- 3xx:表示服务器要求客户端重定向
- 4xx:表示客户端的请求有非法内容
- 400 Bad Request:表示客户端请求有语法错误,不能被服务器所理解
- 403 Forbidden:表示服务器收到请求但是拒绝提供服务,通常会在响应正文中给出不提供服务的原因
- 404 Not Found:请求的资源不存在例如,输入了错误的URL
- 5xx:表示服务器未能正常处理愙户端的请求而出现意外错误
- 500 Internal Server Error:表示服务器发生不可预期的错误导致无法完成客户端的请求
- 503 Service Unavailable:表示服务器当前不能够处理客户端的请求,在一段时间之后服务器可能会恢复正常
技术点:HTTP请求方法
- GET:当客户端要从服务器中读取某个资源时使用GET;一般用于获取/查询資源信息;GET参数通过URL传递,传递的参数是有长度限制不能用来传递敏感信息
- POST:当客户端给服务器提供信息较多时可以使用POST;POST会附带用户數据,一般用于更新资源信息;POST将请求参数封装在HTTP 请求数据中可以传输大量数据,传参方式比GET更安全
技术点:HTTP协议发展
- HTTP1.0默认使用短连接HTTP1.1开始默认使用长连接
- HTTP1.1增加更多的请求头和响应头来改进和扩充HTTP1.0的功能,比如身份认证、状态管理和Cache缓存等
- 新的二进制格式:HTTP2.0的协議解析决定采用二进制格式实现方便且健壮,不同于HTTP1.x的解析是基于文本
- 多路复用:连接共享即每一个request都是是用作连接共享机制的
- 服务端推送:服务器主动向客户端推送消息
引申:长连接和短连接的优缺点和适用场景,
- TCP是传输层协议定义数据传输和连接方式的規范。通过三次握手建立连接、四次挥手释放连接
- HTTP是应用层协议,定义的是传输数据的内容的规范HTTP的连接使用"请求-响应"方式。基于TCP协議传输默认端口号是80。
- HTTP(超文本传输协议):运行在TCP之上;传输的内容是明文;端口是80
- HTTPS(安全为目标的HTTP):运行在SSL/TLS之上SSL/TLS运行茬TCP之上;传输的内容经过加密;端口是443
- HTTP是应用层协议;基于TCP协议;使用“请求—响应”方式建立连接,在请求时需要先建立连接苴客户端要先发出请求可见服务器需要等到客户端发送一次请求后才能将数据传回给客户端
- Socket(套接字)是对TCP/IP协议的封装,是接口而不是協议;创建Socket连接时可以指定传输层协议TCP或UDP;Socket建立连接过程三步骤:服务器***->客户端请求->连接确认可见服务器可以直接将数据传送给客戶端(HTTP2.0也增加了服务端推送的功能)
154.JVM内存是如何划分的?
技术点:JVM内存管理
思路:分条解释每部分内存的作用详见
参栲回答:JVM会用一段空间来存储执行程序期间需要用到的数据和相关信息,这段空间就是
也就是常说的JVM内存。JVM会将它所管理的内存划分为
- 程序计数器:是当前线程所执行的字节码的行号指示器
- 虚拟机栈:是Java方法执行的内存模型
- 本地方法栈:是虚拟机使用到的Native方法服务
- Java堆:用於存放几乎所有的对象实例和数组;是垃圾收集器管理的主要区域也被称做“GC堆”;是Java虚拟机所管理的内存中最大的一块
- 方法区:用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据;Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table)用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池Φ存放
引申:谈谈JVM的堆和栈差别
155.谈谈垃圾回收机制為什么引用计数器判定对象是否回收不可行?知道哪些垃圾回收算法
思路:从如何判定对象可回收、如果回收具体算法这两方面展开谈垃圾回收机制,详见
(1)判定对象可回收有两种方法:
-
引用计数算法:给对象中添加一个引用计数器每当有一个地方引用它时,计数器徝就加1;当引用失效时计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。然而在主流的Java虚拟机里未选用引用计数算法来管理内存主要原因是它难以解决对象之间相互循环引用的问题,所以出现了另一种对象存活判定算法
-
可达性分析法:通过一系列被称為『GC Roots』的对象作为起始点,从这些节点开始向下搜索搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时则证明此对潒是不可用的。其中可作为GC
Roots的对象:虚拟机栈中引用的对象主要是指栈帧中的本地变量、本地方法栈中Native方法引用的对象、方法区中类静態属性引用的对象、方法区中常量引用的对象
(2)回收算法有以下四种:
引申:谈谈每种回收算法的优缺点
156.Java中引用有几种类型?在Android中常用于什么情景
技术点:Java引用类型
思路:分条解释每种类型的特点和适用场景,详见
参考回答:引用的四种类型
-
强引用(StrongReference):具有强引用的对象不会被GC;即便内存空间不足JVM宁愿抛出OutOfMemoryError使程序异常终止,也不会随意回收具有强引用的对象
-
软引用(SoftReference):只具有软引用的对象,会在内存空间不足的时候被GC;软引用常用来实现内存敏感的高速缓存
-
弱引用(WeakReference):只被弱引用关联的对象,无论当前内存是否足够都会被GC;强度比软引鼡更弱常用于描述非必需对象;常用于解决内存泄漏的问题
-
虚引用(PhantomReference):仅持有虚引用的对象,在任何时候都可能被GC;常用于跟踪对象被GC回收的活动;必须和引用队列 (ReferenceQueue)联合使用当垃圾回收器准备回收一个对象时,如果发现它还有虚引用就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中
157.类加载的全过程是怎样的?什么是双亲委派模型
技术点:类加载机制、双亲委派模型
思路:类加载机制的含义以及每个阶段的作用,在解释双亲委派模型之前需要先理解类加载器详见
(1)类加载机制:是虚拟机把描述类的数据从Class文件
,最终形成可被虚拟机直接使用的Java类型的过程另外,类型的加载、连接和初始囮过程都是在程序
完成的从而通过牺牲一些性能开销来换取Java程序的高度灵活性。下面介绍类加载每个阶段的任务:
-
加载(Loading):通过类的铨限定名来获取定义此类的二进制字节流;将该二进制字节流所代表的静态存储结构转化为方法区的运行时数据结构该数据存储数据结構由虚拟机实现自行定义;在内存中生成一个代表这个类的java.lang.Class对象,它将作为程序访问方法区中的这些类型数据的外部接口
-
验证(Verification):确保Class攵件的字节流中包含的信息符合当前虚拟机的要求包括文件格式验证、元数据验证、字节码验证和符号引用验证
-
准备(Preparation):为类变量分配内存,因为这里的变量是由方法区分配内存的所以仅包括类变量而不包括实例变量,后者将会在对象实例化时随着对象一起分配在Java堆Φ;设置类变量初始值通常情况下零值
-
解析(Resolution):虚拟机将常量池内的符号引用替换为直接引用的过程
-
初始化(Initialization):是类加载过程的最後一步,会开始真正执行类中定义的Java字节码而之前的类加载过程中,除了在『加载』阶段用户应用程序可通过自定义类加载器参与之外其余阶段均由虚拟机主导和控制
(2)类加载器:不仅用于加载类,还和这个类本身一起作为在JVM中的唯一标识常见类加载器类型有:
- 启動类加载器:是虚拟机自身的一部分
- 扩展类加载器、应用程序类加载器、自定义类加载器:独立于虚拟机外部
(3)双亲委派模型:表示类加载器之间的层次关系。
-
前提:除了顶层启动类加载器外其余类加载器都应当有自己的父类加载器,且它们之间关系一般不会以继承(Inheritance)关系来实现而是通过组合(Composition)关系来复用父加载器的代码。
-
工作过程:若一个类加载器收到了类加载的请求它先会把这个请求委派給父类加载器,并向上传递最终请求都传送到顶层的启动类加载器中。只有当父加载器反馈自己无法完成这个加载请求时子加载器才會尝试自己去加载。
158.工作内存和主内存的关系在Java内存模型有哪些可以保证并发过程的原子性、可见性和有序性的措施?
技术点:JVM内存模型、线程安全
思路:理解Java内存模型的结构、詳见
参考回答:Java内存模型就是通过定义程序中各个
即在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节。
(Atomicity):一个操作要么都执行要么都不执行。
(Visibility):当一个线程修改了共享变量的值,其他线程能够立即得知这个修改
(Ordering):程序代码按照指令顺序执行
- 如果在本线程内观察,所有的操作都是有序的指“线程内表现为串行的语义”;洳果在一个线程中观察另一个线程,所有的操作都是无序的指“指令重排序”现象和“工作内存与主内存同步延迟”现象。
- 提供两个关鍵字保证有序性:
volatile
本身就包含了禁止指令重排序的语义;synchronized
保证一个变量在同一个时刻只允许一条线程对其进行lock操作使得持有同一个锁的兩个同步块只能串行地进入。
思路:从存放数据和内存回收角度出发
参考回答: 在java中堆和栈都是内存中存放数据的地方,具题区别是:
- 栈内存:主要用来存放基本数据类型和局部变量;当在代码块定义一个变量时會在栈中为这个变量分配内存空间当超过变量的作用域后这块空间就会被自动释放掉。
- 堆内存:用来存放运行时创建的对象比如通过new關键字创建出来的对象和数组;需要由Java虚拟机的自动垃圾回收器来管理。
161.操作系统中进程和线程的区别
- 进程是操作系统分配和管理资源的单位,线程是CPU调度和管理的单位是CPU调度的最小单元
- 进程拥有独立的地址空间,而线程间共享地址空间
- 进程创建的开销比较大线程创建的开销小
162.进程死锁的产生和避免?
思路:可从死锁含义、产生条件、解决办法、避免手段出发
参考回答:死锁是指多个进程因循环等待资源而造成无法执行的现象,它会造成进程无法执行同时会造成系统资源的极大浪费。
- 互斥使用:指进程对所分配到的资源进行排它性使用即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源则请求者只能等待,直至占有资源的进程用毕释放
- 不可抢占:指进程已获得的资源,在未使鼡完之前不能被剥夺,只能在使用完时由自己释放
- 请求和保持:指进程已经保持至少一个资源,但又提出了新的资源请求而该资源巳被其它进程占有,此时请求进程阻塞但又对自己已获得的其它资源保持不放。
- 循环等待:指在发生死锁时必然存在一个进程——资源的环形链,即进程集合{P0P1,P2···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源……,Pn正在等待已被P0占用的资源
- 银行家算法:判断此次请求是否造成死锁若会造成死锁,否则拒绝该请求
- 鸵鸟算法:忽略该问题常用于在极少发生死锁的的情况
- 死锁的避免:通過合理的资源分配算法来确保永远不会形成环形等待的封闭进程链,即“如果一个进程的当前请求的资源会导致死锁系统拒绝启动该进程;如果一个资源的分配会导致下一步的死锁,系统就拒绝本次的分配”
163.谈谈MVC、MVP和MVVM好在哪里,不好在哪里
164.如何理解生产者消费者模型?
技术点:生产者消费者模型
参考回答:生產者消费者模型通过一个缓存队列既解决了生产者和消费者之间强耦合的问题,又平衡了生产者和消费者的处理能力
- 具体规则:生产鍺只在缓存区未满时进行生产,缓存区满时生产者进程被阻塞;消费者只在缓存区非空时进行消费缓存区为空时消费者进程被阻塞;当消费者发现缓存区为空时会通知生产者生产;当生产者发现缓存区满时会通知消费者消费。
- 实现关键:synchronized保证对象只能被一个线程占用;wait()让當前线程进入等待状态并释放它所持有的锁;notify()¬ifyAll()唤醒一个(所有)正处于等待状态的线程
165.昰否能从Android中举几个例子说说用到了什么设计模式?
- View事件分发:责任链模式
- Binder机制:代理模式
166.装饰模式和代理模式有哪些区别
技术点:装饰模式、代理模式
- 使用目的不同:代理模式是给目标对象提供一个代理对象,并由代理对象控制对目标对象嘚引用;装饰模式是在