字节跳动Android面试
-
四大引用,软引用什么时候使用?
强引用:具有强引用就不会被垃圾回收器回收,即使内存空间不足,抛出异常也不会回收;除非其在生命周期已经过了或者显示地将其设置为null;
软引用:SoftReference,通过get来获取对象,在jvm内存不足的时候会被回收;用来实现内存敏感的高速缓存,如网页缓存,图片缓存等,一旦SoftReference保存了对一个java对象的软引用猴,在回收前,get方法提供的是该对象的强引用,回收之后,get返回null;
弱引用:WeakReference,通过get获取对象,在gc的时候会被回收只与弱引用关联的对象;
虚引用:与没有引用是一样的,需要和队列联合使用;
-
补充:对象可及性判断:
对于一个对象的可及性要先判断有几条路径可以到达,取最强的一条路径,而某一条路径是什么是由最弱的一个引用决定; 如一条路径是由一个软和一个强组成,这条路径就是软;有两条路径同时到达该对象,一条软一条弱,则该对象为软可及对象;
-
利用软引用解决OOM问题
当一个应用需要读取大量的本地图片,加载内存时,会造成内存溢出;
这时使用一个HashMap来保存图片的路径与响应图片关联的软引用之间的映射关系,当内存不足时,jvm会自动回收这些缓存图片所占用的空间,有效避免了oom的问题;
-
-
Lrucache,是用什么数据结构实现的?
Least Recently Used最近最少使用,原则:如果一个数据最近一段时间没有被访问,那么将来他被访问的可能性很小;
实现是使用双向链表+hashmap,使用hashmap是为了提高查询速度,如果在map中可以查到,就去链表中去删除节点,然后插到头部,否则返回null;当Cache慢了就删掉最后一个节点;
-
优先队列听说过吗,如何实现的?
在优先队列中,元素被赋予优先级,当访问元素时,具有最高优先级的元素最先删除
使用堆来实现
-
堆是什么?
堆是一种完全二叉树,最大堆孩子节点小于等于父亲节点,最小堆反之;
-
-
GC回收算法
把一系列“GC Roots”作为起始点,从节点向下搜索,路径称为引用链,当一个对象到GC Roots没有任何引用链相连,即不可达时,则证明此对象时不可用的。
-
GCroots有哪些
-
局部变量对象
-
静态
static
字段对象 -
常量
static final
字段对象 -
JNI
(本地方法栈) 对象
-
-
堆和栈的区别
-
堆内存: 用于存储java中的对象和数组,当我们new一个对象或者创建一个数组,就会在堆内存中开辟一块空间; 生存期不必告诉编译器,存取速度较慢; 堆是所有线程共享的一块公共区域,对象都在堆里创建,但为了提升效率,线程会从堆中拷贝一个缓存到自己的栈中;
-
栈内存: 主要用于执行程序,比如基本类型的变量和对象的引用变量; 栈数据数据大小和生存期是确定的,存取速度比堆快; 栈是一块和线程相关的内存区域,每个线程都有自己的栈内存,用于存储本地变量,方法参数和栈调用;
-
-
注解有哪几种类型
代码级别的说明,可以在编译,类加载,运行时被读取并执行相应处理;
-
java内存划分
-
栈:基本数据类型,引用数据类型变量名;
-
堆:引用数据类型变量值,对象/数组;
-
全局代码区:保存所有方法的定义;
-
全局数据区:保存static类型的属性;
1.一个本地变量如果是原始类型,在线程栈上;
如果是指向一个对象的引用,则引用存放在线程栈上,对象本身存放在堆上;
方法中的本地变量存放在栈;
2.解决内存可见性问题
volatile
synchronized
final
-
-
Synchronize机制,当其他线程获取资源时会发生什么?
-
一个变量在同一个时刻只允许一条线程对其进行lock操作,持有同一个锁的两个同步块只能串行的进入;
-
-
final关键字的作用
-
修饰一个引用:引用为基本数据类型,则为常量,无法修改;
-
引用为数据类型,如对象数组,则对象,数组本省可以修改,地址的引用不可修改;
-
引用是类的成员变量,则必须当场赋值
-
-
修饰一个方法
-
无法被子类重写,但是可以被继承,除非是private类型则不可继承
-
-
用来修饰类
-
该类成为最终类,final类中的所有成员方法都会隐式的定义为final方法;
-
-
-
volatile
volatile可以保证直接从主存中读取一个变量,如果这个变量被修改后总是会被写回到主存中去,在变量读取前从主存中刷新变量值实现可见性;
保证了有序性(禁止指令的重排序);
volatile不保证原子性,如果要实现原子性,需要使用lock和unlock来满足
使用场景:用于标志位,用于单例模式的双检锁
-
异步一般使用什么?AsyncTask内部机制怎么实现的?
-
Handler是在什么时候进行线程切换的?
-
Handler的PostDelay方法的原理?
-
postDelay的调用路径?
-
Handler.postDelayed(Runnable r, long delayMillis)
-
Handler.sendMessageDelayed(getPostMessage(r), delayMillis)
-
Handler.sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)
-
Handler.enqueueMessage(queue, msg, uptimeMillis)
-
MessageQueue.enqueueMessage(msg, uptimeMillis)
-
-
Handler没有自己处理Delay,而是交给了MessageQueue处理,然后Looper在调用next方法时,会判断头部的Message是否有延迟且延迟时间没有到,就调用nativePollOnce进行阻塞,这个方法的作用类似于object.wait(也就是说延迟时间的方法是myssageQUeue中的next方法)时间到了也就相当于
-
新的疑问:如果postDelay了一个A,我再去post一个B,B会不会等A执行完了再执行?
-
首先是postDelay一个10秒钟的Runnable A消息进队,MessageQUeue调用nativePollOnce阻塞,Looper阻塞;
-
post进来一个B,消息进队,这时A时间还未到,正在阻塞,就把B插到队列的头部,然后调用nativeWake方法唤醒线程;
-
MessageQueue.next方法被唤醒后,重新开始读取消息链表,第一个消息B无延时,直接返回给
-
-
-
RecyclerView与ListView的区别
-
ListView的两级缓存:
-
mActiveViews:用于屏幕内的ItemView快速重用;
-
mScrapViews:相等于mCachedViews+mRecyclerViewPool
-
-
RecyclerView的四级缓存
-
mAttachedScrap:用于屏幕内ItemView快速重用,无需createView和bindView;
-
mCacheViews:缓存屏幕外的2个ItemView,目的是让即将进入屏幕的itemView重用;
-
mViewCacheExtension:不直接使用,需要用户定制实现;
-
mRecyclerPool:支持所有RecyclerView公用同一个RecyclerViewPool;
-
-
两者的缓存不同点
-
RecyclerView缓存ViewHolder,避免了每次createView时调用findViewById;ListView缓存View;
-
RecyclerView中mCacheViews获取缓存时,是通过匹配pos获取目标位置的缓存,这样在数据源数据不变的情况下,无需重新bindView的;ListView是从mScrapViews根据pos获取响应的缓存,但是并没有直接使用,重新getView
-
-
局部刷新
-
RecyclerView提供了局部刷新的接口,通过局部刷新,就能避免调用许多无用的bindView.ListView和RecyclerView的全部在于数据源改变时的缓存的处理逻辑;ListView是将所有mActiveViews都移入了二级缓存mScrapViews,而RecyclerView则是更加灵活地对每个View修改标志位,区分是否重新bindView;
-
-
-
RecyclerView翻页效果如何实现的?
-
ViewPager中的懒加载
-
第一种方法:Fragment可见时去请求数据
-
在Fragment中重写setUserVisibleHint方法,当isVisibleHint为true时请求数据;
-
-
内存泄露
-
一个对象是可达的,但是无用的,无法被GC回收,然后占用内存;
-
内存泄露的几种情况
-
单例模式:
-
静态变量导致的内存泄露
-
静态变量的生命周期与程序一致
-
-
非静态内部类导致的内存泄露
-
未取消注册导致的内存泄露
-
集合中的对象未清理造成内存泄露
-
资源未关闭导致内存泄露
-
-
-
ANR
-
Okhttp的拦截器作用
Okhttp一共有七个拦截器,使用了list存储起来,每一个拦截器分成三部分:
1].预处理拦截器内容;
2].通过proceed方法把请求交给下一个拦截器
3].下一个拦截器处理完成并返回,后续处理工作;
-
addInterceptor:这个是为开发者设置的,可以在所有拦截器处理之前进行更早的拦截处理,如一些公共参数,Header可以在这里添加;
-
RetryAndFollowUpInterceptor:对连接进行一些初始化工作,以及请求失败的重试工作,重定向的后续请求工作;
-
BridgeInterceptor:会为用户构建一个能够网络访问的请求,同时后续工作将请求回来的响应转化为用户可用的Response;
-
CacheInterceptor:主要处理Cache相关处理,一句OkhttpClient对象的配置以及缓存策略对请求值进行缓存到本地;
-
ConnectInterceptor:主要负责建立连接,会建立TCP连接或者TLS连接
-
netWorkInterceptor:可以做一些网络调试;
-
CallServerInterceptor:进行网络数据的请求和响应,通过socket读写数据;
-
-
Rxjava中的设计模式
-
责任链模式,就是Intercepter的使用
-
-
Retrofit
-
通过java接口以及注解来描述网络请求,用动态代理生成网络请求的request,然后通过client调用响应的网络框架,默认使用okhttp,并将返回的response通过converterFactory转换为相应的数据model,最后通过calladapter转换为其他数据方式如RxJava的Observable
-
动态代理和静态代理的区别:
-
动态代理是程序运行期间由jvm根据反射等机制动态生成,不存在代理类的字节码文件
-
静态代理是成程序员创建或工具生成代理类的与那吗,字节码文件
-
-
-
Retrofit中的数据是怎么处理的
-
数据交给了callAdapter以及converter去处理,callAdapter负责把OkhttpCall转为我们所需的Observable类型,converter负责把服务器返回的数据朱文内具体的实体类
-
-
与Okhttp的联系
-
Rxjava
-
-
二叉树的中序遍历(非递归)
```
public static List<Integer> inorderTraversal(TreeNode root) { List<Integer> list = new ArrayList<>(); if (root == null) return list; Stack<TreeNode> stack = new Stack<>(); TreeNode cur = root; while (cur != null || !stack.isEmpty()) { if (cur != null){ stack.push(cur); //看是否有左节点 cur = cur.left; }else { //返回上一层 cur = stack.pop(); //只有每次左侧没有节点的时候才添加 list.add(cur.val); cur = cur.right; } } return list; }
```